chef 11.10.0.alpha.1 → 11.10.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/README.md +57 -36
  2. data/distro/common/html/chef-client.8.html +4 -4
  3. data/distro/common/html/chef-expander.8.html +4 -4
  4. data/distro/common/html/chef-expanderctl.8.html +4 -4
  5. data/distro/common/html/chef-server-webui.8.html +4 -4
  6. data/distro/common/html/chef-server.8.html +4 -4
  7. data/distro/common/html/chef-shell.1.html +4 -4
  8. data/distro/common/html/chef-solo.8.html +4 -4
  9. data/distro/common/html/chef-solr.8.html +5 -5
  10. data/distro/common/html/knife-bootstrap.1.html +4 -4
  11. data/distro/common/html/knife-client.1.html +4 -4
  12. data/distro/common/html/knife-configure.1.html +4 -4
  13. data/distro/common/html/knife-cookbook-site.1.html +4 -4
  14. data/distro/common/html/knife-cookbook.1.html +4 -4
  15. data/distro/common/html/knife-data-bag.1.html +4 -4
  16. data/distro/common/html/knife-environment.1.html +4 -4
  17. data/distro/common/html/knife-exec.1.html +4 -4
  18. data/distro/common/html/knife-index.1.html +4 -4
  19. data/distro/common/html/knife-node.1.html +4 -4
  20. data/distro/common/html/knife-role.1.html +4 -4
  21. data/distro/common/html/knife-search.1.html +4 -4
  22. data/distro/common/html/knife-ssh.1.html +4 -4
  23. data/distro/common/html/knife-status.1.html +4 -4
  24. data/distro/common/html/knife-tag.1.html +4 -4
  25. data/distro/common/html/knife.1.html +4 -4
  26. data/distro/common/man/man1/knife-bootstrap.1 +58 -64
  27. data/distro/common/man/man1/knife-client.1 +19 -22
  28. data/distro/common/man/man1/knife-configure.1 +37 -46
  29. data/distro/common/man/man1/knife-cookbook-site.1 +14 -17
  30. data/distro/common/man/man1/knife-cookbook.1 +15 -18
  31. data/distro/common/man/man1/knife-data-bag.1 +14 -17
  32. data/distro/common/man/man1/knife-delete.1 +38 -47
  33. data/distro/common/man/man1/knife-deps.1 +39 -48
  34. data/distro/common/man/man1/knife-diff.1 +43 -52
  35. data/distro/common/man/man1/knife-download.1 +47 -53
  36. data/distro/common/man/man1/knife-edit.1 +32 -41
  37. data/distro/common/man/man1/knife-environment.1 +14 -17
  38. data/distro/common/man/man1/knife-exec.1 +52 -61
  39. data/distro/common/man/man1/knife-index-rebuild.1 +1 -61
  40. data/distro/common/man/man1/knife-list.1 +47 -59
  41. data/distro/common/man/man1/knife-node.1 +15 -18
  42. data/distro/common/man/man1/knife-raw.1 +28 -46
  43. data/distro/common/man/man1/knife-recipe-list.1 +1 -61
  44. data/distro/common/man/man1/knife-role.1 +19 -25
  45. data/distro/common/man/man1/knife-search.1 +53 -62
  46. data/distro/common/man/man1/knife-show.1 +36 -28
  47. data/distro/common/man/man1/knife-ssh.1 +55 -61
  48. data/distro/common/man/man1/knife-status.1 +34 -43
  49. data/distro/common/man/man1/knife-tag.1 +14 -17
  50. data/distro/common/man/man1/knife-upload.1 +47 -56
  51. data/distro/common/man/man1/knife-user.1 +17 -20
  52. data/distro/common/man/man1/knife-xargs.1 +60 -69
  53. data/lib/chef/application.rb +3 -1
  54. data/lib/chef/application/windows_service.rb +0 -1
  55. data/lib/chef/client.rb +41 -152
  56. data/lib/chef/config.rb +19 -23
  57. data/lib/chef/data_bag.rb +1 -1
  58. data/lib/chef/data_bag_item.rb +1 -1
  59. data/lib/chef/exceptions.rb +8 -0
  60. data/lib/chef/formatters/doc.rb +15 -0
  61. data/lib/chef/formatters/error_inspectors/api_error_formatting.rb +2 -1
  62. data/lib/chef/http.rb +18 -8
  63. data/lib/chef/http/authenticator.rb +4 -0
  64. data/lib/chef/http/cookie_manager.rb +3 -0
  65. data/lib/chef/http/decompressor.rb +4 -0
  66. data/lib/chef/http/json_input.rb +4 -0
  67. data/lib/chef/http/json_output.rb +4 -0
  68. data/lib/chef/http/validate_content_length.rb +94 -0
  69. data/lib/chef/knife.rb +0 -1
  70. data/lib/chef/knife/configure.rb +6 -6
  71. data/lib/chef/knife/cookbook_create.rb +2 -2
  72. data/lib/chef/knife/core/subcommand_loader.rb +49 -3
  73. data/lib/chef/knife/ssh.rb +34 -4
  74. data/lib/chef/mixin/path_sanity.rb +1 -0
  75. data/lib/chef/monologger.rb +1 -2
  76. data/lib/chef/node.rb +7 -0
  77. data/lib/chef/policy_builder.rb +49 -0
  78. data/lib/chef/policy_builder/expand_node_object.rb +230 -0
  79. data/lib/chef/policy_builder/policyfile.rb +338 -0
  80. data/lib/chef/provider/file.rb +15 -5
  81. data/lib/chef/provider/group.rb +6 -2
  82. data/lib/chef/provider/group/windows.rb +12 -2
  83. data/lib/chef/provider/http_request.rb +3 -2
  84. data/lib/chef/provider/package.rb +1 -0
  85. data/lib/chef/provider/package/aix.rb +1 -1
  86. data/lib/chef/provider/service/debian.rb +7 -2
  87. data/lib/chef/resource/file.rb +8 -1
  88. data/lib/chef/resource/package.rb +9 -0
  89. data/lib/chef/resource/service.rb +0 -1
  90. data/lib/chef/rest.rb +2 -0
  91. data/lib/chef/run_context.rb +1 -1
  92. data/lib/chef/util/file_edit.rb +1 -1
  93. data/lib/chef/util/windows/net_group.rb +7 -6
  94. data/lib/chef/version.rb +1 -1
  95. data/lib/chef/win32/version.rb +31 -18
  96. data/spec/data/cookbooks/preseed/templates/default/preseed-template-variables.seed +1 -0
  97. data/spec/functional/resource/file_spec.rb +0 -1
  98. data/spec/functional/resource/group_spec.rb +96 -16
  99. data/spec/functional/resource/package_spec.rb +17 -0
  100. data/spec/functional/resource/user_spec.rb +2 -2
  101. data/spec/functional/win32/versions_spec.rb +39 -0
  102. data/spec/integration/client/client_spec.rb +27 -28
  103. data/spec/spec_helper.rb +2 -0
  104. data/spec/support/platform_helpers.rb +7 -1
  105. data/spec/support/shared/functional/file_resource.rb +83 -43
  106. data/spec/unit/application_spec.rb +7 -5
  107. data/spec/unit/client_spec.rb +10 -3
  108. data/spec/unit/config_spec.rb +0 -30
  109. data/spec/unit/cookbook_spec.rb +1 -0
  110. data/spec/unit/data_bag_item_spec.rb +8 -0
  111. data/spec/unit/data_bag_spec.rb +6 -0
  112. data/spec/unit/http_spec.rb +48 -0
  113. data/spec/unit/knife/core/subcommand_loader_spec.rb +77 -1
  114. data/spec/unit/knife/ssh_spec.rb +107 -0
  115. data/spec/unit/mixin/path_sanity_spec.rb +6 -0
  116. data/spec/unit/mixin/securable_spec.rb +77 -3
  117. data/spec/unit/monologger_spec.rb +45 -0
  118. data/spec/unit/node_spec.rb +16 -0
  119. data/spec/unit/policy_builder/expand_node_object_spec.rb +320 -0
  120. data/spec/unit/policy_builder/policyfile_spec.rb +399 -0
  121. data/spec/unit/policy_builder_spec.rb +26 -0
  122. data/spec/unit/provider/deploy_spec.rb +3 -0
  123. data/spec/unit/provider/group/windows_spec.rb +1 -0
  124. data/spec/unit/provider/http_request_spec.rb +23 -1
  125. data/spec/unit/provider/service/debian_service_spec.rb +50 -19
  126. data/spec/unit/recipe_spec.rb +4 -0
  127. data/spec/unit/resource/package_spec.rb +5 -0
  128. data/spec/unit/rest_spec.rb +375 -278
  129. data/spec/unit/run_context_spec.rb +4 -0
  130. metadata +96 -59
  131. checksums.yaml +0 -7
@@ -352,16 +352,27 @@ class Chef
352
352
  if tempfile.path.nil? || !::File.exists?(tempfile.path)
353
353
  raise "chef-client is confused, trying to deploy a file that has no path or does not exist..."
354
354
  end
355
+
355
356
  # 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
356
357
  if ::File.file?(@new_resource.path) && contents_changed?
357
- diff.diff(@current_resource.path, tempfile.path)
358
- @new_resource.diff( diff.for_reporting ) unless file_created?
359
- description = [ "update content in file #{@new_resource.path} from #{short_cksum(@current_resource.checksum)} to #{short_cksum(checksum(tempfile.path))}" ]
360
- description << diff.for_output
358
+ description = [ "update content in file #{@new_resource.path} from \
359
+ #{short_cksum(@current_resource.checksum)} to #{short_cksum(checksum(tempfile.path))}" ]
360
+
361
+ # Hide the diff output if the resource is marked as a sensitive resource
362
+ if @new_resource.sensitive
363
+ @new_resource.diff("suppressed sensitive resource")
364
+ description << "suppressed sensitive resource"
365
+ else
366
+ diff.diff(@current_resource.path, tempfile.path)
367
+ @new_resource.diff( diff.for_reporting ) unless file_created?
368
+ description << diff.for_output
369
+ end
370
+
361
371
  converge_by(description) do
362
372
  update_file_contents
363
373
  end
364
374
  end
375
+
365
376
  # unlink necessary to clean up in why-run mode
366
377
  tempfile.unlink
367
378
  end
@@ -420,4 +431,3 @@ class Chef
420
431
  end
421
432
  end
422
433
  end
423
-
@@ -91,7 +91,7 @@ class Chef
91
91
  if(@new_resource.append)
92
92
  missing_members = []
93
93
  @new_resource.members.each do |member|
94
- next if @current_resource.members.include?(member)
94
+ next if has_current_group_member?(member)
95
95
  missing_members << member
96
96
  end
97
97
  if missing_members.length > 0
@@ -100,7 +100,7 @@ class Chef
100
100
 
101
101
  members_to_be_removed = []
102
102
  @new_resource.excluded_members.each do |member|
103
- if @current_resource.members.include?(member)
103
+ if has_current_group_member?(member)
104
104
  members_to_be_removed << member
105
105
  end
106
106
  end
@@ -116,6 +116,10 @@ class Chef
116
116
  !@change_desc.empty?
117
117
  end
118
118
 
119
+ def has_current_group_member?(member)
120
+ @current_resource.members.include?(member)
121
+ end
122
+
119
123
  def action_create
120
124
  case @group_exists
121
125
  when false
@@ -59,7 +59,7 @@ class Chef
59
59
  if @new_resource.append
60
60
  members_to_be_added = [ ]
61
61
  @new_resource.members.each do |member|
62
- members_to_be_added << member if !@current_resource.members.include?(member)
62
+ members_to_be_added << member if ! has_current_group_member?(member)
63
63
  end
64
64
 
65
65
  # local_add_members will raise ERROR_MEMBER_IN_ALIAS if a
@@ -68,7 +68,8 @@ class Chef
68
68
 
69
69
  members_to_be_removed = [ ]
70
70
  @new_resource.excluded_members.each do |member|
71
- members_to_be_removed << member if @current_resource.members.include?(member)
71
+ member_sid = local_group_name_to_sid(member)
72
+ members_to_be_removed << member if has_current_group_member?(member)
72
73
  end
73
74
  @net_group.local_delete_members(members_to_be_removed) unless members_to_be_removed.empty?
74
75
  else
@@ -76,10 +77,19 @@ class Chef
76
77
  end
77
78
  end
78
79
 
80
+ def has_current_group_member?(member)
81
+ member_sid = local_group_name_to_sid(member)
82
+ @current_resource.members.include?(member_sid)
83
+ end
84
+
79
85
  def remove_group
80
86
  @net_group.local_delete
81
87
  end
82
88
 
89
+ def local_group_name_to_sid(group_name)
90
+ locally_qualified_name = group_name.include?("\\") ? group_name : "#{ENV['COMPUTERNAME']}\\#{group_name}"
91
+ Chef::ReservedNames::Win32::Security.lookup_account_name(locally_qualified_name)[1].to_s
92
+ end
83
93
  end
84
94
  end
85
95
  end
@@ -36,7 +36,8 @@ class Chef
36
36
  # Send a HEAD request to @new_resource.url, with ?message=@new_resource.message
37
37
  def action_head
38
38
  message = check_message(@new_resource.message)
39
- # returns true from Chef::REST if returns 2XX (Net::HTTPSuccess)
39
+ # CHEF-4762: we expect a nil return value from Chef::HTTP for a "200 Success" response
40
+ # and false for a "304 Not Modified" response
40
41
  modified = @http.head(
41
42
  "#{@new_resource.url}?message=#{message}",
42
43
  @new_resource.headers
@@ -44,7 +45,7 @@ class Chef
44
45
  Chef::Log.info("#{@new_resource} HEAD to #{@new_resource.url} successful")
45
46
  Chef::Log.debug("#{@new_resource} HEAD request response: #{modified}")
46
47
  # :head is usually used to trigger notifications, which converge_by now does
47
- if modified
48
+ if modified != false
48
49
  converge_by("#{@new_resource} HEAD to #{@new_resource.url} returned modified, trigger notifications") {}
49
50
  end
50
51
  end
@@ -202,6 +202,7 @@ class Chef
202
202
  if template_available?(@new_resource.response_file)
203
203
  Chef::Log.debug("#{@new_resource} fetching preseed file via Template")
204
204
  remote_file = Chef::Resource::Template.new(cache_seed_to, run_context)
205
+ remote_file.variables(@new_resource.response_file_variables)
205
206
  elsif cookbook_file_available?(@new_resource.response_file)
206
207
  Chef::Log.debug("#{@new_resource} fetching preseed file via cookbook_file")
207
208
  remote_file = Chef::Resource::CookbookFile.new(cache_seed_to, run_context)
@@ -88,7 +88,7 @@ class Chef
88
88
  status = popen4("installp -L -d #{@new_resource.source}") do |pid, stdin, stdout, stderr|
89
89
  stdout.each_line do |line|
90
90
  case line
91
- when /\w:{Regexp.escape(@new_resource.package_name)}:(.*)/
91
+ when /\w:#{Regexp.escape(@new_resource.package_name)}:(.*)/
92
92
  fields = line.split(":")
93
93
  @candidate_version = fields[2]
94
94
  @new_resource.version(fields[2])
@@ -103,7 +103,7 @@ class Chef
103
103
  priority.each { |runlevel, arguments|
104
104
  Chef::Log.debug("#{@new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
105
105
  # if we are in a update-rc.d default startup runlevel && we start in this runlevel
106
- if (2..5).include?(runlevel.to_i) && arguments[0] == :start
106
+ if %w[ 1 2 3 4 5 S ].include?(runlevel) && arguments[0] == :start
107
107
  enabled = true
108
108
  end
109
109
  }
@@ -113,7 +113,12 @@ class Chef
113
113
 
114
114
  # Override method from parent to ensure priority is up-to-date
115
115
  def action_enable
116
- if @current_resource.enabled && @current_resource.priority == @new_resource.priority
116
+ if @new_resource.priority.nil?
117
+ priority_ok = true
118
+ else
119
+ priority_ok = @current_resource.priority == @new_resource.priority
120
+ end
121
+ if @current_resource.enabled and priority_ok
117
122
  Chef::Log.debug("#{@new_resource} already enabled - nothing to do")
118
123
  else
119
124
  converge_by("enable service #{@new_resource}") do
@@ -52,9 +52,9 @@ class Chef
52
52
  @force_unlink = false
53
53
  @manage_symlink_source = nil
54
54
  @diff = nil
55
+ @sensitive = false
55
56
  end
56
57
 
57
-
58
58
  def content(arg=nil)
59
59
  set_or_return(
60
60
  :content,
@@ -119,6 +119,13 @@ class Chef
119
119
  )
120
120
  end
121
121
 
122
+ def sensitive(arg=nil)
123
+ set_or_return(
124
+ :sensitive,
125
+ arg,
126
+ :kind_of => [ TrueClass, FalseClass ]
127
+ )
128
+ end
122
129
  end
123
130
  end
124
131
  end
@@ -36,6 +36,7 @@ class Chef
36
36
  @package_name = name
37
37
  @resource_name = :package
38
38
  @response_file = nil
39
+ @response_file_variables = Hash.new
39
40
  @source = nil
40
41
  @version = nil
41
42
  end
@@ -64,6 +65,14 @@ class Chef
64
65
  )
65
66
  end
66
67
 
68
+ def response_file_variables(arg=nil)
69
+ set_or_return(
70
+ :response_file_variables,
71
+ arg,
72
+ :kind_of => [ Hash ]
73
+ )
74
+ end
75
+
67
76
  def source(arg=nil)
68
77
  set_or_return(
69
78
  :source,
@@ -43,7 +43,6 @@ class Chef
43
43
  @init_command = nil
44
44
  @priority = nil
45
45
  @action = "nothing"
46
- @startup_type = :automatic
47
46
  @supports = { :restart => false, :reload => false, :status => false }
48
47
  @allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload)
49
48
  end
@@ -32,6 +32,7 @@ require 'chef/http/decompressor'
32
32
  require 'chef/http/json_input'
33
33
  require 'chef/http/json_to_model_output'
34
34
  require 'chef/http/cookie_manager'
35
+ require 'chef/http/validate_content_length'
35
36
  require 'chef/config'
36
37
  require 'chef/exceptions'
37
38
  require 'chef/platform/query_helpers'
@@ -62,6 +63,7 @@ class Chef
62
63
  @decompressor = Decompressor.new(options)
63
64
  @authenticator = Authenticator.new(options)
64
65
 
66
+ @middlewares << ValidateContentLength.new(options)
65
67
  @middlewares << JSONInput.new(options)
66
68
  @middlewares << JSONToModelOutput.new(options)
67
69
  @middlewares << CookieManager.new(options)
@@ -146,7 +146,7 @@ class Chef
146
146
  false
147
147
  else
148
148
  loaded_recipe(cookbook_name, recipe_short_name)
149
-
149
+ node.loaded_recipe(cookbook_name, recipe_short_name)
150
150
  cookbook = cookbook_collection[cookbook_name]
151
151
  cookbook.load_recipe(recipe_short_name, self)
152
152
  end
@@ -33,7 +33,7 @@ class Chef
33
33
  @file_edited = false
34
34
 
35
35
  raise ArgumentError, "File doesn't exist" unless File.exist? @original_pathname
36
- @contents = File.new(@original_pathname).readlines
36
+ @contents = File.new(@original_pathname){ |f| f.readlines }
37
37
  end
38
38
 
39
39
  #search the file line by line and match each line with the given regex
@@ -55,19 +55,20 @@ class Chef::Util::Windows::NetGroup < Chef::Util::Windows
55
55
  nread = 0.chr * PTR_SIZE
56
56
  total = 0.chr * PTR_SIZE
57
57
 
58
- rc = NetLocalGroupGetMembers.call(nil, @name, 1, ptr, -1,
58
+ rc = NetLocalGroupGetMembers.call(nil, @name, 0, ptr, -1,
59
59
  nread, total, handle)
60
60
  if (rc == NERR_Success) || (rc == ERROR_MORE_DATA)
61
61
  ptr = ptr.unpack('L')[0]
62
62
  nread = nread.unpack('i')[0]
63
- members = 0.chr * (nread * (PTR_SIZE * 3)) #nread * sizeof(LOCALGROUP_MEMBERS_INFO_1)
63
+ members = 0.chr * (nread * PTR_SIZE ) #nread * sizeof(LOCALGROUP_MEMBERS_INFO_0)
64
64
  memcpy(members, ptr, members.size)
65
65
 
66
- # 3 pointer fields in LOCALGROUP_MEMBERS_INFO_1, offset 2*PTR_SIZE is lgrmi1_name
66
+ # 1 pointer field in LOCALGROUP_MEMBERS_INFO_0, offset 0 is lgrmi0_sid
67
67
  nread.times do |i|
68
- offset = (i * 3) + 2
69
- member = lpwstr_to_s(members, offset)
70
- group_members << member
68
+ sid_address = members[i * PTR_SIZE, PTR_SIZE].unpack('L')[0]
69
+ sid_ptr = FFI::Pointer.new(sid_address)
70
+ member_sid = Chef::ReservedNames::Win32::Security::SID.new(sid_ptr)
71
+ group_members << member_sid.to_s
71
72
  end
72
73
  NetApiBufferFree(ptr)
73
74
  else
@@ -17,7 +17,7 @@
17
17
 
18
18
  class Chef
19
19
  CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
20
- VERSION = '11.10.0.alpha.1'
20
+ VERSION = '11.10.0.rc.0'
21
21
  end
22
22
 
23
23
  # NOTE: the Chef::Version class is defined in version_class.rb
@@ -35,20 +35,25 @@ class Chef
35
35
  Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(n_index)
36
36
  end
37
37
 
38
+ def self.method_name_from_marketing_name(marketing_name)
39
+ "#{marketing_name.gsub(/\s/, '_').gsub(/\./, '_').downcase}?"
40
+ # "#{marketing_name.gsub(/\s/, '_').gsub(//, '_').downcase}?"
41
+ end
42
+
38
43
  public
39
44
 
40
45
  WIN_VERSIONS = {
41
- "Windows 8.1" => {:major => 6, :minor => 3, :callable => lambda{ @product_type == VER_NT_WORKSTATION }},
42
- "Windows Server 2012 R2" => {:major => 6, :minor => 3, :callable => lambda{ @product_type != VER_NT_WORKSTATION }},
43
- "Windows 8" => {:major => 6, :minor => 2, :callable => lambda{ @product_type == VER_NT_WORKSTATION }},
44
- "Windows Server 2012" => {:major => 6, :minor => 2, :callable => lambda{ @product_type != VER_NT_WORKSTATION }},
45
- "Windows 7" => {:major => 6, :minor => 1, :callable => lambda{ @product_type == VER_NT_WORKSTATION }},
46
- "Windows Server 2008 R2" => {:major => 6, :minor => 1, :callable => lambda{ @product_type != VER_NT_WORKSTATION }},
47
- "Windows Server 2008" => {:major => 6, :minor => 0, :callable => lambda{ @product_type != VER_NT_WORKSTATION }},
48
- "Windows Vista" => {:major => 6, :minor => 0, :callable => lambda{ @product_type == VER_NT_WORKSTATION }},
49
- "Windows Server 2003 R2" => {:major => 5, :minor => 2, :callable => lambda{ get_system_metrics(SM_SERVERR2) != 0 }},
50
- "Windows Home Server" => {:major => 5, :minor => 2, :callable => lambda{ (@suite_mask & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER }},
51
- "Windows Server 2003" => {:major => 5, :minor => 2, :callable => lambda{ get_system_metrics(SM_SERVERR2) == 0 }},
46
+ "Windows 8.1" => {:major => 6, :minor => 3, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }},
47
+ "Windows Server 2012 R2" => {:major => 6, :minor => 3, :callable => lambda {|product_type, suite_mask| product_type != VER_NT_WORKSTATION }},
48
+ "Windows 8" => {:major => 6, :minor => 2, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }},
49
+ "Windows Server 2012" => {:major => 6, :minor => 2, :callable => lambda{ |product_type, suite_mask| product_type != VER_NT_WORKSTATION }},
50
+ "Windows 7" => {:major => 6, :minor => 1, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }},
51
+ "Windows Server 2008 R2" => {:major => 6, :minor => 1, :callable => lambda{ |product_type, suite_mask| product_type != VER_NT_WORKSTATION }},
52
+ "Windows Server 2008" => {:major => 6, :minor => 0, :callable => lambda{ |product_type, suite_mask| product_type != VER_NT_WORKSTATION }},
53
+ "Windows Vista" => {:major => 6, :minor => 0, :callable => lambda{ |product_type, suite_mask| product_type == VER_NT_WORKSTATION }},
54
+ "Windows Server 2003 R2" => {:major => 5, :minor => 2, :callable => lambda{ |product_type, suite_mask| get_system_metrics(SM_SERVERR2) != 0 }},
55
+ "Windows Home Server" => {:major => 5, :minor => 2, :callable => lambda{ |product_type, suite_mask| (suite_mask & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER }},
56
+ "Windows Server 2003" => {:major => 5, :minor => 2, :callable => lambda{ |product_type, suite_mask| get_system_metrics(SM_SERVERR2) == 0 }},
52
57
  "Windows XP" => {:major => 5, :minor => 1},
53
58
  "Windows 2000" => {:major => 5, :minor => 0}
54
59
  }
@@ -77,11 +82,11 @@ class Chef
77
82
 
78
83
  # General Windows checks
79
84
  WIN_VERSIONS.each do |k,v|
80
- method_name = "#{k.gsub(/\s/, '_').downcase}?"
85
+ method_name = method_name_from_marketing_name(k)
81
86
  define_method(method_name) do
82
87
  (@major_version == v[:major]) &&
83
88
  (@minor_version == v[:minor]) &&
84
- (v[:callable] ? v[:callable].call : true)
89
+ (v[:callable] ? v[:callable].call(@product_type, @suite_mask) : true)
85
90
  end
86
91
  marketing_names << [k, method_name]
87
92
  end
@@ -105,11 +110,19 @@ class Chef
105
110
  private
106
111
 
107
112
  def get_version
108
- version = GetVersion()
109
- major = LOBYTE(LOWORD(version))
110
- minor = HIBYTE(LOWORD(version))
111
- build = version < 0x80000000 ? HIWORD(version) : 0
112
- [major, minor, build]
113
+ # Use WMI here because API's like GetVersion return faked
114
+ # version numbers on Windows Server 2012 R2 and Windows 8.1 --
115
+ # WMI always returns the truth. See article at
116
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
117
+ require 'ruby-wmi'
118
+
119
+ os_info = WMI::Win32_OperatingSystem.find(:first)
120
+ os_version = os_info.send('Version')
121
+
122
+ # The operating system version is a string in the following form
123
+ # that can be split into components based on the '.' delimiter:
124
+ # MajorVersionNumber.MinorVersionNumber.BuildNumber
125
+ os_version.split('.').collect { | version_string | version_string.to_i }
113
126
  end
114
127
 
115
128
  def get_version_ex
@@ -0,0 +1 @@
1
+ chef-integration-test chef-integration-test/sample-var string "<%= @template_variable -%>"
@@ -115,5 +115,4 @@ describe Chef::Resource::File do
115
115
  end
116
116
  end
117
117
  end
118
-
119
118
  end
@@ -38,7 +38,8 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
38
38
  def user_exist_in_group?(user)
39
39
  case ohai[:platform_family]
40
40
  when "windows"
41
- Chef::Util::Windows::NetGroup.new(group_name).local_get_members.include?(user)
41
+ user_sid = sid_string_from_user(user)
42
+ user_sid.nil? ? false : Chef::Util::Windows::NetGroup.new(group_name).local_get_members.include?(user_sid)
42
43
  else
43
44
  Etc::getgrnam(group_name).mem.include?(user)
44
45
  end
@@ -57,6 +58,25 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
57
58
  return resource.gid == Etc::getgrnam(resource.name).gid if unix?
58
59
  end
59
60
 
61
+ def sid_string_from_user(user)
62
+ begin
63
+ sid = Chef::ReservedNames::Win32::Security.lookup_account_name(user)
64
+ rescue Chef::Exceptions::Win32APIError
65
+ sid = nil
66
+ end
67
+
68
+ sid.nil? ? nil : sid[1].to_s
69
+ end
70
+
71
+ def windows_domain_user?(user_name)
72
+ domain, user = user_name.split('\\')
73
+
74
+ if user && domain != '.'
75
+ computer_name = ENV['computername']
76
+ domain.downcase != computer_name.downcase
77
+ end
78
+ end
79
+
60
80
  def user(username)
61
81
  usr = Chef::Resource::User.new("#{username}", run_context)
62
82
  if ohai[:platform_family] == "windows"
@@ -66,12 +86,12 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
66
86
  end
67
87
 
68
88
  def create_user(username)
69
- user(username).run_action(:create)
89
+ user(username).run_action(:create) if ! windows_domain_user?(username)
70
90
  # TODO: User shouldn't exist
71
91
  end
72
92
 
73
93
  def remove_user(username)
74
- user(username).run_action(:remove)
94
+ user(username).run_action(:remove) if ! windows_domain_user?(username)
75
95
  # TODO: User shouldn't exist
76
96
  end
77
97
 
@@ -108,24 +128,24 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
108
128
  end
109
129
 
110
130
  describe "when append is not set" do
111
- let(:included_members) { ["spec-Eric"] }
131
+ let(:included_members) { [spec_members[1]] }
112
132
 
113
133
  before do
114
- create_user("spec-Eric")
115
- create_user("spec-Gordon")
116
- add_members_to_group(["spec-Gordon"])
134
+ create_user(spec_members[1])
135
+ create_user(spec_members[0])
136
+ add_members_to_group([spec_members[0]])
117
137
  end
118
138
 
119
139
  after do
120
- remove_user("spec-Eric")
121
- remove_user("spec-Gordon")
140
+ remove_user(spec_members[1])
141
+ remove_user(spec_members[0])
122
142
  end
123
143
 
124
144
  it "should remove the existing users and add the new users to the group" do
125
145
  group_resource.run_action(tested_action)
126
146
 
127
- user_exist_in_group?("spec-Eric").should == true
128
- user_exist_in_group?("spec-Gordon").should == false
147
+ user_exist_in_group?(spec_members[1]).should == true
148
+ user_exist_in_group?(spec_members[0]).should == false
129
149
  end
130
150
  end
131
151
 
@@ -160,7 +180,7 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
160
180
 
161
181
  describe "when group contains some users" do
162
182
  before(:each) do
163
- add_members_to_group([ "spec-Gordon", "spec-Anthony" ])
183
+ add_members_to_group([ spec_members[0], spec_members[2] ])
164
184
  end
165
185
 
166
186
  it "should add the included users and remove excluded users" do
@@ -192,6 +212,42 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
192
212
  end
193
213
  end
194
214
 
215
+ shared_examples_for "an expected invalid domain error case" do
216
+ let(:invalid_domain_user_name) { "no space\\administrator" }
217
+ let(:nonexistent_domain_user_name) { "xxfakedom\\administrator" }
218
+ before(:each) do
219
+ group_resource.members []
220
+ group_resource.excluded_members []
221
+ group_resource.append(true)
222
+ group_resource.run_action(:create)
223
+ group_should_exist(group_name)
224
+ end
225
+
226
+ describe "when updating membership" do
227
+ it "raises an error for a non well-formed domain name" do
228
+ group_resource.members [invalid_domain_user_name]
229
+ lambda { group_resource.run_action(tested_action) }.should raise_error Chef::Exceptions::Win32APIError
230
+ end
231
+
232
+ it "raises an error for a nonexistent domain" do
233
+ group_resource.members [nonexistent_domain_user_name]
234
+ lambda { group_resource.run_action(tested_action) }.should raise_error Chef::Exceptions::Win32APIError
235
+ end
236
+ end
237
+
238
+ describe "when removing members" do
239
+ it "raises an error for a non well-formed domain name" do
240
+ group_resource.excluded_members [invalid_domain_user_name]
241
+ lambda { group_resource.run_action(tested_action) }.should raise_error Chef::Exceptions::Win32APIError
242
+ end
243
+
244
+ it "raises an error for a nonexistent domain" do
245
+ group_resource.excluded_members [nonexistent_domain_user_name]
246
+ lambda { group_resource.run_action(tested_action) }.should raise_error Chef::Exceptions::Win32APIError
247
+ end
248
+ end
249
+ end
250
+
195
251
  let(:group_name) { "cheftest-#{SecureRandom.random_number(9999)}" }
196
252
  let(:included_members) { nil }
197
253
  let(:excluded_members) { nil }
@@ -274,8 +330,9 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
274
330
  end
275
331
 
276
332
  describe "group modify action", :not_supported_on_solaris do
277
- let(:included_members) { ["spec-Gordon", "spec-Eric"] }
278
- let(:excluded_members) { ["spec-Anthony"] }
333
+ let(:spec_members){ ["spec-Gordon", "spec-Eric", "spec-Anthony"] }
334
+ let(:included_members) { [spec_members[0], spec_members[1]] }
335
+ let(:excluded_members) { [spec_members[2]] }
279
336
  let(:tested_action) { :modify }
280
337
 
281
338
  describe "when there is no group" do
@@ -287,11 +344,23 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
287
344
  describe "when there is a group" do
288
345
  it_behaves_like "correct group management"
289
346
  end
347
+
348
+ describe "when running on Windows", :windows_only do
349
+ describe "when members are Active Directory domain identities", :windows_domain_joined_only do
350
+ let(:computer_domain) { ohai[:kernel]['cs_info']['domain'].split('.')[0] }
351
+ let(:spec_members){ ["#{computer_domain}\\Domain Admins", "#{computer_domain}\\Domain Users", "#{computer_domain}\\Domain Computers"] }
352
+
353
+ include_examples "correct group management"
354
+ end
355
+
356
+ it_behaves_like "an expected invalid domain error case"
357
+ end
290
358
  end
291
359
 
292
360
  describe "group manage action", :not_supported_on_solaris do
293
- let(:included_members) { ["spec-Gordon", "spec-Eric"] }
294
- let(:excluded_members) { ["spec-Anthony"] }
361
+ let(:spec_members){ ["spec-Gordon", "spec-Eric", "spec-Anthony"] }
362
+ let(:included_members) { [spec_members[0], spec_members[1]] }
363
+ let(:excluded_members) { [spec_members[2]] }
295
364
  let(:tested_action) { :manage }
296
365
 
297
366
  describe "when there is no group" do
@@ -304,6 +373,17 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
304
373
  describe "when there is a group" do
305
374
  it_behaves_like "correct group management"
306
375
  end
376
+
377
+ describe "running on windows", :windows_only do
378
+ describe "when members are Windows domain identities", :windows_domain_joined_only do
379
+ let(:computer_domain) { ohai[:kernel]['cs_info']['domain'].split('.')[0] }
380
+ let(:spec_members){ ["#{computer_domain}\\Domain Admins", "#{computer_domain}\\Domain Users", "#{computer_domain}\\Domain Computers"] }
381
+
382
+ include_examples "correct group management"
383
+ end
384
+
385
+ it_behaves_like "an expected invalid domain error case"
386
+ end
307
387
  end
308
388
 
309
389
  describe "group resource with Usermod provider", :solaris_only do