I've got an OctopusDeploy process for deploying to a database server. The steps include copying db backups from a location to the local server, restoring them into SQL Server, then running a dacpac against them to upgrade them to a specific version.
This all works fine, but we've now added a new environment and I can't work out how to configure the deployment process for it.
Initially, the server was to be a windows clustered environment, with the tentacle running as a clustered service (which meant a single deployment target).
However, the company setting up our servers couldn't get clustering to work for whatever reason, and have now given us something in between:
We have two servers, each with the tentacle installed, configured and running on it.
Each tentacle has a unique thumbprint, and are always running and accessible.
Upon the windows servers, SQL Server has been installed and configured as "always on", with one server being the primary and the other being the secondary.
The idea being that if the primary dies, the secondary picks up the pieces and runs fine.
Conceptually, this works for us, as we have a "clustered" ip for the SQL server connection and our web app won't notice the difference.
(It's important to note, I CANNOT change this setup - it's a case of work with what we're given....)
Now, in Octopus, I need to ONLY deploy to one of the servers in this environment, as if I were to deploy to both, I'd either be duplicating the task (if run as a rolling deployment), or worse, have conflicting deployments (if run asynchronous).
I initially tried added a secondary role to each server ("PrimaryNode", "SecondaryNode"), but I then discovered Octopus treats roles as an "or" rather than an "and", so this wouldn't work for us out of the box
I then looked at writing powershell scripts that checked if the machine that had the roles "dbserver" AND "primarynode" had a status of "Online" and a health of "healthy", then set an output variable based on the status:
##CONFIG##
$APIKey = "API-OBSCURED"
$MainRole = "DBServer"
$SecondaryRole = "PrimaryNode"
$roles = $OctopusParameters['Octopus.Machine.Roles'] -split ","
$enableFailoverDeployment = $false
foreach($role in $roles)
{
if ($role -eq "FailoverNode")
{
#This is the failovernode - check if the primary node is up and running
Write-Host "This is the failover database node. Checking if primary node is available before attempting deployment."
$OctopusURL = "https://myOctourl" ##$OctopusParameters['Octopus.Web.BaseUrl']
$EnvironmentID = $OctopusParameters['Octopus.Environment.Id']
$header = @{ "X-Octopus-ApiKey" = $APIKey }
$environment = (Invoke-WebRequest -UseBasicParsing "$OctopusURL/api/environments/$EnvironmentID" -Headers $header).content | ConvertFrom-Json
$machines = ((Invoke-WebRequest -UseBasicParsing ($OctopusURL + $environment.Links.Machines) -Headers $header).content | ConvertFrom-Json).items
$MachinesInRole = $machines | ?{$MainRole -in $_.Roles}
$MachinesInRole = $MachinesInRole | ?{$SecondaryRole -in $_.Roles}
$measure = $MachinesInRole | measure
$total = $measure.Count
if ($total -gt 0)
{
$currentMachine = $MachinesInRole[0]
$machineUri = $currentMachine.URI
if ($currentMachine.Status -eq "Online")
{
if ($currentMachine.HealthStatus -eq "Healthy")
{
Write-Host "Primary node is online and healthy."
Write-Host "Setting flag to disable failover deployment."
$enableFailoverDeployment = $false
}
else
{
Write-Host "Primary node has a health status of $($currentMachine.HealthStatus)."
Write-Host "Setting flag to enable failover deployment."
$enableFailoverDeployment = $true
}
}
else
{
Write-Host "Primary node has a status of $($currentMachine.Status)."
Write-Host "Setting flag to enable failover deployment."
$enableFailoverDeployment = $true
}
}
break;
}
}
Set-OctopusVariable -name "EnableFailoverDeployment" -value $enableFailoverDeployment
This seemingly works - I can tell if I should deploy to the primary OR the secondary.
However, I'm now stuck at how I get the deployment process to use this.
Obviously, if the primary node is offline, then the deployment won't happen on it anyway.
Yet, if BOTH tentacles are online and healthy, then octopus will just attempt to deploy to them.
The deployment process contains about 12 unique steps, and is successfully used in several other environments (all single-server configurations), but as mentioned, now needs to ALSO deploy to a weird active/warm environment.
Any ideas how I might achieve this? (If only you could specify "AND" in roles..)
UPDATE 1 I've now found that you can update specific machines "IsDisabed" via the web api, so I added code to the end of the above to enable/disable the secondary node depending on the outcome instead of setting an output variable.
Whilst this does indeed update the machine's status, it doesn't actually effect the ongoing deployment process. If I stop and restart the whole process, the machine is correctly picked up as enabled/disabled accordingly, but again, if it's status changes DURING the deployment, Octopus doesn't appear to be "smart" enough to recognise this, ruling this option out. (I did try adding a healthcheck step before and after this script to see if that made a difference, but whilst the healthcheck realised the machine was disabled, it still made no difference to the rest of the steps)
Update 2
I've now also found the "ExcludedMachineIds" property of the "Deployment" in the API, but I get a 405 (not allowed) error when trying to update it once a deployment is in process.
gah.. why can't this be easy?
DB-Master
andDB-Slave
on those targets, and then set your deployment process to only target the master! – gvee