chef 11.14.6 → 11.16.0.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.
- checksums.yaml +4 -4
- data/lib/chef/exceptions.rb +2 -0
- data/lib/chef/mixin/windows_architecture_helper.rb +16 -0
- data/lib/chef/provider/dsc_script.rb +148 -0
- data/lib/chef/providers.rb +1 -0
- data/lib/chef/resource/dsc_script.rb +123 -0
- data/lib/chef/resources.rb +1 -0
- data/lib/chef/util/dsc/configuration_generator.rb +115 -0
- data/lib/chef/util/dsc/lcm_output_parser.rb +178 -0
- data/lib/chef/util/dsc/local_configuration_manager.rb +137 -0
- data/lib/chef/util/dsc/resource_info.rb +26 -0
- data/lib/chef/util/path_helper.rb +2 -2
- data/lib/chef/util/powershell/cmdlet.rb +136 -0
- data/lib/chef/util/powershell/cmdlet_result.rb +46 -0
- data/lib/chef/version.rb +1 -1
- data/spec/functional/resource/dsc_script_spec.rb +336 -0
- data/spec/functional/resource/group_spec.rb +5 -1
- data/spec/functional/util/powershell/cmdlet_spec.rb +114 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/platform_helpers.rb +24 -0
- data/spec/unit/provider/dsc_script_spec.rb +141 -0
- data/spec/unit/resource/dsc_script_spec.rb +94 -0
- data/spec/unit/util/dsc/configuration_generator_spec.rb +173 -0
- data/spec/unit/util/dsc/lcm_output_parser_spec.rb +178 -0
- data/spec/unit/util/dsc/local_configuration_manager_spec.rb +134 -0
- data/spec/unit/util/powershell/cmdlet_spec.rb +106 -0
- metadata +77 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df2b6f55c4e80aab655910adf3b81719c0f19a69
|
4
|
+
data.tar.gz: c9c30bb881de7fb5dd779c75c27d1cb0db6bdbeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 904801b965f8a0dfa042099487d8e137d120f77b82b244f85867dbfb1336828da39aab4ac5d600e041034f558f5def160c35749dcfa156c0573640ba16e1790a
|
7
|
+
data.tar.gz: 7766f87df2f6ec9c7f4bcc4805759660863f1464022952c942b32aa8ae632d22df5f2d47e48811c7e5c5c82fd0e727fe2dd157ea60c7960335a9cb27856c4845
|
data/lib/chef/exceptions.rb
CHANGED
@@ -117,6 +117,8 @@ class Chef
|
|
117
117
|
class ObsoleteDependencySyntax < ArgumentError; end
|
118
118
|
class InvalidDataBagPath < ArgumentError; end
|
119
119
|
|
120
|
+
class PowershellCmdletException < RuntimeError; end
|
121
|
+
|
120
122
|
# A different version of a cookbook was added to a
|
121
123
|
# VersionedRecipeList than the one already there.
|
122
124
|
class CookbookVersionConflict < ArgumentError ; end
|
@@ -41,6 +41,22 @@ class Chef
|
|
41
41
|
is_i386_process_on_x86_64_windows?
|
42
42
|
end
|
43
43
|
|
44
|
+
def with_os_architecture(node)
|
45
|
+
wow64_redirection_state = nil
|
46
|
+
|
47
|
+
if wow64_architecture_override_required?(node, node_windows_architecture(node))
|
48
|
+
wow64_redirection_state = disable_wow64_file_redirection(node)
|
49
|
+
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
if wow64_redirection_state
|
55
|
+
restore_wow64_file_redirection(node, wow64_redirection_state)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
44
60
|
def node_supports_windows_architecture?(node, desired_architecture)
|
45
61
|
assert_valid_windows_architecture!(desired_architecture)
|
46
62
|
return (node_windows_architecture(node) == :x86_64 ||
|
@@ -0,0 +1,148 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Edwards (<adamed@getchef.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2014, Chef Software, Inc.
|
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
|
+
require 'chef/util/powershell/cmdlet'
|
20
|
+
require 'chef/util/dsc/configuration_generator'
|
21
|
+
require 'chef/util/dsc/local_configuration_manager'
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Provider
|
25
|
+
class DscScript < Chef::Provider
|
26
|
+
def initialize(dsc_resource, run_context)
|
27
|
+
super(dsc_resource, run_context)
|
28
|
+
@dsc_resource = dsc_resource
|
29
|
+
@resource_converged = false
|
30
|
+
@operations = {
|
31
|
+
:set => Proc.new { |config_manager, document|
|
32
|
+
config_manager.set_configuration(document)
|
33
|
+
},
|
34
|
+
:test => Proc.new { |config_manager, document|
|
35
|
+
config_manager.test_configuration(document)
|
36
|
+
}}
|
37
|
+
end
|
38
|
+
|
39
|
+
def action_run
|
40
|
+
if ! @resource_converged
|
41
|
+
converge_by(generate_description) do
|
42
|
+
run_configuration(:set)
|
43
|
+
Chef::Log.info("DSC resource configuration completed successfully")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_current_resource
|
49
|
+
@dsc_resources_info = run_configuration(:test)
|
50
|
+
@resource_converged = @dsc_resources_info.all? do |resource|
|
51
|
+
!resource.changes_state?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def whyrun_supported?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def run_configuration(operation)
|
62
|
+
config_directory = ::Dir.mktmpdir("chef-dsc-script")
|
63
|
+
configuration_data_path = get_configuration_data_path(config_directory)
|
64
|
+
configuration_flags = get_augmented_configuration_flags(configuration_data_path)
|
65
|
+
|
66
|
+
config_manager = Chef::Util::DSC::LocalConfigurationManager.new(@run_context.node, config_directory)
|
67
|
+
|
68
|
+
begin
|
69
|
+
configuration_document = generate_configuration_document(config_directory, configuration_flags)
|
70
|
+
@operations[operation].call(config_manager, configuration_document)
|
71
|
+
rescue Exception => e
|
72
|
+
Chef::Log.error("DSC operation failed: #{e.message.to_s}")
|
73
|
+
raise e
|
74
|
+
ensure
|
75
|
+
::FileUtils.rm_rf(config_directory)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_augmented_configuration_flags(configuration_data_path)
|
80
|
+
updated_flags = nil
|
81
|
+
if configuration_data_path
|
82
|
+
updated_flags = @dsc_resource.flags.nil? ? {} : @dsc_resource.flags.dup
|
83
|
+
Chef::Util::PathHelper.validate_path(configuration_data_path)
|
84
|
+
updated_flags[:configurationdata] = configuration_data_path
|
85
|
+
end
|
86
|
+
updated_flags
|
87
|
+
end
|
88
|
+
|
89
|
+
def generate_configuration_document(config_directory, configuration_flags)
|
90
|
+
shellout_flags = {
|
91
|
+
:cwd => @dsc_resource.cwd,
|
92
|
+
:environment => @dsc_resource.environment,
|
93
|
+
:timeout => @dsc_resource.timeout
|
94
|
+
}
|
95
|
+
|
96
|
+
generator = Chef::Util::DSC::ConfigurationGenerator.new(@run_context.node, config_directory)
|
97
|
+
|
98
|
+
if @dsc_resource.command
|
99
|
+
generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_flags, shellout_flags)
|
100
|
+
else
|
101
|
+
# If code is also not provided, we mimic what the other script resources do (execute nothing)
|
102
|
+
Chef::Log.warn("Neither code or command were provided for dsc_resource[#{@dsc_resource.name}].") unless @dsc_resource.code
|
103
|
+
generator.configuration_document_from_script_code(@dsc_resource.code || '', configuration_flags, shellout_flags)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_configuration_data_path(config_directory)
|
108
|
+
if @dsc_resource.configuration_data_script
|
109
|
+
@dsc_resource.configuration_data_script
|
110
|
+
elsif @dsc_resource.configuration_data
|
111
|
+
configuration_data_path = "#{config_directory}/chef_dsc_config_data.psd1"
|
112
|
+
::File.open(configuration_data_path, 'wt') do | script |
|
113
|
+
script.write(@dsc_resource.configuration_data)
|
114
|
+
end
|
115
|
+
configuration_data_path
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def configuration_name
|
120
|
+
@dsc_resource.configuration_name || @dsc_resource.name
|
121
|
+
end
|
122
|
+
|
123
|
+
def configuration_friendly_name
|
124
|
+
if @dsc_resource.code
|
125
|
+
@dsc_resource.name
|
126
|
+
else
|
127
|
+
configuration_name
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def generate_description
|
134
|
+
["converge DSC configuration '#{configuration_friendly_name}'"] +
|
135
|
+
@dsc_resources_info.map do |resource|
|
136
|
+
if resource.changes_state?
|
137
|
+
# We ignore the last log message because it only contains the time it took, which looks weird
|
138
|
+
cleaned_messages = resource.change_log[0..-2].map { |c| c.sub(/^#{Regexp.escape(resource.name)}/, '').strip }
|
139
|
+
"converge DSC resource #{resource.name} by #{cleaned_messages.find_all{ |c| c != ''}.join("\n")}"
|
140
|
+
else
|
141
|
+
# This is needed because a dsc script can have resouces that are both converged and not
|
142
|
+
"converge DSC resource #{resource.name} by doing nothing because it is already converged"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/chef/providers.rb
CHANGED
@@ -24,6 +24,7 @@ require 'chef/provider/cron/solaris'
|
|
24
24
|
require 'chef/provider/cron/aix'
|
25
25
|
require 'chef/provider/deploy'
|
26
26
|
require 'chef/provider/directory'
|
27
|
+
require 'chef/provider/dsc_script'
|
27
28
|
require 'chef/provider/env'
|
28
29
|
require 'chef/provider/erl_call'
|
29
30
|
require 'chef/provider/execute'
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Edwards (<adamed@getchef.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 Chef Software, 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
|
+
class Chef
|
20
|
+
class Resource
|
21
|
+
class DscScript < Chef::Resource
|
22
|
+
|
23
|
+
provides :dsc_script, :on_platforms => ["windows"]
|
24
|
+
|
25
|
+
def initialize(name, run_context=nil)
|
26
|
+
super
|
27
|
+
@allowed_actions.push(:run)
|
28
|
+
@action = :run
|
29
|
+
provider(Chef::Provider::DscScript)
|
30
|
+
end
|
31
|
+
|
32
|
+
def code(arg=nil)
|
33
|
+
if arg && command
|
34
|
+
raise ArgumentError, "Only one of 'code' and 'command' attributes may be specified"
|
35
|
+
end
|
36
|
+
if arg && configuration_name
|
37
|
+
raise ArgumentError, "The 'code' and 'command' attributes may not be used together"
|
38
|
+
end
|
39
|
+
set_or_return(
|
40
|
+
:code,
|
41
|
+
arg,
|
42
|
+
:kind_of => [ String ]
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def configuration_name(arg=nil)
|
47
|
+
if arg && code
|
48
|
+
raise ArgumentError, "Attribute `configuration_name` may not be set if `code` is set"
|
49
|
+
end
|
50
|
+
set_or_return(
|
51
|
+
:configuration_name,
|
52
|
+
arg,
|
53
|
+
:kind_of => [ String ]
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def command(arg=nil)
|
58
|
+
if arg && code
|
59
|
+
raise ArgumentError, "The 'code' and 'command' attributes may not be used together"
|
60
|
+
end
|
61
|
+
set_or_return(
|
62
|
+
:command,
|
63
|
+
arg,
|
64
|
+
:kind_of => [ String ]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def configuration_data(arg=nil)
|
69
|
+
if arg && configuration_data_script
|
70
|
+
raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' attributes may not be used together"
|
71
|
+
end
|
72
|
+
set_or_return(
|
73
|
+
:configuration_data,
|
74
|
+
arg,
|
75
|
+
:kind_of => [ String ]
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def configuration_data_script(arg=nil)
|
80
|
+
if arg && configuration_data
|
81
|
+
raise ArgumentError, "The 'configuration_data' and 'configuration_data_script' attributes may not be used together"
|
82
|
+
end
|
83
|
+
set_or_return(
|
84
|
+
:configuration_data_script,
|
85
|
+
arg,
|
86
|
+
:kind_of => [ String ]
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def flags(arg=nil)
|
91
|
+
set_or_return(
|
92
|
+
:flags,
|
93
|
+
arg,
|
94
|
+
:kind_of => [ Hash ]
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def cwd(arg=nil)
|
99
|
+
set_or_return(
|
100
|
+
:cwd,
|
101
|
+
arg,
|
102
|
+
:kind_of => [ String ]
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
def environment(arg=nil)
|
107
|
+
set_or_return(
|
108
|
+
:environment,
|
109
|
+
arg,
|
110
|
+
:kind_of => [ Hash ]
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def timeout(arg=nil)
|
115
|
+
set_or_return(
|
116
|
+
:timeout,
|
117
|
+
arg,
|
118
|
+
:kind_of => [ Integer ]
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/chef/resources.rb
CHANGED
@@ -28,6 +28,7 @@ require 'chef/resource/deploy'
|
|
28
28
|
require 'chef/resource/deploy_revision'
|
29
29
|
require 'chef/resource/directory'
|
30
30
|
require 'chef/resource/dpkg_package'
|
31
|
+
require 'chef/resource/dsc_script'
|
31
32
|
require 'chef/resource/easy_install_package'
|
32
33
|
require 'chef/resource/env'
|
33
34
|
require 'chef/resource/erl_call'
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Edwards (<adamed@getchef.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2014, Chef Software, Inc.
|
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
|
+
require 'chef/util/powershell/cmdlet'
|
20
|
+
|
21
|
+
class Chef::Util::DSC
|
22
|
+
class ConfigurationGenerator
|
23
|
+
def initialize(node, config_directory)
|
24
|
+
@node = node
|
25
|
+
@config_directory = config_directory
|
26
|
+
end
|
27
|
+
|
28
|
+
def configuration_document_from_script_code(code, configuration_flags, shellout_flags)
|
29
|
+
Chef::Log.debug("DSC: DSC code:\n '#{code}'")
|
30
|
+
generated_script_path = write_document_generation_script(code, 'chef_dsc')
|
31
|
+
begin
|
32
|
+
configuration_document_from_script_path(generated_script_path, 'chef_dsc', configuration_flags, shellout_flags)
|
33
|
+
ensure
|
34
|
+
::FileUtils.rm(generated_script_path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def configuration_document_from_script_path(script_path, configuration_name, configuration_flags, shellout_flags)
|
39
|
+
validate_configuration_name!(configuration_name)
|
40
|
+
|
41
|
+
document_generation_cmdlet = Chef::Util::Powershell::Cmdlet.new(
|
42
|
+
@node,
|
43
|
+
configuration_document_generation_code(script_path, configuration_name))
|
44
|
+
|
45
|
+
merged_configuration_flags = get_merged_configuration_flags!(configuration_flags, configuration_name)
|
46
|
+
|
47
|
+
document_generation_cmdlet.run!(merged_configuration_flags, shellout_flags)
|
48
|
+
configuration_document_location = find_configuration_document(configuration_name)
|
49
|
+
|
50
|
+
if ! configuration_document_location
|
51
|
+
raise RuntimeError, "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script"
|
52
|
+
end
|
53
|
+
|
54
|
+
configuration_document = get_configuration_document(configuration_document_location)
|
55
|
+
::FileUtils.rm_rf(configuration_document_location)
|
56
|
+
configuration_document
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
# From PowerShell error help for the Configuration language element:
|
62
|
+
# Standard names may only contain letters (a-z, A-Z), numbers (0-9), and underscore (_).
|
63
|
+
# The name may not be null or empty, and should start with a letter.
|
64
|
+
def validate_configuration_name!(configuration_name)
|
65
|
+
if !!(configuration_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
|
66
|
+
raise ArgumentError, 'Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_merged_configuration_flags!(configuration_flags, configuration_name)
|
71
|
+
merged_configuration_flags = { :outputpath => configuration_document_directory(configuration_name) }
|
72
|
+
if configuration_flags
|
73
|
+
configuration_flags.map do | switch, value |
|
74
|
+
if merged_configuration_flags.key?(switch.to_s.downcase.to_sym)
|
75
|
+
raise ArgumentError, "The `flags` attribute for the dsc_script resource contained a command line switch :#{switch.to_s} that is disallowed."
|
76
|
+
end
|
77
|
+
merged_configuration_flags[switch.to_s.downcase.to_sym] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
merged_configuration_flags
|
81
|
+
end
|
82
|
+
|
83
|
+
def configuration_code(code, configuration_name)
|
84
|
+
"$ProgressPreference = 'SilentlyContinue';Configuration '#{configuration_name}'\n{\n\tnode 'localhost'\n{\n\t#{code}\n}}\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
def configuration_document_generation_code(configuration_script, configuration_name)
|
88
|
+
". '#{configuration_script}';#{configuration_name}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_document_generation_script(code, configuration_name)
|
92
|
+
script_path = "#{@config_directory}/chef_dsc_config.ps1"
|
93
|
+
::File.open(script_path, 'wt') do | script |
|
94
|
+
script.write(configuration_code(code, configuration_name))
|
95
|
+
end
|
96
|
+
script_path
|
97
|
+
end
|
98
|
+
|
99
|
+
def find_configuration_document(configuration_name)
|
100
|
+
document_directory = configuration_document_directory(configuration_name)
|
101
|
+
document_file_name = ::Dir.entries(document_directory).find { | path | path =~ /.*.mof/ }
|
102
|
+
::File.join(document_directory, document_file_name) if document_file_name
|
103
|
+
end
|
104
|
+
|
105
|
+
def configuration_document_directory(configuration_name)
|
106
|
+
::File.join(@config_directory, configuration_name)
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_configuration_document(document_path)
|
110
|
+
::File.open(document_path, 'rb') do | file |
|
111
|
+
file.read
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|