1
votes

I am trying to create a TeamCity build configuration for creating a Docker Image.

For redundancy, I want the first Build Step to check whether Docker is running on the TeamCity server, and start it if necessary.

I have created the following PowerShell script that does exactly that. The script even waits until Docker is fully functional before finishing.

# This File checks if docker is running, and starts it if necessary, waiting until it has finished starting up before exiting

# ---------
# VARIABLES
# ---------

$TimeoutInterval = 10 #Timeout interval for checking whether docker has finished starting, in seconds
$DockerPath = "C:\Program Files\Docker\Docker\Docker for Windows.exe"


# ---------
# FUNCTIONS
# ---------

Function ProcessRunning([string] $ProcessName) {
    [bool] $Return = $False
    if ( Get-Process | Where-Object {$_.Name -like $ProcessName} ) {
        $Return = $True
    }
    Return $Return
}


# -------
# PROGRAM
# -------

# Enables Error  Action Preference to stop to enable try-catches
$ErrorActionPreference = 'Stop'

if(ProcessRunning("Docker for Windows")){
    echo "Docker is running"
    echo ""
    docker version
}else{
    echo "Docker is not running"
    echo ""
    echo "Starting Docker"
    echo ""
    Start-Process $DockerPath

    #Waits while Docker has not finished starting up
    $dockerStarting = $true
    while($dockerStarting){
        try{
            docker version
            $dockerStarting = $false
        }catch{
            echo ""
            echo "Docker is still starting up..."
            echo ""
            $dockerStarting = $true
            Wait-Event -Timeout $TimeoutInterval
        }
    }
    echo ""
    echo "Docker has finished starting up!"
    echo ""
}

This script works fine when executed locally on the TeamCity server. However, when I attempt to run it as a BuildStep, it appears to ignore the try-catch block like it did in my local version before I set the $ErrorActionPreference = 'Stop'

Specifically, the docker version command fails as I intended it to to indicate that Docker is not yet fully running. However, the try-catch block fails to catch it as it does when run locally on the server.

I have already tried both values of Format stderr output as: as well as Script execution mode:, but the result remains the same: The script throws an error, and Docker is not started on the TeamCity Server.

Now, my basic question is: Is what I'm trying even technically possible? Because I could imagine that TeamCity employs some sort of safeguard mechanism that prevents certain changes on the Server it's run from. Then again, I have in the past successfully employed PowerShell scripts to copy and delete files on the Server, so honestly, at this point I'm more confused than anything about why this doesn't seem to work.

Any input on this would be greatly appreciated.

1

1 Answers

0
votes

Okay, so I tried around a lot, and eventually found a solution that allows me to check if Docker is running via a TeamCity build step and successfully start it if not.

This actually turned out to be a two-tier problem, with two separate things having to be done in order to make it work:

1.) Run the TeamCity Build Agent Service as an elevated local account

In my case, I had the TeamCity Build Agent Service running under the SYSTEM account. I switched it to an Administrator account using:

Services
↳TeamCity Build Agent
 ↳Log On>Log on as
  ↳◉This account: .\Administrator

2.) Use a "clean" skript that does not rely on try-catch to see if Docker is running

The final PowerShell Skript that I'm using now and that works nicely looks like this:

# This File checks if docker is running, and starts it if necessary, waiting until it has finished starting up before exiting

# ---------
# VARIABLES
# ---------

$TimeoutInterval = 10 #Timeout interval for checking whether docker has finished starting, in seconds
$DockerPath = "C:\Program Files\Docker\Docker\Docker for Windows.exe"


# ---------
# FUNCTIONS
# ---------

Function ProcessRunning([string] $ProcessName) {
    [bool] $Return = $False
    if ( Get-Process | Where-Object {$_.Name -like $ProcessName} ) {
        $Return = $True
    }
    Return $Return
}

# Determines if a Service exists with a name as defined in $ServiceName.
Function ServiceExists([string] $ServiceName) {
    [bool] $Return = $False
    if ( Get-Service "$ServiceName*" -Include $ServiceName ) {
        $Return = $True
    }
    Return $Return
}

# Determines if a Service with a name as defined in $ServiceName exists and is running
Function ServiceRunning([string] $ServiceName) {
    [bool] $Return = $False
    Write-Host "Checking if Service "$ServiceName" exists and is running"
    Write-Host ""
    if ( Get-Service "$ServiceName*" -Include $ServiceName ) {

        $arrService = Get-Service -Include $ServiceName

        if ( $arrService.Status -eq "Running" ) {
            Write-Host "Service "$ServiceName" exists, and is running!"
            Write-Host ""
            $Return = $True
        }else{
            Write-Host "Service "$ServiceName" exists, but is not running!"
            Write-Host ""
        }
    }else{
        Write-Host "Service "$ServiceName" does not exist!"
        Write-Host ""
    }
    Return $Return
}


# -------
# PROGRAM
# -------

if(ProcessRunning("Docker for Windows")){
    Write-Host "Docker is running"
    Write-Host ""
    docker version
}else{
    Write-Host "Docker is not running"
    Write-Host ""
    Write-Host "Starting Docker"
    Write-Host ""
    Start-Process $DockerPath

    #Waits while Docker has not finished starting up
    $dockerStarting = $true
    while($dockerStarting){
        if((ServiceRunning("com.docker.service")) -and (ServiceRunning("docker"))){
            Write-Host ""
            Write-Host "Docker has finished starting up!"
            Write-Host ""
            docker version
            $dockerStarting = $false
        }else{
            Write-Host ""
            Write-Host "Docker is still starting up..."
            Write-Host ""

            #Attempts to start the relevant services
            if(!(ServiceRunning("com.docker.service"))){
                if(ServiceExists("com.docker.service")){
                    Start-Service "com.docker.service"
                }
            }
            if(!(ServiceRunning("docker"))){
                if(ServiceExists("docker")){
                    Start-Service "docker"
                }
            }

            $dockerStarting = $true
            Wait-Event -Timeout $TimeoutInterval
        }
    }
}

I still have no idea why try-catch isn't working in TeamCity, so if someone knows the answer to that, please comment.

Anyway, I hope this solution is helpful to someone.