281 lines
14 KiB
PowerShell
281 lines
14 KiB
PowerShell
|
function New-AlkamiServiceFabricPackage {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Turns a Chocolatey/Nuget package into the file/folder input that Service Fabric requires for deployments.
|
|||
|
Note: The output folder is -not- automatically cleaned up! Delete it so avoid piling up memory.
|
|||
|
.PARAMETER source
|
|||
|
The nuget URL of the package repository.
|
|||
|
.PARAMETER name
|
|||
|
The name/ID of the package to be created.
|
|||
|
.PARAMETER version
|
|||
|
The version of the package to be created.
|
|||
|
.PARAMETER userMicro
|
|||
|
The name of the non-database microservice user gmsa account.
|
|||
|
.PARAMETER userDbms
|
|||
|
The name of the database microservice user gmsa account.
|
|||
|
.PARAMETER defaultInstanceCount
|
|||
|
The default number of microservice instances that will be deployed.
|
|||
|
.PARAMETER outputFolder
|
|||
|
The download/output directory of the package.
|
|||
|
#>
|
|||
|
|
|||
|
[CmdletBinding()]
|
|||
|
Param(
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[Alias("s")]
|
|||
|
[string]$source,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[Alias("n")]
|
|||
|
[string]$name,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[Alias("v")]
|
|||
|
[string]$version,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]$environmentName,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]$environmentType,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]$userMicro,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]$userDbms,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[int]$defaultInstanceCount,
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[Alias("o")]
|
|||
|
[string]$outputFolder,
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[string]$defaultLogConfigFolder = $null,
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[switch]$force,
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[pscredential]$nugetCredential = $null
|
|||
|
)
|
|||
|
|
|||
|
$loglead = Get-LogLeadName;
|
|||
|
|
|||
|
$packageString = "$name|$version";
|
|||
|
Write-Host "$loglead : Creating Service Fabric package for $packageString";
|
|||
|
|
|||
|
# Download Alkami package.
|
|||
|
if (Test-Path $outputFolder) {
|
|||
|
if (!$force) {
|
|||
|
throw "$loglead : Directory `"$outputFolder`" already exists. If you intend to overwrite it use -force.";
|
|||
|
} else {
|
|||
|
Write-Host "$loglead : Recreating directory $outputFolder";
|
|||
|
Remove-Item -Path $outputFolder -Recurse -Force;
|
|||
|
New-Item -Path $outputFolder -ItemType Directory | Out-Null;
|
|||
|
}
|
|||
|
} else {
|
|||
|
Write-Host "$loglead : Creating directory $outputFolder";
|
|||
|
New-Item -Path $outputFolder -ItemType Directory | Out-Null;
|
|||
|
}
|
|||
|
|
|||
|
$tempPackagePath = (Join-Path $outputFolder "nugetDownload.zip");
|
|||
|
$fabricConfigTemplatesPath = (Join-Path $PSScriptRoot "ServiceFabricConfigTemplates");
|
|||
|
|
|||
|
try {
|
|||
|
# Download specified package from nuget feed.
|
|||
|
Write-Verbose "$loglead : Downloading package $packageString from proget to `"$tempPackagePath.`"";
|
|||
|
Get-PackageFromProget -source $source -name $name -version $version -output $tempPackagePath -nugetCredential $nugetCredential;
|
|||
|
|
|||
|
# Get a version of the microservice name with the "v3" style version name on the end.
|
|||
|
$environmentShortName = Format-AlkamiEnvironmentName -name $environmentName;
|
|||
|
$applicationTypeName = (Format-AlkamiServiceFabricApplicationName -name $name -environmentName $environmentName);
|
|||
|
$serviceName = "svc";
|
|||
|
# Note: The service name "svc" is for short service fabric pathing. This is not actually the name of the service.
|
|||
|
# The full name of the service will look like this: "Alkami.Widget.Sample/v1/svc/"
|
|||
|
|
|||
|
# Create Service Fabric directory structure.
|
|||
|
Write-Verbose "$loglead : Creating SF package directory structure in `"$outputFolder`"";
|
|||
|
$subfolders = @(
|
|||
|
$serviceName,
|
|||
|
"$serviceName\Code",
|
|||
|
"$serviceName\Config"
|
|||
|
)
|
|||
|
for ($i = 0; $i -lt $subfolders.Count; $i++) {
|
|||
|
$path = (Join-Path $outputFolder $subfolders[$i]);
|
|||
|
if (!(Test-Path $path)) {
|
|||
|
New-Item -ItemType directory -Path $path -Force | Out-Null;
|
|||
|
}
|
|||
|
Write-Verbose "$loglead : Created $path";
|
|||
|
$subfolders[$i] = $path;
|
|||
|
}
|
|||
|
$serviceRoot = $subfolders[0];
|
|||
|
$codeRoot = $subfolders[1];
|
|||
|
$configRoot = $subfolders[2];
|
|||
|
|
|||
|
# Unzip the .nupkg to where Service Fabric expects files to be.
|
|||
|
Write-Verbose "$loglead : Unzipping $packageString from `"$tempPackagePath`" to `"$codeRoot`"";
|
|||
|
$szPath = Get-7ZipPath
|
|||
|
& $szPath x "$tempPackagePath" -aoa -o"$codeRoot" "tools/*" -r | Out-Null
|
|||
|
& $szPath e "$tempPackagePath" -aoa -o"$codeRoot" "*.nuspec" -r | Out-Null
|
|||
|
|
|||
|
# Copy everything in the tools folder up a directory and remove it.
|
|||
|
$toolsFolder = "$codeRoot/tools";
|
|||
|
$items = Get-ChildItem -Path $toolsFolder;
|
|||
|
foreach ($item in $items) {
|
|||
|
Move-Item -Path $item.FullName -Destination $codeRoot;
|
|||
|
}
|
|||
|
Remove-Item $toolsFolder -Force;
|
|||
|
|
|||
|
# Clean up nuget .nupkg download.
|
|||
|
if (Test-Path $tempPackagePath) {
|
|||
|
Remove-Item -Path $tempPackagePath;
|
|||
|
}
|
|||
|
|
|||
|
# Figure out if the service .exe exists before we proceed. We might be packaging up an orb provider.
|
|||
|
$exeName = "$name.exe";
|
|||
|
$exeLocation = (Join-Path $codeRoot $exeName);
|
|||
|
if (!(Test-Path $exeLocation)) {
|
|||
|
Write-Warning "$loglead : Could not locate standard $exeName name by convention. Falling back to searching for .exe's in the tools directory."
|
|||
|
|
|||
|
# Search the code root for a .exe.
|
|||
|
$executableSearch = Get-ChildItem -Path $codeRoot -Filter "*.exe" | Where-Object { $_.Name -notlike "*.vshost.exe" };
|
|||
|
if (($null -eq $executableSearch)) {
|
|||
|
# Fail if we could not find any executable.
|
|||
|
throw "$loglead : Could not locate microservice executable. Are you sure this is a microservice?";
|
|||
|
} elseif ($executableSearch.Count -gt 1) {
|
|||
|
# Fail if we found more than one executable in this folder.
|
|||
|
throw "$loglead : Located more than one microservice executable. Cannot assume which executable to use. Investigate assumptions made.";
|
|||
|
}
|
|||
|
$exeLocation = $executableSearch.FullName
|
|||
|
$exeName = $executableSearch.Name
|
|||
|
Write-Verbose "$loglead : Found executable $exeName";
|
|||
|
}
|
|||
|
|
|||
|
# Determine if the config defaults folder has been configured.
|
|||
|
$hasConfigDefaults = (!([string]::IsNullOrWhiteSpace($defaultLogConfigFolder))) -and (Test-Path $defaultLogConfigFolder)
|
|||
|
|
|||
|
# If the config defaults folder was specified, see if the microservice should have new relic enabled/disabled.
|
|||
|
if($hasConfigDefaults) {
|
|||
|
|
|||
|
# Read in the package names to leave new-relic enabled for.
|
|||
|
$newRelicServicePath = (Join-Path $defaultLogConfigFolder "NewRelicMicroServices/NewRelicMicroServices.txt")
|
|||
|
|
|||
|
$newRelicMicroservicesToLeaveEnabled = $null
|
|||
|
if(Test-Path $newRelicServicePath) {
|
|||
|
$newRelicMicroservicesToLeaveEnabled = [array](Get-Content -Path $newRelicServicePath)
|
|||
|
} else {
|
|||
|
Write-Warning "$loglead : Could not find NewRelicMicroServices.txt to leave new relic enabled. Assuming no microservices should have NewRelic enabled."
|
|||
|
$newRelicMicroservicesToLeaveEnabled = $null
|
|||
|
}
|
|||
|
|
|||
|
$enableNewRelic = ($newRelicMicroservicesToLeaveEnabled -contains $name)
|
|||
|
Set-ChocolateyPackageNewRelicState -Directory $codeRoot -Enabled $enableNewRelic
|
|||
|
}
|
|||
|
|
|||
|
# If the config defaults folder was specified, look for a log4net config for this microservice.
|
|||
|
if ($hasConfigDefaults) {
|
|||
|
|
|||
|
Write-Verbose "$loglead : Config default folder exists, looking for $name log4net default."
|
|||
|
|
|||
|
$configDefaultsLog4Net = (Join-Path $defaultLogConfigFolder "log4net")
|
|||
|
$logConfigPath = Get-DefaultLog4NetPathForPackage -SourcePath $configDefaultsLog4Net -PackageName $name -EnvironmentName $environmentShortName -EnvironmentType $environmentType
|
|||
|
Write-Verbose "$loglead : Looking for log4net default config path at '$logConfigPath'"
|
|||
|
|
|||
|
# If the log4net config path exists for the microservice we're deploying, replace the one in the package!
|
|||
|
if ((!([String]::IsNullOrEmpty($logConfigPath))) -and (Test-Path $logConfigPath)) {
|
|||
|
$destinationLog4NetPath = (Join-Path $codeRoot "log4net.config")
|
|||
|
Write-Verbose "$loglead : Log4net config default replacement exists, replacing the config from the package."
|
|||
|
Copy-Item -Path $logConfigPath -Destination $destinationLog4NetPath -Force
|
|||
|
} else {
|
|||
|
Write-Verbose "$loglead : There is no log4net config default. Using the log4net.config from the package."
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
# Copy config template files to the appropriate locations.
|
|||
|
$applicationConfigPath = (Join-Path $outputFolder "ApplicationManifest.xml");
|
|||
|
$serviceConfigPath = (Join-Path $serviceRoot "ServiceManifest.xml");
|
|||
|
$settingsConfigPath = (Join-Path $configRoot "Settings.xml");
|
|||
|
Copy-Item -Path (Join-Path $fabricConfigTemplatesPath "ApplicationManifest.xml") -Destination $applicationConfigPath -Force
|
|||
|
Copy-Item -Path (Join-Path $fabricConfigTemplatesPath "ServiceManifest.xml") -Destination $serviceConfigPath -Force
|
|||
|
Copy-Item -Path (Join-Path $fabricConfigTemplatesPath "Settings.xml") -Destination $settingsConfigPath -Force
|
|||
|
|
|||
|
# Determine the number of instances to run for a service. -1 means it runs on every node.
|
|||
|
$instanceCount = $defaultInstanceCount;
|
|||
|
$everywhereServices = $Global:InfrastructureMicroServices;
|
|||
|
if ($null -ne ($everywhereServices | Where-Object { $_ -like $name })) {
|
|||
|
$instanceCount = -1;
|
|||
|
}
|
|||
|
|
|||
|
# Detect which user to run the application as by reading the nuspec for a dependency to the database migrator.
|
|||
|
$nuspecPath = (Get-ChildItem -Path $codeRoot -Filter "*.nuspec" | Select-Object -First 1).FullName;
|
|||
|
|
|||
|
$hasMigrations = $true;
|
|||
|
if (($null -ne $nuspecPath) -and (Test-Path $nuspecPath)) {
|
|||
|
$nuspec = [xml](Get-Content -Path $nuspecPath);
|
|||
|
$hasMigrations = (Test-IsPackageDbms -nuspec $nuspec);
|
|||
|
$nuspec = $null;
|
|||
|
}
|
|||
|
# Else we assume the package has migrations just to be conservative.
|
|||
|
|
|||
|
$gmsaUser = if($hasMigrations) { $userDbms; } else { $userMicro; }
|
|||
|
|
|||
|
# Fill out the application manifest.
|
|||
|
Write-Verbose "$loglead : Creating Application Manifest at `"$applicationConfigPath`"";
|
|||
|
$applicationManifest = (Read-XMLFile -xmlPath $applicationConfigPath);
|
|||
|
$applicationManifestNS = $applicationManifest.DocumentElement.NamespaceURI
|
|||
|
$root = $applicationManifest.ApplicationManifest;
|
|||
|
$root.SetAttribute("ApplicationTypeName", $applicationTypeName);
|
|||
|
$root.SetAttribute("ApplicationTypeVersion", $version);
|
|||
|
$instanceCountNode = $root.Parameters.ChildNodes | Where-Object { $_.Name -eq "InstanceCount" };
|
|||
|
$instanceCountNode.SetAttribute("DefaultValue", $instanceCount);
|
|||
|
$root.ServiceManifestImport.ServiceManifestRef.SetAttribute("ServiceManifestName", $serviceName);
|
|||
|
$root.ServiceManifestImport.ServiceManifestRef.SetAttribute("ServiceManifestVersion", $version);
|
|||
|
$root.DefaultServices.Service.SetAttribute("Name", $serviceName);
|
|||
|
$root.DefaultServices.Service.StatelessService.SetAttribute("ServiceTypeName", $serviceName);
|
|||
|
$root.Principals.Users.User.SetAttribute("AccountName", $gmsaUser);
|
|||
|
if ($null -ne $root.DefaultServices.Service.StatelessService.PlacementConstraints) {
|
|||
|
$environmentWorkerNodeType = (Format-AlkamiEnvironmentWorkerNodeType $environmentName);
|
|||
|
$root.DefaultServices.Service.StatelessService.PlacementConstraints = "NodeType == $environmentWorkerNodeType";
|
|||
|
}
|
|||
|
|
|||
|
# Set nuget package name as a property.
|
|||
|
$nugetPackageNameNode = $root.Parameters.ChildNodes | Where-Object { $_.Name -eq "NugetPackageName" } | Select-Object -First 1;
|
|||
|
if ($null -eq $nugetPackageNameNode) {
|
|||
|
Write-Verbose "$loglead Could not find ApplicationManifest parameter NugetPackageName, adding..";
|
|||
|
$nugetPackageNameNode = $applicationManifest.CreateElement("Parameter",$applicationManifestNS);
|
|||
|
[void]$root.Parameters.AppendChild($nugetPackageNameNode);
|
|||
|
}
|
|||
|
$nugetPackageNameNode.SetAttribute("Name", "NugetPackageName");
|
|||
|
$nugetPackageNameNode.SetAttribute("DefaultValue", $name);
|
|||
|
|
|||
|
Save-XMLFile -xmlPath $applicationConfigPath -xml $applicationManifest;
|
|||
|
|
|||
|
$root = $null;
|
|||
|
$applicationManifest = $null;
|
|||
|
|
|||
|
# Fill out the service manifest.
|
|||
|
Write-Verbose "$loglead : Creating Service Manifest at `"$serviceConfigPath`"";
|
|||
|
$serviceManifest = (Read-XMLFile -xmlPath $serviceConfigPath);
|
|||
|
$serviceManifestNS = $serviceManifest.DocumentElement.NamespaceURI
|
|||
|
$root = $serviceManifest.ServiceManifest;
|
|||
|
$root.SetAttribute("Name", $serviceName);
|
|||
|
$root.SetAttribute("Version", $version);
|
|||
|
$root.ServiceTypes.StatelessServiceType.SetAttribute("ServiceTypeName", $serviceName);
|
|||
|
$root.CodePackage.SetAttribute("Version", $version);
|
|||
|
$root.ConfigPackage.SetAttribute("Version", $version);
|
|||
|
$root.CodePackage.EntryPoint.ExeHost.Program = $exeName;
|
|||
|
if ($exeName -eq "Alkami.Services.Subscriptions.Host.exe") {
|
|||
|
$elem = $servicemanifest.CreateElement("ConsoleRedirection",$serviceManifestNS)
|
|||
|
$root.CodePackage.EntryPoint.ExeHost.AppendChild($elem)
|
|||
|
$elem.SetAttribute("FileRetentionCount", '5')
|
|||
|
$elem.SetAttribute("FileMaxSizeInKb", '2048')
|
|||
|
}
|
|||
|
$root.Resources.Endpoints.Endpoint.SetAttribute("Name", "$($name).HostEndpoint");
|
|||
|
Save-XMLFile -xmlPath $serviceConfigPath -xml $serviceManifest;
|
|||
|
$root = $null;
|
|||
|
$serviceManifest = $null;
|
|||
|
} catch {
|
|||
|
Write-Error "$loglead : Failed to create Service Fabric package:`n $($_.Exception.Message)";
|
|||
|
|
|||
|
# Clean up incomplete package build.
|
|||
|
Start-Sleep -Seconds 1;
|
|||
|
if (Test-Path $outputFolder) {
|
|||
|
Write-Warning "$loglead : Removing incomplete package creation at $outputFolder";
|
|||
|
Remove-Item $outputFolder -Recurse -Force;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Write-Host "$loglead : Package creation complete at `"$outputFolder`".";
|
|||
|
}
|