Public/Start-SLMongo.ps1

function Start-SLMongo {
    <#
    .SYNOPSIS
        Starts the local MongoDB replica set and waits until it can take writes.
 
    .DESCRIPTION
        1. If every node is already listening, does nothing to the processes.
        2. Otherwise runs 'mlaunch start --dir <set folder>'.
        3. Waits for every node's port (mlaunch staggers nodes, and a node can
           take 20s+ to open its port on a cold start).
        4. Waits for a PRIMARY. Freshly started members are all SECONDARY until
           they elect one, and apps that connect before that can't write.
 
        Returns the final status (see Get-SLMongoStatus). Throws if the ports
        never open; warns if no PRIMARY appears in time.
 
    .PARAMETER Path
        The folder passed to 'mlaunch init --dir'. See Get-SLMongoStatus.
 
    .EXAMPLE
        Start-SLMongo
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [string] $Path,
        [int] $PortTimeoutSeconds = 60,
        [int] $PrimaryTimeoutSeconds = 60,
        [switch] $Quiet
    )

    $set = Resolve-SLMongoReplicaSet -Path $Path
    function Say([string] $t, [string] $c = 'Gray') { if (-not $Quiet) { Write-Host " $t" -ForegroundColor $c } }

    $open = @(Get-SLOpenPorts $set.Ports)
    if ($open.Count -eq $set.Ports.Count) {
        Say "Replica set '$($set.Name)' is already running." 'Green'
    }
    else {
        $mlaunch = Get-SLMlaunchPath
        if (-not $mlaunch) { throw "mlaunch is not on PATH. Install it with: pip install mtools" }
        if (-not $PSCmdlet.ShouldProcess($set.Dir, 'mlaunch start')) { return }

        Say "Starting replica set '$($set.Name)' from $($set.Dir)"
        $run = Invoke-SLTool -Path $mlaunch -Arguments "start --dir `"$($set.Dir)`"" -TimeoutSeconds ($PortTimeoutSeconds + 30)
        if ($run.Error) { throw "Could not run mlaunch: $($run.Error)" }

        Say "Waiting for ports $($set.Ports -join ', ') (up to $PortTimeoutSeconds s)"
        if (-not (Wait-SLPorts -Ports $set.Ports -TimeoutSeconds $PortTimeoutSeconds)) {
            $still = @($set.Ports | Where-Object { -not (Test-SLPortOpen $_) })
            $logs = ($set.Nodes | Where-Object { $still -contains $_.Port } | ForEach-Object { $_.LogPath }) -join ', '
            $msg = "Timed out waiting for ports: $($still -join ', ')."
            if ($logs) { $msg += " Check: $logs" }
            if ($run.Output) { $msg += "`nmlaunch said: $($run.Output)" }
            throw $msg
        }
    }

    Say "Waiting for a PRIMARY (up to $PrimaryTimeoutSeconds s)"
    $deadline = (Get-Date).AddSeconds($PrimaryTimeoutSeconds)
    $primary = ''
    while (-not $primary -and (Get-Date) -lt $deadline) {
        $primary = Get-SLMongoPrimary $set.Ports
        if (-not $primary) { Start-Sleep -Seconds 2 }
    }
    if (-not $primary) {
        Write-Warning "No PRIMARY after $PrimaryTimeoutSeconds s. The set may still be electing; check with Get-SLMongoStatus."
    }

    Get-SLMongoStatus -Path $set.StartupFile -Quiet:$Quiet
}