chef 11.14.6 → 11.16.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|