chef 11.14.6-x86-mingw32 → 11.16.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,133 @@
1
+ #
2
+ # Author:: Jay Mundrawala (<jdm@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/log'
20
+ require 'chef/util/dsc/resource_info'
21
+
22
+ class Chef
23
+ class Util
24
+ class DSC
25
+ class LocalConfigurationManager
26
+ module Parser
27
+ # Parses the output from LCM and returns a list of Chef::Util::DSC::ResourceInfo objects
28
+ # that describe how the resources affected the system
29
+ #
30
+ # Example:
31
+ # parse <<-EOF
32
+ # What if: [Machine]: LCM: [Start Set ]
33
+ # What if: [Machine]: LCM: [Start Resource ] [[File]FileToNotBeThere]
34
+ # What if: [Machine]: LCM: [Start Set ] [[File]FileToNotBeThere]
35
+ # What if: [C:\ShouldNotExist.txt] removed
36
+ # What if: [Machine]: LCM: [End Set ] [[File]FileToNotBeThere] in 0.1 seconds
37
+ # What if: [Machine]: LCM: [End Resource ] [[File]FileToNotBeThere]
38
+ # What if: [Machine]: LCM: [End Set ]
39
+ # EOF
40
+ #
41
+ # would return
42
+ #
43
+ # [
44
+ # Chef::Util::DSC::ResourceInfo.new(
45
+ # '[[File]FileToNotBeThere]',
46
+ # true,
47
+ # [
48
+ # '[[File]FileToNotBeThere]',
49
+ # '[C:\Shouldnotexist.txt]',
50
+ # '[[File]FileToNotBeThere] in 0.1 seconds'
51
+ # ]
52
+ # )
53
+ # ]
54
+ #
55
+ def self.parse(lcm_output)
56
+ return [] unless lcm_output
57
+
58
+ current_resource = Hash.new
59
+
60
+ resources = []
61
+ lcm_output.lines.each do |line|
62
+ op_action, op_type, info = parse_line(line)
63
+
64
+ case op_action
65
+ when :start
66
+ case op_type
67
+ when :set
68
+ if current_resource[:name]
69
+ current_resource[:context] = :logging
70
+ current_resource[:logs] = [info]
71
+ end
72
+ when :resource
73
+ if current_resource[:name]
74
+ resources.push(current_resource)
75
+ end
76
+ current_resource = {:name => info}
77
+ else
78
+ Chef::Log.debug("Ignoring op_action #{op_action}: Read line #{line}")
79
+ end
80
+ when :end
81
+ # Make sure we log the last line
82
+ if current_resource[:context] == :logging and info.include? current_resource[:name]
83
+ current_resource[:logs].push(info)
84
+ end
85
+ current_resource[:context] = nil
86
+ when :skip
87
+ current_resource[:skipped] = true
88
+ when :info
89
+ if current_resource[:context] == :logging
90
+ current_resource[:logs].push(info)
91
+ end
92
+ end
93
+ end
94
+
95
+ if current_resource[:name]
96
+ resources.push(current_resource)
97
+ end
98
+
99
+ build_resource_info(resources)
100
+ end
101
+
102
+ def self.parse_line(line)
103
+ if match = line.match(/^.*?:.*?:\s*LCM:\s*\[(.*?)\](.*)/)
104
+ # If the line looks like
105
+ # What If: [machinename]: LCM: [op_action op_type] message
106
+ # extract op_action, op_type, and message
107
+ operation, info = match.captures
108
+ op_action, op_type = operation.strip.split(' ').map {|m| m.downcase.to_sym}
109
+ else
110
+ op_action = op_type = :info
111
+ if match = line.match(/^.*?:.*?: \s+(.*)/)
112
+ info = match.captures[0]
113
+ else
114
+ info = line
115
+ end
116
+ end
117
+ info.strip! # Because this was formatted for humans
118
+ return [op_action, op_type, info]
119
+ end
120
+ private_class_method :parse_line
121
+
122
+ def self.build_resource_info(resources)
123
+ resources.map do |r|
124
+ Chef::Util::DSC::ResourceInfo.new(r[:name], !r[:skipped], r[:logs])
125
+ end
126
+ end
127
+ private_class_method :build_resource_info
128
+
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,137 @@
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/lcm_output_parser'
21
+
22
+ class Chef::Util::DSC
23
+ class LocalConfigurationManager
24
+ def initialize(node, configuration_path)
25
+ @node = node
26
+ @configuration_path = configuration_path
27
+ clear_execution_time
28
+ end
29
+
30
+ def test_configuration(configuration_document)
31
+ status = run_configuration_cmdlet(configuration_document)
32
+ handle_what_if_exception!(status.stderr) unless status.succeeded?
33
+ configuration_update_required?(status.return_value)
34
+ end
35
+
36
+ def set_configuration(configuration_document)
37
+ run_configuration_cmdlet(configuration_document, true)
38
+ end
39
+
40
+ def last_operation_execution_time_seconds
41
+ if @operation_start_time && @operation_end_time
42
+ @operation_end_time - @operation_start_time
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def run_configuration_cmdlet(configuration_document, apply_configuration = false)
49
+ Chef::Log.debug("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.")
50
+ test_only_parameters = ! apply_configuration ? '-whatif; if (! $?) { exit 1 }' : ''
51
+
52
+ start_operation_timing
53
+ command_code = lcm_command_code(@configuration_path, test_only_parameters)
54
+ status = nil
55
+
56
+ begin
57
+ save_configuration_document(configuration_document)
58
+ cmdlet = ::Chef::Util::Powershell::Cmdlet.new(@node, "#{command_code}")
59
+ if apply_configuration
60
+ status = cmdlet.run!
61
+ else
62
+ status = cmdlet.run
63
+ end
64
+ ensure
65
+ end_operation_timing
66
+ remove_configuration_document
67
+ if last_operation_execution_time_seconds
68
+ Chef::Log.debug("DSC: DSC operation completed in #{last_operation_execution_time_seconds} seconds.")
69
+ end
70
+ end
71
+ Chef::Log.debug("DSC: Completed call to DSC Local Config Manager")
72
+ status
73
+ end
74
+
75
+ def lcm_command_code(configuration_path, test_only_parameters)
76
+ <<-EOH
77
+ $ProgressPreference = 'SilentlyContinue';start-dscconfiguration -path #{@configuration_path} -wait -erroraction 'continue' -force #{test_only_parameters}
78
+ EOH
79
+ end
80
+
81
+ def handle_what_if_exception!(what_if_exception_output)
82
+ if what_if_exception_output.gsub(/\s+/, ' ') =~ /A parameter cannot be found that matches parameter name 'Whatif'/i
83
+ # LCM returns an error if any of the resources do not support the opptional What-If
84
+ Chef::Log::warn("Received error while testing configuration due to resource not supporting 'WhatIf'")
85
+ elsif output_has_dsc_module_failure?(what_if_exception_output)
86
+ 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+/, ' ')}")
87
+ else
88
+ raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{what_if_exception_output.gsub(/\s+/, ' ')}"
89
+ end
90
+ end
91
+
92
+ def output_has_dsc_module_failure?(what_if_output)
93
+ !! (what_if_output =~ /\sCimException/ &&
94
+ what_if_output =~ /ProviderOperationExecutionFailure/ &&
95
+ what_if_output =~ /\smodule\s+is\s+installed/)
96
+ end
97
+
98
+ def configuration_update_required?(what_if_output)
99
+ Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{what_if_output}")
100
+ begin
101
+ Parser::parse(what_if_output)
102
+ rescue Chef::Util::DSC::LocalConfigurationManager::Parser => e
103
+ Chef::Log::warn("Could not parse LCM output: #{e}")
104
+ [Chef::Util::DSC::ResourceInfo.new('Unknown DSC Resources', true, ['Unknown changes because LCM output was not parsable.'])]
105
+ end
106
+ end
107
+
108
+ def save_configuration_document(configuration_document)
109
+ ::FileUtils.mkdir_p(@configuration_path)
110
+ ::File.open(configuration_document_path, 'wb') do | file |
111
+ file.write(configuration_document)
112
+ end
113
+ end
114
+
115
+ def remove_configuration_document
116
+ ::FileUtils.rm(configuration_document_path)
117
+ end
118
+
119
+ def configuration_document_path
120
+ File.join(@configuration_path,'..mof')
121
+ end
122
+
123
+ def clear_execution_time
124
+ @operation_start_time = nil
125
+ @operation_end_time = nil
126
+ end
127
+
128
+ def start_operation_timing
129
+ clear_execution_time
130
+ @operation_start_time = Time.now
131
+ end
132
+
133
+ def end_operation_timing
134
+ @operation_end_time = Time.now
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,26 @@
1
+
2
+ class Chef
3
+ class Util
4
+ class DSC
5
+ class ResourceInfo
6
+ # The name is the text following [Start Set]
7
+ attr_reader :name
8
+
9
+ # A list of all log messages between [Start Set] and [End Set].
10
+ # Each line is an element in the list.
11
+ attr_reader :change_log
12
+
13
+ def initialize(name, sets, change_log)
14
+ @name = name
15
+ @sets = sets
16
+ @change_log = change_log || []
17
+ end
18
+
19
+ # Does this resource change the state of the system?
20
+ def changes_state?
21
+ @sets
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -32,7 +32,7 @@ class Chef
32
32
  Chef::Log.error(msg)
33
33
  raise Chef::Exceptions::ValidationFailed, msg
34
34
  end
35
-
35
+
36
36
  if windows_max_length_exceeded?(path)
37
37
  Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
38
38
  path.insert(0, "\\\\?\\")
@@ -50,7 +50,7 @@ class Chef
50
50
  return true
51
51
  end
52
52
  end
53
-
53
+
54
54
  false
55
55
  end
56
56
 
@@ -0,0 +1,136 @@
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 'mixlib/shellout'
20
+ require 'chef/mixin/windows_architecture_helper'
21
+ require 'chef/util/powershell/cmdlet_result'
22
+
23
+ class Chef::Util::Powershell
24
+ class Cmdlet
25
+ def initialize(node, cmdlet, output_format=nil, output_format_options={})
26
+ @output_format = output_format
27
+ @node = node
28
+
29
+ case output_format
30
+ when nil
31
+ @json_format = false
32
+ when :json
33
+ @json_format = true
34
+ when :text
35
+ @json_format = false
36
+ when :object
37
+ @json_format = true
38
+ else
39
+ raise ArgumentError, "Invalid output format #{output_format.to_s} specified"
40
+ end
41
+
42
+ @cmdlet = cmdlet
43
+ @output_format_options = output_format_options
44
+ end
45
+
46
+ attr_reader :output_format
47
+
48
+ def run(switches={}, execution_options={}, *arguments)
49
+ arguments_string = arguments.join(' ')
50
+
51
+ switches_string = command_switches_string(switches)
52
+
53
+ json_depth = 5
54
+
55
+ if @json_format && @output_format_options.has_key?(:depth)
56
+ json_depth = @output_format_options[:depth]
57
+ end
58
+
59
+ json_command = @json_format ? " | convertto-json -compress -depth #{json_depth}" : ""
60
+ command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive -command \"trap [Exception] {write-error -exception ($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} #{arguments_string}#{json_command}\";if ( ! $? ) { exit 1 }"
61
+
62
+ augmented_options = {:returns => [0], :live_stream => false}.merge(execution_options)
63
+ command = Mixlib::ShellOut.new(command_string, augmented_options)
64
+
65
+ os_architecture = "#{ENV['PROCESSOR_ARCHITEW6432']}" == 'AMD64' ? :x86_64 : :i386
66
+
67
+ status = nil
68
+
69
+ with_os_architecture(@node) do
70
+ status = command.run_command
71
+ end
72
+
73
+ CmdletResult.new(status, @output_format)
74
+ end
75
+
76
+ def run!(switches={}, execution_options={}, *arguments)
77
+ result = run(switches, execution_options, arguments)
78
+
79
+ if ! result.succeeded?
80
+ raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{result.stderr}"
81
+ end
82
+
83
+ result
84
+ end
85
+
86
+ protected
87
+
88
+ include Chef::Mixin::WindowsArchitectureHelper
89
+
90
+ def validate_switch_name!(switch_parameter_name)
91
+ if !!(switch_parameter_name.to_s =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
92
+ raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name"
93
+ end
94
+ end
95
+
96
+ def escape_parameter_value(parameter_value)
97
+ parameter_value.gsub(/(`|'|"|#)/,'`\1')
98
+ end
99
+
100
+ def escape_string_parameter_value(parameter_value)
101
+ "'#{escape_parameter_value(parameter_value)}'"
102
+ end
103
+
104
+ def command_switches_string(switches)
105
+ command_switches = switches.map do | switch_name, switch_value |
106
+ if switch_name.class != Symbol
107
+ raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name.to_s}'. The switch must be specified as a Symbol'"
108
+ end
109
+
110
+ validate_switch_name!(switch_name)
111
+
112
+ switch_argument = ''
113
+ switch_present = true
114
+
115
+ case switch_value
116
+ when Numeric
117
+ switch_argument = switch_value.to_s
118
+ when Float
119
+ switch_argument = switch_value.to_s
120
+ when FalseClass
121
+ switch_present = false
122
+ when TrueClass
123
+ when String
124
+ switch_argument = escape_string_parameter_value(switch_value)
125
+ else
126
+ 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`"
127
+ end
128
+
129
+ switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(' ').strip : ''
130
+ end
131
+
132
+ command_switches.join(' ')
133
+ end
134
+ end
135
+ end
136
+