Chef Client NuGet package for Chef Server 12

- Configure using the config.json file
- Cleanup Main.ps1 script to get it to work out of box
- Add ticks to the node name to avoid name collisions when Azure flattens/recreates a node
- Move the last of the stuff over to C:\

git-tfs-id: [http://vstfmsn:8080/tfs/MSN01]$/PubServices/Deployment/InstallationSDK/Main/src/Modules/InstallationSDK.ChefClientInstaller;C1143058
This commit is contained in:
James Prompanya 2014-12-24 06:05:00 +00:00
Родитель 566e44b8ed
Коммит 86146cc6b3
8 изменённых файлов: 866 добавлений и 20 удалений

Просмотреть файл

@ -0,0 +1,283 @@
<#
.SYNOPSIS
Runs the unit tests for the Chef Client Installer
.DESCRIPTION
Builds the Chef Client Installer using the Powershellution Module. Then
executes unit tests for the Chef Client Installer. Functions are
mocked to have no impact on the computer on which you execute the script
and to have no external dependencies.
.INPUTS
<None>
.OUTPUTS
<Test results>
.NOTES
Requires Pester (PowerShell Testing Framework). Download from
https://github.com/pester/Pester
or use Chocolatey (https://chocolatey.org/)
choco install pester
or use PsGet (http://psget.net/)
Install-Module Pester
to install
#>
$ModuleUnderTest = "InstallationSDK.ChefClientInstaller"
$PSProjDepedencies = @(
Join-Path $PSScriptRoot "..\InstallationSDK.AzureServiceRuntime\InstallationSDK.AzureServiceRuntime.psproj"
)
$ModuleDependencies = @()
$ErrorActionPreference = "STOP"
$modulesToCleanup = @($ModuleUnderTest)
Import-Module Pester
Import-Module Powershellution
#region Helper Functions
function WriteVerbose
{
param($msg)
Write-Verbose ("{0}: {1}" -f (Split-Path -Leaf $MyInvocation.ScriptName),$msg)
}
function ImportModuleAndToCleanupList
{
param($module)
Import-Module $module
$script:modulesToCleanup += $module
}
function SafeAddModuleFolderToPSRoot
{
param($moduleFolder)
if (-not $env:PSModulePath.Contains($moduleFolder))
{
$env:PSModulePath += ";$moduleFolder"
}
}
function Cleanup
{
foreach($module in $modulesToCleanup)
{
Remove-Module $module -Force -ErrorAction SilentlyContinue
}
# Eh, sometimes Pester fails with the error that TestDrive already exists.. clear it for them
Remove-Variable -Name TestDrive -Force -Scope Script -ErrorAction SilentlyContinue
}
#endregion
#region Setup
#
# Ensure clean environment
#
WriteVerbose "Remove modules to ensure a clean environment"
Cleanup
#
# Build Dependent psprojs
#
WriteVerbose "Building Project Dependencies"
foreach ($dependantPSproject in $PSProjDepedencies)
{
New-ProjectBuild -Path $dependantPSproject
$moduleName = [IO.Path]::GetFileNameWithoutExtension((Split-Path -Leaf $dependantPSproject))
$moduleFolder = Join-Path (Split-Path $dependantPSproject) "bin"
SafeAddModuleFolderToPSRoot $moduleFolder
ImportModuleAndToCleanupList $moduleName
}
#
# Build Module Under Test
#
WriteVerbose "Building the Module Under Test"
$psprojFile = Resolve-Path (Join-Path $PSScriptRoot "*.psproj")
Write-Verbose "Building psproj: $psprojFile"
New-ProjectBuild -Path $psprojFile
#
# Loading Module-Under-Test
#
WriteVerbose "Loading Module-Under-Test"
$ModuleRoot = Join-Path $PSScriptRoot "bin"
SafeAddModuleFolderToPSRoot $moduleRoot
Import-Module $ModuleUnderTest
#endregion
#
# Ready to run tests!
#
InModuleScope $ModuleUnderTest {
Describe "Get-ChefClientConfig" {
$tempFile = "TestDrive:\test.txt"
It "Should parse and return an object" {
"log_level :info" | Set-Content $tempFile
$config = Get-ChefClientConfig -Path $tempFile
$config.log_level | Should Be ":info"
}
It "Should parse a value with spaces in them and return an object" {
"cache_path 'C:\Some Path With Spaces''" | Set-Content $tempFile
$config = Get-ChefClientConfig -Path $tempFile
$config.cache_path | Should Be "C:\Some Path With Spaces"
}
Context "Given no arguments" {
It "Should return an empty object" {
$config = Get-ChefClientConfig
$members = $config | Get-Member -MemberType NoteProperty | Select-Object -Property Name
$members.Count -gt 0 | Should Be $true
($members | where { $_.Name -eq "node_name" }).Name | Should BeExactly "node_name"
$config.node_name | Should BeNullOrEmpty
}
}
}
Describe "Save-ChefClientConfig" {
$tempFile = "TestDrive:\test.txt"
$newFile = "TestDrive:\new.txt"
$templateFile = "TestDrive:\template.rb"
"cache_path 'C:\chef'`nlog_level :info`ninterval 100" | Set-Content $tempFile
"now = Time.new`n`nlog_location `"c:/chef/client_`" + now.strftime(`"%Y%m%d`") + `".log`"`nlog_level :info`ninterval 999" | Set-Content $templateFile
Get-ChefClientConfig -Path $tempFile | Save-ChefClientConfig -Path $newFile
It "Should save a properly formatted file" {
$newFile | Should Exist
$newFile | Should Contain "cache_path\s+'C:\\chef'"
}
It "Should check that symbols are not quoted" {
$newFile | Should Exist
$newFile | Should Contain "['`"][^:]"
$newFile | Should Contain "[^']:"
}
It "Should check that numbers are not quoted" {
$newFile | Should Exist
$newFile | Should Contain "\s[^'][0-9]"
}
It "Should overwrite when -Overwrite is used" {
# overwrite new file with an empty file, so we can properly check the contents
New-Item $newFile -ItemType File -Force
{ Get-ChefClientConfig -Path $tempFile | Save-ChefClientConfig -Path $newFile } | Should Throw
{ Get-ChefClientConfig -Path $tempFile | Save-ChefClientConfig -Path $newFile -Overwrite } | Should Not Throw
$newFile | Should Exist
$newFile | Should Contain "cache_path\s+'C:\\chef'"
}
It "Should append to an existing file" {
Get-ChefClientConfig -Path $tempFile | Save-ChefClientConfig -Path $templateFile -Append
$templateFile | Should Exist
$templateFile | Should Contain "now = Time\.new"
$templateFile | Should Contain ([regex]::Escape("log_location `"c:/chef/client_`" + now.strftime(`"%Y%m%d`") + `".log`""))
$templateFile | Should Contain "interval\s*100"
}
}
Describe "Get-ChefNodeList" {
Context "Verify Command Line if no client config" {
Mock Invoke-Knife { "knife $args" }
It "Should execute 'knife node list' if there's no client" {
$commandLine = (Get-ChefNodeList).Trim()
$commandLine | Should BeExactly "knife node list"
}
}
Context "Verify Command Line if client config is specified" {
Mock Invoke-Knife { "knife $args" }
$configFile = "TestDrive:\Config.rb"
"chef_server_url 'http://localhost/organizations/msn'" | Set-Content $configFile
$configObject = Get-ChefClientConfig -Path $configFile
It "Should execute 'knife node list -c client.rb' if given the config file path" {
$commandLine = (Get-ChefNodeList -Config $configFile).Trim()
$commandLine | Should Match ("knife node list -c {0}" -f [regex]::Escape($configFile))
}
It "Should execute 'knife node list -c client.rb' if given the config object" {
$commandLine = (Get-ChefNodeList -Config $configObject).Trim()
$commandLine | Should Match "knife node list -c .*"
}
It "Should fail if config does not exist" {
{ Get-ChefNodeList -Config "DoesNotExist" } | Should Throw
}
}
Context "Returns as expected" {
Mock Invoke-Knife { return @("SomeNode_0","SomeNode_1","SomeNode_2") }
$nodes = Get-ChefNodeList
It "Should return a list of nodes" {
$nodes | Should Be @("SomeNode_0","SomeNode_1","SomeNode_2")
}
}
}
Describe "Install-ChefClient" {
Mock Start-Process {param($FilePath, $ArgumentList) "$FilePath $ArgumentList" }
Mock Invoke-SC { "sc.exe $args" }
Mock Set-Path { "$env:path;$args" }
$InstallLocation = "TestDrive:\Opscode"
$RootPath = "TestDrive:\Chef"
$commandLinesToExecute = Install-ChefClient -InstallLocation $InstallLocation -RootPath $RootPath
$msiExecCommand = $commandLinesToExecute[0]
$scFailureCommand = $commandLinesToExecute[1]
$scConfigCommand = $commandLinesToExecute[2]
$path = $commandLinesToExecute[3]
It "(msiexec) should contain everything it needs to install the chef-client properly" {
$msiExecCommand | Should Match "msiexec"
$msiExecCommand | Should Match "\.msi"
$msiExecCommand | Should Match "ChefClientFeature,ChefServiceFeature"
$msiExecCommand | Should Match "InstallLocation"
$msiExecCommand | Should Match "ROOTDRIVE"
}
It "(sc) should set the reset on failure settings properly" {
$scFailureCommand | Should Match "sc.exe"
$scFailureCommand | Should Match "failure"
$scFailureCommand | Should Match "chef-client reset= [0-9]* actions= restart/[0-9]*"
}
It "(sc) should configure the service with the binpath" {
$rubyPath = "$InstallLocation\chef\embedded\bin\ruby.exe"
$servicePath = "$InstallLocation\chef\bin\chef-windows-service"
$scConfigCommand | Should Match "sc.exe"
$scConfigCommand | Should Match "config"
$scConfigCommand | Should Match ("chef-client binpath= `"{0} {1} -c {2} -l {3}`"" -f
[regex]::Escape($rubyPath),
[regex]::Escape($servicePath),
[regex]::Escape("$RootPath\Client.rb"),
[regex]::Escape("$RootPath\client.log"))
}
It "Path environment variable should installation directory" {
$path | Should Match ([Regex]::Escape("$InstallLocation\chef\bin"))
}
}
}
#
# Clean-up
#
WriteVerbose "DONE! Cleaning up"
Cleanup

Просмотреть файл

@ -3,15 +3,6 @@
$ErrorActionPreference = "Stop"
$VerbosePreference = "Continue"
pushd $PSScriptRoot
try
{
$msi = (resolve-path "./resources/*msi").path
}
finally
{
popd
}
function Install-ChefClient
{
@ -29,18 +20,280 @@ function Install-ChefClient
Install-ChefClient -verbose
#>
param(
$InstallLocation = "C:\Opscode",
$RootDrive = $env:SystemDrive,
$RootPath = "C:\Chef",
$ConfigFile = "Client.rb",
$LogFile = "client.log"
)
Process
{
# TODO - change the location of the client root via chef-client configuration so that we don't install on the System Drive.
md d:\chef -ErrorAction SilentlyContinue
copy-item .\*.rb d:\chef
copy-item .\*.pem d:\chef
write-verbose $PSScriptRoot
# chef repository
md $RootPath -ErrorAction SilentlyContinue | Out-Null
$pathToConfigFile = Join-Path $RootPath $ConfigFile
$pathToLogFile = Join-Path $RootPath $LogFile
$msi = (Resolve-Path (Join-Path $PSScriptRoot "\resources\*.msi")).Path
write-verbose "Installing chef client from $msi."
start-process msiexec -ArgumentList "/qn /i $msi ADDLOCAL=`"ChefClientFeature,ChefServiceFeature`" /log $($msi).log" -Wait
sc.exe failure "chef-client" reset= 86400 actions= restart/5000
start-service chef-client
start-process msiexec -ArgumentList "/qn /i $msi ADDLOCAL=`"ChefClientFeature,ChefServiceFeature`" InstallLocation=`"$InstallLocation`" ROOTDRIVE=`"$RootDrive\`" /L $($msi).log" -Wait
Invoke-SC failure "chef-client" reset= 86400 actions= restart/5000
$rubyPath = Join-Path $InstallLocation "\chef\embedded\bin\ruby.exe"
$servicePath = Join-Path $InstallLocation "\chef\bin\chef-windows-service"
# Add the --Config and --LogFile paramters to the binpath of the service
Invoke-SC config "chef-client" binpath= "`"$rubyPath $servicePath -c $pathToConfigFile -l $pathToLogFile`""
# For running knife commands in the same process as the installer. The installer sets the path variable but in a different context, so we can't run knife otherwise.
Set-Path (Join-Path $InstallLocation "chef\bin")
}
}
Export-ModuleMember Install-ChefClient
function Get-ChefClientConfig
{
<#
.SYNOPSIS
Loads the Chef Client Config into memory
.DESCRIPTION
Loads the Chef Client Config for itempotency and easy editing of config values
.EXAMPLE
Get-ChefClientConfig -Path .\Client.rb
.EXAMPLE
Get-ChefClientConfig
#>
param(
$Path = $null
)
Process
{
$InitialProperties = @{
"log_level" = "";
"log_location" = "";
"cache_path" = "";
"client_key" = "";
"node_name" = "";
"chef_server_url" = "";
"validation_client_name" = "";
"validation_key" = "";
"interval" = "";
"json_attribs" = "";
"ssl_verify_mode" = ""}
if ($Path)
{
# Regex matches simple "somekey value" pattern
# Parses out custom Ruby (like now = Time.new)
# Use -Template in Save-ChefClientConfig to get back custom ruby script
# (Side note, I don't really like this implemention. Probably needs to be replaced with something more robust)
Get-Content -Path $Path | foreach {
if (-not ($_ -match "^[a-zA-Z0-9_]*\s*[:'`"0-9].*['`"]?" ))
{
# Ran into this scenario before
Write-Warning "The configuration might contain some unsupported values for this cmdlet. When piped into the Save-ChefClientConfig function, ensure the values are properly set"
}
$tokens = $_.Split(@(" ", "`t"), "RemoveEmptyEntries")
$key = $tokens[0]
$value = ($tokens | Select-Object -Last ($tokens.Count - 1)) -join " "
$InitialProperties.$key = $value.Trim("'", '"');
}
}
$InitialProperties.Add("AdditionalProperties", @{})
New-Object -TypeName PSObject -Property $InitialProperties
}
}
function Save-ChefClientConfig
{
<#
.SYNOPSIS
Save the Chef Client Config object to a file
.DESCRIPTION
Takes a dictionary or PSObject and writes a Client.rb file for chef to use
.EXAMPLE
Save-ChefClientConfig -Path .\Client.rb
.EXAMPLE
Save-ChefClientConfig -Path .\Client.rb -Append
.EXAMPLE
Save-ChefClientConfig -Path .\Client.rb -Overwrite
.EXAMPLE
Save-ChefClientConfig
#>
[CmdLetBinding(DefaultParameterSetName="Append")]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
$InputObject,
[Parameter(Mandatory=$true)]
$Path = $null,
$Template = $null,
[Parameter(ParameterSetName="Append")]
[switch]$Append = $false,
[Parameter(ParameterSetName="Overwrite")]
[switch]$Overwrite = $false
)
Process
{
if (Test-Path $Path)
{
if ($Overwrite)
{
Remove-Item $Path -Force
}
elseif (-not $Append)
{
throw "Cannot save chef Client.rb to '$Path' because the file already exists. Use -Overwrite to overwrite or -Append to add"
}
}
$hashToWrite = @{}
if ($InputObject -is [HashTable])
{
$hashToWrite = $InputObject
}
elseif ($InputObject -is [PSObject])
{
$InputObject | Get-Member -MemberType NoteProperty | where { $_.Name -ne "AdditionalProperties" } | foreach {
$hashToWrite.$($_.Name) = $InputObject.$($_.Name)
}
# Overwrite with anything in the AdditionalProperties property
$InputObject.AdditionalProperties.Keys | foreach {
$hashToWrite.$_ = $InputObject.AdditionalProperties.$_
}
}
$contents = @()
if ($Append)
{
$contents = Get-Content -Path $Path
}
# TODO: Could use some optimization
$hashToWrite.Keys | where { $hashToWrite[$_] } | foreach {
$key = $_
$value = $hashToWrite[$key]
# symbols and numbers should not be surrounded in quotes
if (-not $value.StartsWith(":") -and -not ($value -match "^[0-9.]*$"))
{
$value = "'$value'"
}
$newLineToAdd = "{0} {1}" -f $key,$value
# Check if the line already exists in the file (i.e. if we're just appending), if so replace that line, rather than add a new line
# TODO: More optimization. -Match and -Replace could do two iterations
if ($contents -match "^$key\s")
{
$contents = $contents -replace "^$key\s.*$",$newLineToAdd
}
else
{
$contents += $newLineToAdd
}
}
$contents -join "`n" | Set-Content $Path
}
}
function Get-ChefNodeList
{
<#
.SYNOPSIS
Gets the list of nodes from the chef server
.DESCRIPTION
Uses knife to connect to the server and retrieve the node list
.EXAMPLE
Get-ChefNodeList -Config .\Client.rb
.EXAMPLE
Get-ChefNodeList -Config $configObject
.EXAMPLE
Get-ChefNodeList
#>
param(
$Config
)
$TemporaryConfigFile = $null
$ConfigArgument = $null
try
{
if ($Config)
{
if ($Config -is [PSObject])
{
$TemporaryConfigFile = [IO.Path]::GetRandomFileName()
Save-ChefClientConfig -InputObject $Config -Path $TemporaryConfigFile
$Config = $TemporaryConfigFile
}
elseif (-not (Test-Path $Config))
{
throw "$config is not a valid config object or path to a config file"
}
$ConfigArgument = "-c"
}
Invoke-Knife node list $ConfigArgument $Config
}
finally
{
if ($TemporaryConfigFile)
{
Remove-Item -Path $TemporaryConfigFile -Force -ErrorAction SilentlyContinue | Out-Null
}
}
}
# Wrap cmd exe files to make things mockable. Can't use Start-Process because I want the output (Start-Process would write to a file first with -RedirectStandardOutput)
function Invoke-Knife
{
knife $args
}
function Invoke-SC
{
sc.exe $args
}
# Same with env:path
function Set-Path
{
param($newPath)
if (-not $env:Path.Contains($newPath))
{
$env:Path += ";$newPath;"
}
}
Export-ModuleMember Install-ChefClient
Export-ModuleMember Get-ChefClientConfig
Export-ModuleMember Save-ChefClientConfig
Export-ModuleMember Get-ChefNodeList

Просмотреть файл

@ -10,10 +10,10 @@
"Description": "PowerShell Cmdlets for installing Chef-Client.",
"Copyright": "(c) 2014 Microsoft. All rights reserved.",
"PowerShellVersion": "3.0",
"RequiredModules": []
"RequiredModules": ["InstallationSDK.AzureServiceRuntime"]
},
"Files": {
"RootModule": "InstallationSDK.ChefClientInstaller.psm1",
"Content": [ "./resources/*.msi"]
"Content": [ "./resources/*.*", "./code/*.*", "./script/*.*" ]
}
}

92
code/ClientService.cs Normal file
Просмотреть файл

@ -0,0 +1,92 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ClientService.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft. All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Microsoft.OnlinePublishing.Chef
{
using System;
using System.Diagnostics;
using System.ServiceProcess;
/// <summary>
/// The Chef.ClientService Start and Stop methods should be called in the Roles OnStart and OnStop methods respectively.
/// </summary>
public static class ClientService
{
/// <summary>
/// Stop the Chef Client windows service with the default wait time of 1 minute.
/// </summary>
public static void Stop()
{
Stop(new TimeSpan(0, 1, 0));
}
/// <summary>
/// Stop the Chef Client windows service.
/// </summary>
/// <param name="timeToWait">Wait time for operation to complete.</param>
public static void Stop(TimeSpan timeToWait)
{
try
{
// Stop Chef Client
Trace.TraceInformation("Chef Client - attempting to stop the Chef Client windows service.");
using (var chefService = new ServiceController("chef-client"))
{
if (chefService != null && chefService.Status != ServiceControllerStatus.Stopped)
{
chefService.Stop();
chefService.WaitForStatus(ServiceControllerStatus.Stopped, timeToWait);
Trace.TraceInformation("Chef Client - Chef Client windows service Stopped.");
}
else
{
Trace.TraceInformation("Chef Client - Chef Client windows service is not running.");
}
}
}
catch (System.ServiceProcess.TimeoutException)
{
Trace.TraceInformation("Chef Client - failed to stop Chef Client in time alloted [{0}].", timeToWait);
}
catch (InvalidOperationException e)
{
Trace.TraceInformation("Chef Client - Invalid Operation, is the role running with elevated privledges. Ex:{0}.", e.ToString());
}
}
/// <summary>
/// Start Chef Client windows service.
/// </summary>
public static void Start()
{
try
{
// Start Chef Client - wait 30 seconds
Trace.TraceInformation("Chef Client - Attempting to start Chef-Client.");
using (var chefService = new ServiceController("chef-client"))
{
if (chefService != null && chefService.Status != ServiceControllerStatus.Running)
{
chefService.Start();
chefService.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 30));
Trace.TraceInformation("Chef Client - Chef-Client Started.");
}
else
{
Trace.TraceInformation("Chef Client - Chef-Client previously running.");
}
}
}
catch (System.ServiceProcess.TimeoutException)
{
Trace.TraceInformation("Chef Client - failed to start Chef Client within time range.");
}
catch (InvalidOperationException e)
{
Trace.TraceInformation("Chef Client - Invalid Operation, is the role running with elevated privledges. Ex:{0}.", e.ToString());
}
}
}
}

Двоичный файл не отображается.

204
script/Main.ps1 Normal file
Просмотреть файл

@ -0,0 +1,204 @@
param(
[String]$ClientRb = "Client.rb",
[string]$ClientPem = "client.pem",
[string]$RootPath = "C:\chef\"
)
$ErrorActionPreference = "Stop"
Write-Output "Script:main.ps1 starting"
$pathToClientPem = Join-path -Path $RootPath -ChildPath $ClientPem
$pathToClientRb = Join-path -Path $RootPath -ChildPath $ClientRb
if (Test-Path $PathToClientPem)
{
Write-Output "Script:main.ps1 $pathToClientPem already exists. Nothing to do. Exiting"
return
}
# Add approot module path
$modulePath = resolve-Path -Path $PSScriptRoot\Modules
if(-not $env:PSModulePath.Contains($modulePath.Path))
{
$env:PSModulePath += (";" + $modulePath.Path)
Write-Output "PSModulePath: $env:PSModulePath"
}
# Insert your custom logic below...
# Import modules
Import-Module InstallationSDK.ChefClientInstaller
Import-Module InstallationSDK.AzureServiceRuntime
# Go ahead and install, we'll need the knife tool later
# Override the rootdrive, for Azure
Install-ChefClient -RootDrive "C:" -RootPath $RootPath
$ClientRbObject = $null
# Get template Client.rb file, if it exists
$TemplateClientRb = Join-Path $PSScriptRoot $ClientRb
$ClientRbObject = Get-ChefClientConfig
# Set node name. Format: [cloud service name]_IN_[Azure instance number]
Get-CloudServiceRoleInstance -ErrorAction Continue | out-Null
$roleInstance = Get-CloudServiceRoleInstance
$config = ConvertFrom-Json ((Get-Content $PSScriptRoot\config.json -ErrorAction SilentlyContinue) -join "`n")
$name = $roleInstance.DeploymentID
$roleName = $roleInstance.Id.Substring(0, $roleInstance.Id.IndexOf("_"))
if ($config -and $config.name)
{
$name = $config.name
}
# Add Instance number to the node name
Write-Output "Role Instance Name: $($roleInstance.Id)"
$nodeName = $($roleInstance.Id).Replace($roleName, $name)
# Set ssl_verify_mode on Client.rb. (As of Chef 12, it defaults to :verify_peer).
# For Non-Production ONLY: Unless a valid certificate is set, need to use :verify_none
if ($config -and $config.sslVerifyMode)
{
$sslVerifyMode = $config.sslVerifyMode
$ClientRbObject.ssl_verify_mode = $sslVerifyMode
Write-Output "Set ssl_verify_mode to: $sslVerifyMode"
if ($sslVerifyMode -eq ":verify_none")
{
Write-Warning "`"ssl_verify_mode: '$sslVerifyMode'`" should only be used for testing purposes only!"
}
}
# Try to get server_url from Cloud Service CsCfg first. If not, check the config.json
# Value from Cloud Service CsCfg always wins.
$url = Get-CloudServiceConfigurationSettingValue "ChefClient_ServerUrl"
if (-not $url -and ($config -and $config.serverUrl))
{
$url = $config.serverUrl
}
if ($url)
{
$ClientRbObject.chef_server_url = $url
Write-Output "Set chef url to: $url"
#
# If we're connecting to the server. Client Name and Validation Key needs to be set
#
# Validation client name is now on a per-org basis
if ($config -and $config.ValidationClientName)
{
$validationClientName = $config.ValidationClientName
$ClientRbObject.validation_client_name = $validationClientName
Write-Output "Set Validation Client Name to '$validationClientName'"
# Temporarily set node name to validation client name. This is so we can call node list and determine what names are available
$ClientRbObject.node_name = $validationClientName
}
else
{
throw "Validation client name must be set if serverUrl is defined"
}
# Try to get validationKey from Cloud Service CsCfg first. If not, check the config.json
# Value from Cloud Service CsCfg always wins.
$validationKey = Get-CloudServiceConfigurationSettingValue "ChefClient_ValidationKey"
if (-not $validationKey -and ($config -and $config.validationKey))
{
$validationKey = $config.validationKey
}
if ($validationKey)
{
# Ensure the key exists with that filename
$validationKeyTemp = Join-Path $PSScriptRoot $validationKey
if (-not (Test-Path $validationKeyTemp))
{
throw "Did not find validation key at path $(Join-Path $PSScriptRoot $validationKey)"
}
$pathToValidationKey = Join-Path $RootPath $validationKey
Copy-Item $validationKeyTemp $pathToValidationKey -Force
$ClientRBObject.validation_key = $pathToValidationKey
Write-Output "Set validation key to '$pathToValidationKey'"
# Temporarily set node name to validation client name. This is so we can call node list and determine what names are available
$ClientRbObject.client_key = $pathToValidationKey
}
else
{
throw "Validation key must be set if serverUrl is defined"
}
#
# Check if the node's name is available
#
$tempConfigFile = $null
try
{
$tempConfigFile = [IO.Path]::GetRandomFileName()
Copy-Item -Path $TemplateClientRb -Destination $tempConfigFile
Save-ChefClientConfig -InputObject $ClientRbObject -Path $tempConfigFile -Append
$nodes = Get-ChefNodeList -Config $tempConfigFile
}
finally
{
if ($tempConfigFile)
{
Remove-Item -Path $tempConfigFile -ErrorAction SilentlyContinue
}
}
# increment the node name until one exists that doesn't conflict
$baseName = $nodeName
for($i = 1; $nodes.Contains($nodeName) ;$i++)
{
$nodeName = "$baseName{0}" -f ".$i"
}
}
else
{
Write-Output "chef url not set in configuration file. Node will not register with Chef Server."
}
# Alter client.rb with new node name
$ClientRbObject.node_name = $nodeName
Write-Output "Set chef node_name to: $nodeName"
$ClientRbObject.client_key = $pathToClientPem
Write-Output "Set chef client_key to: $pathToClientRb"
if ($config -and $config.pollInterval)
{
$interval = $config.pollInterval
$ClientRbObject.interval = $interval
Write-Output "Set poll interval to: $interval seconds"
}
else
{
Write-Output "Poll Interval not set. Default value (if not set) is 1800s (30m)."
}
# Create first-run-bootstrap.json to register new node with Chef Server
if ($config -and $config.role)
{
# Register with the correct update domain role [role name]
$chefRole = $($config.role)
$bootStrapperFile = "first-run-bootstrap.json"
$bootStrapper = "{`r`n `"run_list`": [ `"role[$chefRole]`" ]`r`n}"
Write-Output "Setting bootstrap content: $bootStrapper"
$pathToBootStrapper = Join-Path $RootPath $bootStrapperFile
$bootStrapper | Out-File $pathToBootStrapper -Encoding ascii
$ClientRbObject.json_attribs = $pathToBootStrapper
Write-Output "Set bootstrapper path to: '$pathToBootStrapper'"
}
Copy-Item -Path $TemplateClientRb -Destination $pathToClientRb -Force
$ClientRbObject | Save-ChefClientConfig -Path $pathToClientRb -Append
start-service chef-client
Write-Output "Script:main.ps1 exiting"

5
script/client.rb Normal file
Просмотреть файл

@ -0,0 +1,5 @@
now = Time.new
log_level :info
log_location 'c:/chef/client_' + now.strftime("%Y%m%d") + '.log'
cache_path 'c:\chef'
client_key 'c:\chef\client.pem'

9
script/config.json Normal file
Просмотреть файл

@ -0,0 +1,9 @@
{
"name": "chefClient",
"role": "",
"pollInterval": "",
"serverUrl": "",
"sslVerifyMode": ":verify_none",
"validationClientName": "",
"validationKey": ""
}