class Chef::Knife::Core::WindowsBootstrapContext
Instances of BootstrapContext
are the context objects (i.e., self
) for bootstrap templates. For backwards compatibility, they must
set the following instance variables:
-
@config - a hash of knife's config values
-
@run_list - the run list for the node to bootstrap
Attributes
chef_config[RW]
config[RW]
secret[RW]
Public Class Methods
new(config, run_list, chef_config, secret = nil)
click to toggle source
Calls superclass method
Chef::Knife::Core::BootstrapContext::new
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 37 def initialize(config, run_list, chef_config, secret = nil) @config = config @run_list = run_list @chef_config = chef_config @secret = secret super(config, run_list, chef_config, secret) end
Public Instance Methods
bootstrap_directory()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 289 def bootstrap_directory ChefConfig::Config.etc_chef_dir(windows: true) end
clean_etc_chef_file(path)
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 281 def clean_etc_chef_file(path) ChefConfig::PathHelper.cleanpath(etc_chef_file(path), windows: true) end
config_content()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 61 def config_content # The windows: true / windows: false in the block that follows is more than a bit weird. The way to read this is that we need # the e.g. var_chef_dir to be rendered for the windows value ("C:\chef"), but then we are rendering into a file to be read by # ruby, so we don't actually care about forward-vs-backslashes and by rendering into unix we avoid having to deal with the # double-backwhacking of everything. So we expect to see: # # file_cache_path "C:/chef" # # Which is mildly odd, but should be entirely correct as far as ruby cares. # client_rb = <<~CONFIG chef_server_url "#{chef_config[:chef_server_url]}" validation_client_name "#{chef_config[:validation_client_name]}" file_cache_path "#{ChefConfig::PathHelper.escapepath(chef_config[:windows_bootstrap_file_cache_path] || "")}" file_backup_path "#{ChefConfig::PathHelper.escapepath(chef_config[:windows_bootstrap_file_backup_path] || "")}" cache_options ({:path => "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.etc_chef_dir(windows: true))}\\\\cache\\\\checksums", :skip_expires => true}) CONFIG unless chef_config[:chef_license].nil? client_rb << "chef_license \"#{chef_config[:chef_license]}\"\n" end if config[:chef_node_name] client_rb << %Q{node_name "#{config[:chef_node_name]}"\n} else client_rb << "# Using default node name (fqdn)\n" end if chef_config[:config_log_level] client_rb << %Q{log_level :#{chef_config[:config_log_level]}\n} else client_rb << "log_level :auto\n" end client_rb << "log_location #{get_log_location}" # We configure :verify_api_cert only when it's overridden on the CLI # or when specified in the knife config. if !config[:node_verify_api_cert].nil? || config.key?(:verify_api_cert) value = config[:node_verify_api_cert].nil? ? config[:verify_api_cert] : config[:node_verify_api_cert] client_rb << %Q{verify_api_cert #{value}\n} end # We configure :ssl_verify_mode only when it's overridden on the CLI # or when specified in the knife config. if config[:node_ssl_verify_mode] || config.key?(:ssl_verify_mode) value = case config[:node_ssl_verify_mode] when "peer" :verify_peer when "none" :verify_none when nil config[:ssl_verify_mode] else nil end if value client_rb << %Q{ssl_verify_mode :#{value}\n} end end if config[:ssl_verify_mode] client_rb << %Q{ssl_verify_mode :#{config[:ssl_verify_mode]}\n} end if config[:bootstrap_proxy] client_rb << "\n" client_rb << %Q{http_proxy "#{config[:bootstrap_proxy]}"\n} client_rb << %Q{https_proxy "#{config[:bootstrap_proxy]}"\n} client_rb << %Q{no_proxy "#{config[:bootstrap_no_proxy]}"\n} if config[:bootstrap_no_proxy] end if config[:bootstrap_no_proxy] client_rb << %Q{no_proxy "#{config[:bootstrap_no_proxy]}"\n} end if secret client_rb << %Q{encrypted_data_bag_secret "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.etc_chef_dir(windows: true))}\\\\encrypted_data_bag_secret"\n} end unless trusted_certs_script.empty? client_rb << %Q{trusted_certs_dir "#{ChefConfig::PathHelper.escapepath(ChefConfig::Config.etc_chef_dir(windows: true))}\\\\trusted_certs"\n} end if chef_config[:fips] client_rb << "fips true\n" end escape_and_echo(client_rb) end
encrypted_data_bag_secret()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 53 def encrypted_data_bag_secret escape_and_echo(@secret) end
escape_and_echo(file_contents)
click to toggle source
escape WIN BATCH special chars and prefixes each line with an echo
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 318 def escape_and_echo(file_contents) file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1') end
etc_chef_file(path)
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 285 def etc_chef_file(path) "#{bootstrap_directory}/#{path}" end
first_boot()
click to toggle source
Calls superclass method
Chef::Knife::Core::BootstrapContext#first_boot
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 311 def first_boot escape_and_echo(super.to_json) end
get_log_location()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 153 def get_log_location if chef_config[:config_log_location].equal?(:win_evt) %Q{:#{chef_config[:config_log_location]}\n} elsif chef_config[:config_log_location].equal?(:syslog) raise "syslog is not supported for log_location on Windows OS\n" elsif chef_config[:config_log_location].equal?(STDOUT) "STDOUT\n" elsif chef_config[:config_log_location].equal?(STDERR) "STDERR\n" elsif chef_config[:config_log_location].nil? || chef_config[:config_log_location].empty? "STDOUT\n" elsif chef_config[:config_log_location] %Q{"#{chef_config[:config_log_location]}"\n} else "STDOUT\n" end end
install_chef()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 275 def install_chef # The normal install command uses regular double quotes in # the install command, so request such a string from install_command install_command('"') + "\n" + fallback_install_task_command end
local_download_path()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 293 def local_download_path "%TEMP%\\#{ChefUtils::Dist::Infra::CLIENT}-latest.msi" end
msi_url(machine_os = nil, machine_arch = nil, download_context = nil)
click to toggle source
Build a URL that will redirect to the correct Chef
Infra msi download.
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 298 def msi_url(machine_os = nil, machine_arch = nil, download_context = nil) if config[:msi_url].nil? || config[:msi_url].empty? url = "https://omnitruck.chef.io/chef/download?p=windows" url += "&pv=#{machine_os}" unless machine_os.nil? url += "&m=#{machine_arch}" unless machine_arch.nil? url += "&DownloadContext=#{download_context}" unless download_context.nil? url += "&channel=#{config[:channel]}" url += "&v=#{version_to_install}" else config[:msi_url] end end
start_chef()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 171 def start_chef c_opscode_dir = ChefConfig::PathHelper.cleanpath(ChefConfig::Config.c_opscode_dir, windows: true) client_rb = clean_etc_chef_file("client.rb") first_boot = clean_etc_chef_file("first-boot.json") bootstrap_environment_option = bootstrap_environment.nil? ? "" : " -E #{bootstrap_environment}" start_chef = "SET \"PATH=%SYSTEM32%;%SystemRoot%;%SYSTEM32%\\Wbem;%SYSTEM32%\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;#{c_opscode_dir}\\bin;#{c_opscode_dir}\\embedded\\bin\;%PATH%\"\n" start_chef << "#{ChefUtils::Dist::Infra::CLIENT} -c #{client_rb} -j #{first_boot}#{bootstrap_environment_option}\n" end
trusted_certs_script()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 57 def trusted_certs_script @trusted_certs_script ||= trusted_certs_content end
validation_key()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 45 def validation_key if File.exist?(File.expand_path(chef_config[:validation_key])) IO.read(File.expand_path(chef_config[:validation_key])) else false end end
win_wget()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 182 def win_wget # I tried my best to figure out how to properly url decode and switch / to \ # but this is VBScript - so I don't really care that badly. win_wget = <<~WGET url = WScript.Arguments.Named("url") path = WScript.Arguments.Named("path") proxy = null '* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all '* / into \. Also assume that file:/// is a local absolute path and that file://<foo> '* is possibly a network file path. If InStr(url, "file://") = 1 Then url = Unescape(url) If InStr(url, "file:///") = 1 Then sourcePath = Mid(url, Len("file:///") + 1) Else sourcePath = Mid(url, Len("file:") + 1) End If sourcePath = Replace(sourcePath, "/", "\\") Set objFSO = CreateObject("Scripting.FileSystemObject") If objFSO.Fileexists(path) Then objFSO.DeleteFile path objFSO.CopyFile sourcePath, path, true Set objFSO = Nothing Else Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP") Set wshShell = CreateObject( "WScript.Shell" ) Set objUserVariables = wshShell.Environment("USER") rem http proxy is optional rem attempt to read from HTTP_PROXY env var first On Error Resume Next If NOT (objUserVariables("HTTP_PROXY") = "") Then proxy = objUserVariables("HTTP_PROXY") rem fall back to named arg ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then proxy = WScript.Arguments.Named("proxy") End If If NOT isNull(proxy) Then rem setProxy method is only available on ServerXMLHTTP 6.0+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0") objXMLHTTP.setProxy 2, proxy End If On Error Goto 0 objXMLHTTP.open "GET", url, false objXMLHTTP.send() If objXMLHTTP.Status = 200 Then Set objADOStream = CreateObject("ADODB.Stream") objADOStream.Open objADOStream.Type = 1 objADOStream.Write objXMLHTTP.ResponseBody objADOStream.Position = 0 Set objFSO = Createobject("Scripting.FileSystemObject") If objFSO.Fileexists(path) Then objFSO.DeleteFile path Set objFSO = Nothing objADOStream.SaveToFile path objADOStream.Close Set objADOStream = Nothing End If Set objXMLHTTP = Nothing End If WGET escape_and_echo(win_wget) end
win_wget_ps()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 252 def win_wget_ps win_wget_ps = <<~WGET_PS param( [String] $remoteUrl, [String] $localPath ) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $ProxyUrl = $env:http_proxy; $webClient = new-object System.Net.WebClient; if ($ProxyUrl -ne '') { $WebProxy = New-Object System.Net.WebProxy($ProxyUrl,$true) $WebClient.Proxy = $WebProxy } $webClient.DownloadFile($remoteUrl, $localPath); WGET_PS escape_and_echo(win_wget_ps) end
Private Instance Methods
client_d_content()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 341 def client_d_content content = "" if chef_config[:client_d_dir] && File.exist?(chef_config[:client_d_dir]) root = Pathname(chef_config[:client_d_dir]) root.find do |f| relative = f.relative_path_from(root) if f != root file_on_node = "#{bootstrap_directory}/client.d/#{relative}".tr("/", "\\") if f.directory? content << "mkdir #{file_on_node}\n" else content << "> #{file_on_node} (\n" + escape_and_echo(IO.read(File.expand_path(f))) + "\n)\n" end end end end content end
fallback_install_task_command()
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 361 def fallback_install_task_command # This command will be executed by schtasks.exe in the batch # code below. To handle tasks that contain arguments that # need to be double quoted, schtasks allows the use of single # quotes that will later be converted to double quotes command = install_command("'") <<~EOH @set MSIERRORCODE=!ERRORLEVEL! @if ERRORLEVEL 1 ( @echo WARNING: Failed to install #{ChefUtils::Dist::Infra::PRODUCT} MSI package in remote context with status code !MSIERRORCODE!. @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614 @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log" @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION! @echo WARNING: Retrying installation with local context... @schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\" @if ERRORLEVEL 1 ( @echo ERROR: Failed to create #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code !ERRORLEVEL! > "&2" ) else ( @echo Successfully created scheduled task to install #{ChefUtils::Dist::Infra::PRODUCT}. @schtasks /run /tn chefclientbootstraptask @if ERRORLEVEL 1 ( @echo ERROR: Failed to execute #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task with status code !ERRORLEVEL!. > "&2" ) else ( @echo Successfully started #{ChefUtils::Dist::Infra::PRODUCT} installation scheduled task. @echo Waiting for installation to complete -- this may take a few minutes... waitfor chefclientinstalldone /t 600 if ERRORLEVEL 1 ( @echo ERROR: Timed out waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install ) else ( @echo Finished waiting for #{ChefUtils::Dist::Infra::PRODUCT} package to install. ) @schtasks /delete /f /tn chefclientbootstraptask > NUL ) ) ) else ( @echo Successfully installed #{ChefUtils::Dist::Infra::PRODUCT} package. ) EOH end
install_command(executor_quote)
click to toggle source
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 324 def install_command(executor_quote) "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}" end
trusted_certs_content()
click to toggle source
Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped This string should contain both the commands necessary to both create the files, as well as their content
# File lib/chef/knife/core/windows_bootstrap_context.rb, line 330 def trusted_certs_content content = "" if chef_config[:trusted_certs_dir] Dir.glob(File.join(ChefConfig::PathHelper.escape_glob_dir(chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert| content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" + escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n" end end content end