function Publish-AlkamiServiceFabricPackages { <# .SYNOPSIS Stages Microservice Packages to the service fabric cluster endpoint before deployments. .PARAMETER packages The package objects to be staged onto the service fabric cluster. .PARAMETER hostname A single server hostname from the target Service Fabric cluster. #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [Alias("PackagesToPublish")] [AllowNull()] [object[]]$packages, [Parameter(Mandatory = $false)] [Alias("s")] [string]$hostname = "localhost", [Parameter(Mandatory = $false)] [string]$defaultLogConfigFolder = $null, [Parameter(Mandatory = $false)] [pscredential]$NugetCredential ) $loglead = Get-LogLeadName; $StopWatch = [System.Diagnostics.Stopwatch]::StartNew(); # Set feeds to packages if they are not already there. Set-ChocoPackageSourceFeeds -packages $packages -hostname $hostname -Verbose; # Remove installers from the list. $packages = $packages | Where-Object { !(Test-IsPackageInstaller -packageName $_.Name) }; Write-Host "$loglead : Looking up information on remote host $hostname"; # Connect to SF cluster. Connect-AlkamiServiceFabricCluster -hostname $hostname | Out-Null; # Get machine config AppSettings. $environmentName = Get-AppSetting -appSettingKey "Environment.Name" -ComputerName $hostname; $environmentType = Get-AppSetting -appSettingKey "Environment.Type" -ComputerName $hostname; $dbUser = Get-AppSetting -appSettingKey "DatabaseMicroServiceAccount" -ComputerName $hostname; $microUser = Get-AppSetting -appSettingKey "NonDatabaseMicroServiceAccount" -ComputerName $hostname; $environmentShortName = Format-AlkamiEnvironmentName -name $environmentName; # Get the server nodes relevant to the environment. $nodes = Get-AlkamiServiceFabricNode -EnvironmentName $environmentName; if(Test-IsCollectionNullOrEmpty $nodes) { throw "$loglead : Could not identify environment Service Fabric nodes for $environmentShortName"; } $nodeCount = $nodes.count; # Determine the default instance count of microservices based on the environment. # Make sure there are at least two instances, unless it's a single server environment. $defaultInstanceCount = [System.Math]::Max($nodeCount-2, 2); if($nodeCount -eq 1) { $defaultInstanceCount = 1; } # Get the running applications on the cluster. Note: This is not the list of registered package images. $runningApplications = Get-AlkamiServiceFabricApplications -ComputerName $hostname -EnvironmentName $environmentName; # Create and register applications. foreach($package in $packages) { $packageStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); $name = $package.Name; $version = $package.Version; $hasError = $false; # Try to find/create the package from each of the choco feeds configured on the remote server. $packageSource = $package.Feed.Source; if([string]::IsNullOrWhiteSpace($packageSource)) { throw "$loglead : Unable to locate chocolatey feed containing package $name|$version on remote computer $hostname"; } # Determine if the package is a SF reliable service vs a normal microservice. $isReliableService = (Test-IsPackageReliableService -feedSource $packageSource -name $name -version $version -Credential $NugetCredential); # Determine application name and application type name. $applicationTypeName = $null; if($isReliableService) { # The application type name -must- be the name of the class that implements the reliable service contract. # Pull this name from the manifest located inside the package. $applicationTypeName = Get-AlkamiServiceFabricPackageServiceTypeName -source $packageSource -name $name -version $version -NugetCredential $NugetCredential } else { # For normal microservices the application type name is environment tied, and the application name matches the service name. $applicationTypeName = Format-AlkamiServiceFabricApplicationName -name $name -environmentName $environmentName; } # Build the full name for SF image versioning purposes. $fullNameVersion = "$name-$version-$environmentShortName"; Write-Verbose "$loglead : Determining if `"$name|$version`" is already registered on the cluster."; $registrationInfo = Get-ServiceFabricApplicationType -ApplicationTypeName $applicationTypeName -ApplicationTypeVersion $version; if(!($registrationInfo)) { # Determine the number of instances to deploy the application with. $instanceCount = $defaultInstanceCount; # Figure out if another version of the service is already running on the cluster, and set the instance count to what it currently is. $applicationSearch = $runningApplications | Where-Object {$_.Name -eq $name} | Select-Object -First 1; if($null -ne $applicationSearch) { $instanceCount = Get-AlkamiServiceFabricApplicationInstanceCount -ServiceFabricApplicationName $applicationSearch.ServiceFabricApplicationName -ComputerName $hostname; } # Create temporary folder location. $tempOutputFolder = [System.IO.Path]::GetTempPath() + [guid]::NewGuid().ToString(); try { if($isReliableService) { # Poke required values into reliable service app/svc manifests where appropriate. $arguments = @{ source = $packageSource name = $name version = $version outputFolder = $tempOutputFolder userDbms = $dbUser userMicro = $microUser defaultInstanceCount = $instanceCount environmentType = $environmentType defaultLogConfigFolder = $defaultLogConfigFolder NugetCredential = $NugetCredential } New-AlkamiServiceFabricReliableServicePackage @arguments; } else { # Repackage topshelf microservice to the format that service fabric expects.. $arguments = @{ source = $packageSource name = $name version = $version outputFolder = $tempOutputFolder userDbms = $dbUser userMicro = $microUser defaultInstanceCount = $instanceCount environmentName = $environmentName environmentType = $environmentType defaultLogConfigFolder = $defaultLogConfigFolder NugetCredential = $NugetCredential } New-AlkamiServiceFabricPackage @arguments; } # Copy package to image store. Write-Host "$loglead : Uploading and Registering $fullNameVersion from package built at $tempOutputFolder"; Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $tempOutputFolder -ApplicationPackagePathInImageStore $fullNameVersion -CompressPackage; # Register the application with Service Fabric. Register-ServiceFabricApplicationType -ApplicationPathInImageStore $fullNameVersion -ApplicationPackageCleanupPolicy Automatic; Write-Host "$loglead : Package publish complete."; } catch { Write-Error "$loglead : Failed to publish Service Fabric package:`n $($_.Exception.Message)"; $hasError = $true; } finally { if(Test-Path $tempOutputFolder) { if($hasError) { Write-Warning "$loglead : Removing incomplete package creation at $tempOutputFolder"; } else { Write-Host "$loglead : Removing package files at $tempOutputFolder"; } Invoke-CommandWithRetry -MaxRetries 3 -SecondsDelay 1 -Arguments @($tempOutputFolder) -ScriptBlock { param($folderPath) Remove-FileSystemItem $folderPath -Recurse } } } } else { Write-Host "$loglead : Service Fabric Application $name|$version is already registered. Skipping."; } $packageStopWatch.Stop(); Write-Verbose "$loglead : Completed $name|$version in $($packageStopWatch.Elapsed.TotalSeconds) seconds."; } $StopWatch.Stop(); Write-Verbose "$loglead : Completed in $($StopWatch.Elapsed.TotalSeconds) seconds."; }