private/bootstrap.ps1
Function UpdateRemotelyNodeMap { [CmdletBinding()] param( [Parameter(Mandatory)] [HashTable]$ModuleStatus, [Parameter(Mandatory)] [bool]$PathStatus, [Parameter(Mandatory)] [String]$NodeName ) $nodeName = $NodeName.Replace('[','') $nodeName = $nodeName.Replace(']','') $nodeExists = $PSRemotely.NodeMap | Where-Object -Property NodeName -eq $NodeName if ($nodeExists) { $nodeExists.PathStatus = $PathStatus $nodeExists.ModuleStatus = $ModuleStatus } else { $PSRemotely.NodeMap += @{ NodeName = $NodeName; PathStatus = $PathStatus; ModuleStatus = $ModuleStatus; } } } Function TestRemotelyNodeBootStrapped { [CmdletBinding(DefaultParameterSetName='SessionInfo')] param( [Parameter(Mandatory, ParameterSetName='SessionInfo')] $SessionInfo, [Parameter(Mandatory, ParameterSetName='ComputerName')] [String]$ComputerName ) if ($PSRemotely.NodeMap.Count -gt 0) { Switch -Exact ($PSCmdlet.ParameterSetName) { 'SessionInfo' { $node = $PSRemotely.NodeMap | Where -Property NodeName -eq $($SessionInfo.Session.ComputerName) break } 'ComputerName' { $node = $PSRemotely.NodeMap | Where -Property NodeName -eq $ComputerName break } } if($node) { if($node.pathStatus -and $($node.moduleStatus.Values -notcontains $False)) { $True } else { $false # node is either missing the path or module bootstrapping } } else { $false } } else { $False } } Function BootstrapRemotelyNode { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.ModuleSpecification[]]$FullyQualifiedName, [Parameter()] [String]$PSRemotelyNodePath ) # at the beginning read the status of the node $ModuleStatus, $pathStatus = TestRemotelyNode @PSBoundParameters # Add the above information to the PSRemotely var UpdateRemotelyNodeMap -ModuleStatus $ModuleStatus -PathStatus $PathStatus -NodeName $session.ComputerName if ($pathStatus -and $($moduleStatus.Values -notcontains $False)) { # Node is already bootstrapped, no need to take action # archive the existing tests files on the PSRemotely node Write-VerboseLog -Message "Cleaning up $PSRemotelyNodePath on Node -> $($Session.ComputerName)" CleanupPSRemotelyNodePath -Session $session -PSRemotelyNodePath $PSRemotelyNodePath } else { # Node is missing some of the configs, bootstrap it foreach ($hash in $moduleStatus.GetEnumerator()) { if ($hash.Value) { # module present on the remote node } else { # module not present on the remote node CopyRemotelyNodeModule -Session $session -FullyQualifiedName $($FullyQualifiedName | Where -Property Name -EQ $hash.Name) } } if ($pathStatus) { # PSRemotely node path created # archive the existing tests files on the PSRemotely node Write-VerboseLog -Message "Cleaning up $PSRemotelyNodePath on Node -> $($Session.ComputerName)" CleanupPSRemotelyNodePath -Session $session -PSRemotelyNodePath $PSRemotelyNodePath } else { CreatePSRemotelyNodePath -session $session -Path $PSRemotelyNodePath } # at the end update the status of the node $ModuleStatus, $pathStatus = TestRemotelyNode @PSBoundParameters # Add the above information to the PSRemotely var UpdateRemotelyNodeMap -ModuleStatus $ModuleStatus -PathStatus $PathStatus -NodeName $session.ComputerName } } Function TestRemotelyNode { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.ModuleSpecification[]]$FullyQualifiedName, [Parameter()] [String]$PSRemotelyNodePath ) Invoke-Command -Session $session -ArgumentList $FullyQualifiedName,$PSRemotelyNodePath -ScriptBlock { param( $FullyQualifiedName, $PSRemotelyNodePath ) $outputHash = @{} $FullyQualifiedName | Foreach-Object -Process {$outputHash.Add($PSItem.Name, $false) } if (Test-Path -path "$PSRemotelyNodePath\Lib\Artifacts" -PathType Container) { foreach($module in $FullyQualifiedName) { $moduleName=$module.Name $moduleVersion=$module.version if(Test-Path -Path "$PSRemotelyNodePath\lib\$moduleName\$moduleVersion") { # module present $outputHash[$($module.Name)] = $true } else { $outputHash[$($module.Name)] = $false } } $outputHash,$true } else { $outputHash, $false } } } Function CreatePSRemotelyNodePath { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, [Parameter()] [String]$Path ) Invoke-Command -Session $session -ScriptBlock { $null = New-Item -Path "$using:Path\lib\Artifacts" -ItemType Directory -Force } } Function TestModulePresentInLib { [CmdletBinding()] param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.ModuleSpecification]$FullyQualifiedName ) $LibPath = Resolve-Path -Path $PSScriptRoot\..\lib | Select-Object -ExpandProperty Path $ModulePath = '{0}\{1}' -f $FullyQualifiedName.Name, $FullyQualifiedName.Version Test-Path -Path "$libPath\$modulePath" -ErrorAction Stop } Function CopyRemotelyNodeModule { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.ModuleSpecification]$FullyQualifiedName) # copy the modules from the lib directory $moduleName = $FullyQualifiedName.Name $moduleVersion = $FullyQualifiedName.Version # now copy the module to the remote node TRY { if (testModulePresentInLib -FullyQualifiedName $FullyQualifiedName) { $LibPath = Resolve-Path -Path $PSScriptRoot\..\lib | Select-Object -ExpandProperty Path CopyModuleFolderToRemotelyNode -path "$LibPath\$moduleName\$moduleVersion" -Destination "$($PSRemotely.PSRemotelyNodePath)\lib\$moduleName" -session $session -ErrorAction Stop } else { throw "Lib folder does not have a folder named $($moduleName)\$($moduleVersion), so it can't be copied to the PSRemotely node." } } CATCH { $PSCmdlet.ThrowTerminatingError($PSitem) } } Function CopyModuleFolderToRemotelyNode { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, # Source folder to copy. [Parameter(Mandatory)] [String]$Path, # Destination where the copy is created. [Parameter(Mandatory)] [String]$Destination ) Invoke-Command -Session $Session -ScriptBlock {$null = New-Item -Path $Using:Destination -ItemType Directory} -ErrorAction SilentlyContinue #$folderName = Split-Path -Path $Path -Leaf Copy-Item -Path "$Path" -Destination $Destination -Recurse -ToSession $session -Force } Function CopyTestsFileToRemotelyNode { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, [Parameter(Mandatory)] [String]$TestName, [Parameter(Mandatory)] [String]$TestBlock, [Parameter(Mandatory=$False)] [String]$NodeName ) $copyTestsFileParams = @{ 'Session' = $session; 'ArgumentList' = @($PSRemotely, $testName, $testBlock, $NodeName) 'Scriptblock' = { param( [HashTable]$PSRemotely, [String]$testName, [String]$testBlock, [String]$NodeName ) # generate the test file name..naming convention -> NodeName.TestName.Tests.ps1 # TODO - instead of the computername , the nodename defined in the environment config data should flow down here. # This would be used when using reportunit for the report creation. if ($NodeName) { $testFileName = "{0}.{1}.Tests.ps1" -f $NodeName, $testName.replace(' ','_') # Check that the filename does not contain invalid file characters e.g ::1 is the nodename in case of link local ipv6 address # Credits : http://stackoverflow.com/questions/36408035/validating-file-name-input-in-powershell $IndexOfInvalidChar = $testFileName.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) # IndexOfAny() returns the value -1 to indicate no such character was found if($IndexOfInvalidChar -ne -1){ # if there is an invalid character in the filename, fall back to using the computername Write-Warning -Message "Invalid character found in the file name > $($testFileName). Switching to using env:computername for filename" $testFileName = "{0}.{1}.Tests.ps1" -f $Env:COMPUTERNAME, $testName.replace(' ','_') } } else { $testFileName = "{0}.{1}.Tests.ps1" -f $Env:COMPUTERNAME, $testName.replace(' ','_') } $testFile = "$($PSRemotely.PSRemotelyNodePath)\$testFileName" if ($Node) { # Check if the $Node var was populated in the remote session, then add param node to the test block $testBlock = $testBlock.Insert(0,"param(`$node)`n") } Set-Content -Path $testFile -Value $testBlock -Force }; } Invoke-Command @copyTestsFileParams } Function CleanupPSRemotelyNodePath { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.Runspaces.PSSession]$session, [Parameter(Mandatory)] [String]$PSRemotelyNodePath ) $cleanupParams = @{ 'Session' = $session; 'ArgumentList' = $PSRemotelyNodePath 'ScriptBlock' = { param([String]$path) if ( -not (Test-Path -Path $Path\archive -PathType Container)) { $null = New-Item -Path "$Path\archive" -ItemType Directory } Get-ChildItem -Path $Path -File | Move-Item -Destination "$path\archive" -Force } } Invoke-Command @cleanupParams } |