chef 10.16.4 → 10.16.6.rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ require File.dirname(__FILE__) + '/lib/chef/version'
21
21
 
22
22
  require 'rubygems'
23
23
  require 'rubygems/package_task'
24
- require 'rake/rdoctask'
24
+ require 'rdoc/task'
25
25
  require './tasks/rspec.rb'
26
26
 
27
27
  GEM_NAME = "chef"
@@ -0,0 +1,65 @@
1
+ #
2
+ # Author:: Stephen Delano (<stephen@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ # == FileUtils::Entry_ (Patch)
20
+ # On Ruby 1.9.3 and earlier, FileUtils.cp_r(foo, bar, :preserve => true) fails
21
+ # when attempting to copy a directory containing symlinks. This has been
22
+ # patched in the trunk of Ruby, and this is a monkey patch of the offending
23
+ # code.
24
+
25
+ unless RUBY_VERSION =~ /^2/
26
+ require 'fileutils'
27
+
28
+ class FileUtils::Entry_
29
+ def copy_metadata(path)
30
+ st = lstat()
31
+ if !st.symlink?
32
+ File.utime st.atime, st.mtime, path
33
+ end
34
+ begin
35
+ if st.symlink?
36
+ begin
37
+ File.lchown st.uid, st.gid, path
38
+ rescue NotImplementedError
39
+ end
40
+ else
41
+ File.chown st.uid, st.gid, path
42
+ end
43
+ rescue Errno::EPERM
44
+ # clear setuid/setgid
45
+ if st.symlink?
46
+ begin
47
+ File.lchmod st.mode & 01777, path
48
+ rescue NotImplementedError
49
+ end
50
+ else
51
+ File.chmod st.mode & 01777, path
52
+ end
53
+ else
54
+ if st.symlink?
55
+ begin
56
+ File.lchmod st.mode, path
57
+ rescue NotImplementedError
58
+ end
59
+ else
60
+ File.chmod st.mode, path
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -18,6 +18,7 @@
18
18
 
19
19
  require "chef/mixin/command"
20
20
  require "chef/mixin/from_file"
21
+ require "chef/monkey_patches/fileutils"
21
22
  require "chef/provider/git"
22
23
  require "chef/provider/subversion"
23
24
 
@@ -262,7 +263,7 @@ class Chef
262
263
  target_dir_path = @new_resource.deploy_to + "/releases"
263
264
  converge_by("deploy from repo to #{@target_dir_path} ") do
264
265
  FileUtils.mkdir_p(target_dir_path)
265
- run_command(:command => "cp -RPp #{::File.join(@new_resource.destination, ".")} #{release_path}")
266
+ FileUtils.cp_r(::File.join(@new_resource.destination, "."), release_path, :preserve => true)
266
267
  Chef::Log.info "#{@new_resource} copied the cached checkout to #{release_path}"
267
268
  release_created(release_path)
268
269
  end
@@ -61,7 +61,7 @@ class Chef
61
61
  description << diff_current(rendered_template.path)
62
62
  converge_by(description) do
63
63
  backup
64
- FileUtils.mv(rendered_template.path, @new_resource.path)
64
+ FileUtils.cp(rendered_template.path, @new_resource.path)
65
65
  Chef::Log.info("#{@new_resource} updated content")
66
66
  access_controls.set_all!
67
67
  stat = ::File.stat(@new_resource.path)
@@ -17,7 +17,7 @@
17
17
 
18
18
  class Chef
19
19
  CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
20
- VERSION = '10.16.4'
20
+ VERSION = '10.16.6.rc.0'
21
21
  end
22
22
 
23
23
  # NOTE: the Chef::Version class is defined in version_class.rb
@@ -23,8 +23,6 @@ describe Chef::Knife::CookbookDelete do
23
23
  before(:all) do
24
24
  @original_config = Chef::Config.hash_dup
25
25
 
26
- Thin::Logging.silent = true
27
-
28
26
  @server = TinyServer::Manager.new
29
27
  @server.start
30
28
  end
@@ -6,9 +6,9 @@
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
8
8
  # You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -23,8 +23,6 @@ describe Chef::Knife::Exec do
23
23
  before(:all) do
24
24
  @original_config = Chef::Config.hash_dup
25
25
 
26
- Thin::Logging.silent = false
27
-
28
26
  @server = TinyServer::Manager.new#(:debug => true)
29
27
  @server.start
30
28
  end
@@ -24,7 +24,6 @@ describe Chef::Knife::Ssh do
24
24
  before(:all) do
25
25
  @original_config = Chef::Config.hash_dup
26
26
  Chef::Knife::Ssh.load_deps
27
- Thin::Logging.silent = true
28
27
  @server = TinyServer::Manager.new
29
28
  @server.start
30
29
  end
@@ -24,7 +24,13 @@ describe Chef::Resource::CookbookFile do
24
24
  let(:file_base) { 'cookbook_file_spec' }
25
25
  let(:source) { 'java.response' }
26
26
  let(:cookbook_name) { 'java' }
27
- let(:expected_content) { IO.read(File.join(CHEF_SPEC_DATA, 'cookbooks', 'java', 'files', 'default', 'java.response')) }
27
+ let(:expected_content) do
28
+ content = File.open(File.join(CHEF_SPEC_DATA, 'cookbooks', 'java', 'files', 'default', 'java.response'), "rb") do |f|
29
+ f.read
30
+ end
31
+ content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
32
+ content
33
+ end
28
34
 
29
35
  def create_resource
30
36
  # set up cookbook collection for this run to use, based on our
@@ -69,16 +69,20 @@ describe Chef::Resource::DeployRevision, :unix_only => true do
69
69
 
70
70
  let(:git_bundle_with_in_repo_callbacks) { File.expand_path("git_bundles/sinatra-test-app-with-callback-files.gitbundle", CHEF_SPEC_DATA) }
71
71
 
72
+ let(:git_bundle_with_in_repo_symlinks) { File.expand_path("git_bundles/sinatra-test-app-with-symlinks.gitbundle", CHEF_SPEC_DATA) }
73
+
72
74
  # This is the fourth version
73
75
  let(:latest_rev) { "3eb5ca6c353c83d9179dd3b29347539829b401f3" }
74
76
 
75
77
  # This is the third version
76
78
  let(:previous_rev) { "6d19a6dbecc8e37f5b2277345885c0c783eb8fb1" }
77
79
 
78
-
79
80
  # This is the sixth version, it is on the "with-deploy-scripts" branch
80
81
  let(:rev_with_in_repo_callbacks) { "2404d015882659754bdb93ad6e4b4d3d02691a82" }
81
82
 
83
+ # This is the fifth version in the "with-symlinks" branch
84
+ let(:rev_with_in_repo_symlinks) { "5a4748c52aaea8250b4346a9b8ede95ee3755e28" }
85
+
82
86
  # Read values from the +observe_order_file+ and split each line. This way you
83
87
  # can see in which order things really happened.
84
88
  def actual_operations_order
@@ -494,6 +498,18 @@ describe Chef::Resource::DeployRevision, :unix_only => true do
494
498
  end
495
499
  end
496
500
 
501
+ context "when deploying an app with in-repo symlinks" do
502
+ let(:deploy_with_in_repo_symlinks) do
503
+ basic_deploy_resource.dup.tap do |r|
504
+ r.repo git_bundle_with_in_repo_symlinks
505
+ r.revision rev_with_in_repo_symlinks
506
+ end
507
+ end
508
+
509
+ it "should not raise an exception calling File.utime on symlinks" do
510
+ lambda { deploy_with_in_repo_symlinks.run_action(:deploy) }.should_not raise_error
511
+ end
512
+ end
497
513
  end
498
514
 
499
515
 
@@ -22,7 +22,7 @@ if windows?
22
22
  require 'chef/win32/file' #probably need this in spec_helper
23
23
  end
24
24
 
25
- describe Chef::Resource::Link do
25
+ describe Chef::Resource::Link, :not_supported_on_win2k3 do
26
26
  let(:file_base) { "file_spec" }
27
27
 
28
28
  let(:base_dir) do
@@ -24,7 +24,13 @@ describe Chef::Resource::RemoteFile do
24
24
 
25
25
  let(:file_base) { "remote_file_spec" }
26
26
  let(:source) { 'http://localhost:9000/nyan_cat.png' }
27
- let(:expected_content) { IO.read(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png')) }
27
+ let(:expected_content) do
28
+ content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'), "rb") do |f|
29
+ f.read
30
+ end
31
+ content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding)
32
+ content
33
+ end
28
34
 
29
35
  def create_resource
30
36
  node = Chef::Node.new
@@ -40,13 +46,14 @@ describe Chef::Resource::RemoteFile do
40
46
  end
41
47
 
42
48
  before(:all) do
43
- Thin::Logging.silent = false
44
49
  @server = TinyServer::Manager.new
45
50
  @server.start
46
51
  @api = TinyServer::API.instance
47
52
  @api.clear
48
53
  @api.get("/nyan_cat.png", 200) {
49
- IO.read(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'))
54
+ File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'), "rb") do |f|
55
+ f.read
56
+ end
50
57
  }
51
58
  end
52
59
 
@@ -37,15 +37,16 @@ describe TinyServer::API do
37
37
 
38
38
  it "creates a route for a GET request" do
39
39
  @api.get('/foo/bar', 200, 'hello foobar')
40
- response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => '/foo/bar')
41
- response.should == [200, {'Content-Type' => 'application/json'}, 'hello foobar']
40
+ # WEBrick gives you the full URI with host, Thin only gave the part after scheme+host+port
41
+ response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => 'http://localhost:1974/foo/bar')
42
+ response.should == [200, {'Content-Type' => 'application/json'}, [ 'hello foobar' ]]
42
43
  end
43
44
 
44
45
  it "creates a route for a request with a block" do
45
46
  block_called = false
46
47
  @api.get('/bar/baz', 200) { block_called = true; 'hello barbaz' }
47
- response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => '/bar/baz')
48
- response.should == [200, {'Content-Type' => 'application/json'}, 'hello barbaz']
48
+ response = @api.call("REQUEST_METHOD" => "GET", "REQUEST_URI" => 'http://localhost:1974/bar/baz')
49
+ response.should == [200, {'Content-Type' => 'application/json'}, [ 'hello barbaz' ]]
49
50
  block_called.should be_true
50
51
  end
51
52
 
@@ -66,11 +66,13 @@ RSpec.configure do |config|
66
66
 
67
67
  # Add jruby filters here
68
68
  config.filter_run_excluding :windows_only => true unless windows?
69
+ config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3?
69
70
  config.filter_run_excluding :unix_only => true unless unix?
70
71
  config.filter_run_excluding :ruby_18_only => true unless ruby_18?
71
72
  config.filter_run_excluding :ruby_19_only => true unless ruby_19?
72
73
  config.filter_run_excluding :requires_root => true unless ENV['USER'] == 'root'
73
74
  config.filter_run_excluding :requires_unprivileged_user => true if ENV['USER'] == 'root'
75
+ config.filter_run_excluding :uses_diff => true unless has_diff?
74
76
 
75
77
  config.run_all_when_everything_filtered = true
76
78
  config.treat_symbols_as_metadata_keys_with_true_values = true
@@ -50,3 +50,16 @@ def make_tmpname(prefix_suffix, n)
50
50
  path << "-#{n}" if n
51
51
  path << suffix
52
52
  end
53
+
54
+ # NOTE:
55
+ # This is a temporary fix to get tests passing on systems that have no `diff`
56
+ # until we can replace shelling out to `diff` with ruby diff-lcs
57
+ def has_diff?
58
+ begin
59
+ diff_cmd = Mixlib::ShellOut.new("diff -v")
60
+ diff_cmd.run_command
61
+ true
62
+ rescue Errno::ENOENT
63
+ false
64
+ end
65
+ end
@@ -10,6 +10,14 @@ def windows?
10
10
  !!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
11
11
  end
12
12
 
13
+ def windows_win2k3?
14
+ return false unless windows?
15
+ require 'ruby-wmi'
16
+
17
+ host = WMI::Win32_OperatingSystem.find(:first)
18
+ (host.version && host.version.start_with?("5.2"))
19
+ end
20
+
13
21
  # def jruby?
14
22
 
15
23
  def unix?
@@ -78,6 +78,14 @@ shared_examples_for "a file resource" do
78
78
  # note the stripping of the drive letter from the tmpdir on windows
79
79
  let(:backup_glob) { File.join(CHEF_SPEC_BACKUP_PATH, Dir.tmpdir.sub(/^([A-Za-z]:)/, ""), "#{file_base}*") }
80
80
 
81
+ def binread(file)
82
+ content = File.open(file, "rb") do |f|
83
+ f.read
84
+ end
85
+ content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding)
86
+ content
87
+ end
88
+
81
89
  context "when the target file does not exist" do
82
90
  it "creates the file when the :create action is run" do
83
91
  resource.run_action(:create)
@@ -86,12 +94,12 @@ shared_examples_for "a file resource" do
86
94
 
87
95
  it "creates the file with the correct content when the :create action is run" do
88
96
  resource.run_action(:create)
89
- IO.read(path).should == expected_content
97
+ binread(path).should == expected_content
90
98
  end
91
99
 
92
100
  it "creates the file with the correct content when the :create_if_missing action is run" do
93
101
  resource.run_action(:create_if_missing)
94
- IO.read(path).should == expected_content
102
+ binread(path).should == expected_content
95
103
  end
96
104
 
97
105
  it "deletes the file when the :delete action is run" do
@@ -112,7 +120,7 @@ shared_examples_for "a file resource" do
112
120
 
113
121
  context "when the target file has the wrong content" do
114
122
  before(:each) do
115
- File.open(path, "w") { |f| f.print "This is so wrong!!!" }
123
+ File.open(path, "wb") { |f| f.print "This is so wrong!!!" }
116
124
  @expected_mtime = File.stat(path).mtime
117
125
  @expected_checksum = sha256_checksum(path)
118
126
  end
@@ -136,7 +144,7 @@ shared_examples_for "a file resource" do
136
144
 
137
145
  context "when the target file has the correct content" do
138
146
  before(:each) do
139
- File.open(path, "w") { |f| f.print expected_content }
147
+ File.open(path, "wb") { |f| f.print expected_content }
140
148
  @expected_mtime = File.stat(path).mtime
141
149
  @expected_atime = File.stat(path).atime
142
150
  @expected_checksum = sha256_checksum(path)
@@ -29,12 +29,16 @@ shared_context "setup correct permissions" do
29
29
  before :each do
30
30
  File.chown(Etc.getpwnam('nobody').uid, 1337, path)
31
31
  File.chmod(0776, path)
32
+ now = Time.now.to_i
33
+ File.utime(now - 9000, now - 9000, path)
32
34
  end
33
35
  end
34
36
 
35
37
  context "without root", :requires_unprivileged_user do
36
38
  before :each do
37
39
  File.chmod(0776, path)
40
+ now = Time.now.to_i
41
+ File.utime(now - 9000, now - 9000, path)
38
42
  end
39
43
  end
40
44
  end
@@ -17,11 +17,13 @@
17
17
  #
18
18
 
19
19
  require 'rubygems'
20
+ require 'webrick'
20
21
  require 'rack'
21
- require 'thin'
22
+ #require 'thin'
22
23
  require 'singleton'
23
24
  require 'chef/json_compat'
24
25
  require 'open-uri'
26
+ require 'chef/config'
25
27
 
26
28
  module TinyServer
27
29
 
@@ -29,30 +31,42 @@ module TinyServer
29
31
 
30
32
  attr_writer :app
31
33
 
32
- def self.run(options=nil, &block)
34
+ def self.setup(options=nil, &block)
33
35
  tiny_app = new(options)
34
36
  app_code = Rack::Builder.new(&block).to_app
35
37
  tiny_app.app = app_code
36
- tiny_app.start
38
+ tiny_app
39
+ end
40
+
41
+ def shutdown
42
+ server.shutdown
37
43
  end
38
44
  end
39
45
 
40
46
  class Manager
41
47
 
42
- DEFAULT_OPTIONS = {:server => 'thin', :Port => 9000, :Host => 'localhost', :environment => :none}
48
+ # 5 == debug, 3 == warning
49
+ LOGGER = WEBrick::Log.new(STDOUT, 3)
50
+ DEFAULT_OPTIONS = {
51
+ :server => 'webrick',
52
+ :Port => 9000,
53
+ :Host => 'localhost',
54
+ :environment => :none,
55
+ :Logger => LOGGER,
56
+ :AccessLog => [] # Remove this option to enable the access log when debugging.
57
+ }
43
58
 
44
59
  def initialize(options=nil)
45
60
  @options = options ? DEFAULT_OPTIONS.merge(options) : DEFAULT_OPTIONS
46
61
  @creator = caller.first
47
-
48
- Thin::Logging.silent = !@options[:debug]
49
62
  end
50
63
 
51
64
  def start
52
65
  @server_thread = Thread.new do
53
- @server = Server.run(@options) do
66
+ @server = Server.setup(@options) do
54
67
  run API.instance
55
68
  end
69
+ @server.start
56
70
  end
57
71
  block_until_started
58
72
  end
@@ -63,7 +77,10 @@ module TinyServer
63
77
 
64
78
  def block_until_started
65
79
  200.times do
66
- return true if started?
80
+ if started?
81
+ raise "ivar weirdness" if @server.nil?
82
+ return true
83
+ end
67
84
  end
68
85
  raise "TinyServer failed to boot :/"
69
86
  end
@@ -84,6 +101,7 @@ module TinyServer
84
101
 
85
102
  def stop
86
103
  # yes, this is terrible.
104
+ @server.shutdown
87
105
  @server_thread.kill
88
106
  @server_thread.join
89
107
  @server_thread = nil
@@ -132,7 +150,7 @@ module TinyServer
132
150
  debug_info = {:message => "no data matches the request for #{env['REQUEST_URI']}",
133
151
  :available_routes => @routes, :request => env}
134
152
  # Uncomment me for glorious debugging
135
- #pp :not_found => debug_info
153
+ # pp :not_found => debug_info
136
154
  [404, {'Content-Type' => 'application/json'}, debug_info.to_json]
137
155
  end
138
156
  end
@@ -152,6 +170,7 @@ module TinyServer
152
170
  end
153
171
 
154
172
  def matches_request?(uri)
173
+ uri = URI.parse(uri).request_uri
155
174
  @path_spec === uri
156
175
  end
157
176
 
@@ -171,7 +190,7 @@ module TinyServer
171
190
 
172
191
  def call
173
192
  data = @data || @block.call
174
- [@response_code, HEADERS, data]
193
+ [@response_code, HEADERS, Array(data)]
175
194
  end
176
195
 
177
196
  def to_s