Завершение всего конвейера между машинами при ошибке завершения

#powershell #error-handling #powershell-4.0

#powershell #обработка ошибок #powershell-4.0

Вопрос:

Я пытаюсь автоматизировать задачу, которая выполняется путем ручного вызова подчиненного скрипта, который я не хочу изменять больше, чем необходимо, и должен оставить возможность использовать его вручную.

До сих пор мне удалось создать вызывающий скрипт, который выполняет то, что мы привыкли делать вручную, но я не могу найти элегантный способ завершить конвейер при первой ошибке завершения.

Идея заключается в том, что если ошибка завершения встречается где-либо в главном или подчиненном, весь конвейер завершается со статусом ошибки.

Это самое дальнее, что я получил:

PipelineMaster.ps1

 $groups = (@('server1', 'server2', 'server3'), @('server4'))
trap { 
    'trap at master';
    if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
    throw 1 
}

ForEach ($group in $groups) {
    Invoke-Command -ComputerName $group[0] -ArgumentList $group -ScriptBlock {
        param([Parameter(ValueFromRemainingArguments = $true)][String[]]$group)
        trap { 
            'trap at invocation';
            if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
            throw 1 
        }
        
        # preparation per group
        .PipelineSlave.ps1 -group $group
    } 
} 
  

PipelineSlave.ps1:

 param([string[]]$group)
trap { 
    'trap at slave';
    if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
    throw 1 
}

ForEach ($h in $group) {
    Invoke-Command -ComputerName $h -ScriptBlock {
        trap { 
            'trap at node';
            if ($_.FullyQualifiedErrorId -ne 1) { Write-Error -ErrorRecord $_ }
            throw 1
        }
        " ## $(hostname)"
        "minor err"; New-Item -ItemType Directory -Path c:
        'major err'; New-Item -ItemType Directory -Path c:temp -ErrorAction Stop
        " ## Done on $(hostname)"
    }
}
  

Это используется trap на каждом уровне области.

Я экспериментировал с $? после завершения каждой области, но это также срабатывает при не завершающихся ошибках, и я не нашел способа отличить.

Другим вариантом было просто обернуть все в try и throw 1 после получения там. В качестве побочного вопроса: почему try{...} catch {throw} работает по-другому от try{...} catch {throw 1} ? Первый завершает только контекст, последний — весь конвейер, и я не смог найти ни одной статьи, в которой это объяснялось.

Наконец, я также экспериментировал с этим (упрощено здесь):

 ForEach ($group in $groups) {
    Invoke-Command -ComputerName $group[0] -ArgumentList $group -ScriptBlock {
        ForEach ($h in $group) {
            Invoke-Command ComputerName $h -ScriptBlock {
                New-Item -ItemType Directory -Path C:
                New-Item -ItemType Directory -Path C:temp
                if (!$?) { return 'ERROR' }
                "Done on $(hostname)"
            } | % {if ('ERROR' -eq $_) {return 'ERROR'} else {$_}}
        }
    } | % {if ('ERROR' -eq $_) {return 'ERROR'} else {$_}}
}
  

Это сделало то, что я хотел (в основном), но вместо того, чтобы полагаться на собственное разграничение PS между завершающими и не завершающими ошибками, мне нужно убедиться, что ошибка не завершается, а затем проверить результат каждой команды, которую я хочу завершить.

Я понимаю, что я мог бы также сделать выше, но throw 1 вместо return 'ERROR' этого, но это очень похоже на первый подход и более утомительно.

Примечание: Команды не обязательно являются новыми элементами, но они являются надежным способом запуска как завершающих, так и не завершающих ошибок в PS.

Комментарии:

1. Вы хотите, чтобы ошибки завершения… завершить? Это то, что они делают. Просто удалите ловушки. Или, пожалуйста, уточните вашу проблему.

2. @marsze Да… Если я удалю ловушки, они завершат работу только среды, а не всего конвейера, поэтому в случае ошибки завершения на «server2» выполнение будет следующим: «запуск на server1: OK» «запуск на server2: ОШИБКА, прервана» «запуск на server4 …» Здорово, что он пропускает ‘server3’, но все равно продолжает ‘server4’ — это моя проблема.