Better exit codes and error messages for auto command
This commit is contained in:
@@ -12,6 +12,15 @@ use Symfony\Component\Yaml\Exception\ParseException;
|
|||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class Auto implements Command {
|
class Auto implements Command {
|
||||||
|
const EXIT_CONFIG_ERROR = 1;
|
||||||
|
const EXIT_SETUP_ERROR = 2;
|
||||||
|
const EXIT_ISSUANCE_ERROR = 3;
|
||||||
|
const EXIT_ISSUANCE_PARTIAL = 4;
|
||||||
|
const EXIT_ISSUANCE_OK = 5;
|
||||||
|
|
||||||
|
const STATUS_NO_CHANGE = 0;
|
||||||
|
const STATUS_RENEWED = 1;
|
||||||
|
|
||||||
private $climate;
|
private $climate;
|
||||||
|
|
||||||
public function __construct(CLImate $climate) {
|
public function __construct(CLImate $climate) {
|
||||||
@@ -37,23 +46,23 @@ class Auto implements Command {
|
|||||||
);
|
);
|
||||||
} catch (FilesystemException $e) {
|
} catch (FilesystemException $e) {
|
||||||
$this->climate->error("Config file ({$configPath}) not found.");
|
$this->climate->error("Config file ({$configPath}) not found.");
|
||||||
yield new CoroutineResult(1);
|
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||||
return;
|
return;
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
$this->climate->error("Config file ({$configPath}) had an invalid format and couldn't be parsed.");
|
$this->climate->error("Config file ({$configPath}) had an invalid format and couldn't be parsed.");
|
||||||
yield new CoroutineResult(1);
|
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($config["email"])) {
|
if (!isset($config["email"])) {
|
||||||
$this->climate->error("Config file ({$configPath}) didn't have a 'email' set.");
|
$this->climate->error("Config file ({$configPath}) didn't have a 'email' set.");
|
||||||
yield new CoroutineResult(2);
|
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($config["certificates"]) || !is_array($config["certificates"])) {
|
if (!isset($config["certificates"]) || !is_array($config["certificates"])) {
|
||||||
$this->climate->error("Config file ({$configPath}) didn't have a 'certificates' section that's an array.");
|
$this->climate->error("Config file ({$configPath}) didn't have a 'certificates' section that's an array.");
|
||||||
yield new CoroutineResult(2);
|
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,13 +86,14 @@ class Auto implements Command {
|
|||||||
$this->climate->error($command);
|
$this->climate->error($command);
|
||||||
$this->climate->br()->out($result->out);
|
$this->climate->br()->out($result->out);
|
||||||
$this->climate->br()->error($result->err);
|
$this->climate->br()->error($result->err);
|
||||||
yield new CoroutineResult(3);
|
yield new CoroutineResult(self::EXIT_SETUP_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$certificateChunks = array_chunk($config["certificates"], 10);
|
$certificateChunks = array_chunk($config["certificates"], 10, true);
|
||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
$values = [];
|
||||||
|
|
||||||
foreach ($certificateChunks as $certificateChunk) {
|
foreach ($certificateChunks as $certificateChunk) {
|
||||||
$promises = [];
|
$promises = [];
|
||||||
@@ -92,18 +102,30 @@ class Auto implements Command {
|
|||||||
$promises[] = \Amp\resolve($this->checkAndIssue($certificate, $server, $storage));
|
$promises[] = \Amp\resolve($this->checkAndIssue($certificate, $server, $storage));
|
||||||
}
|
}
|
||||||
|
|
||||||
list($errors) = (yield \Amp\any($promises));
|
list($chunkErrors, $chunkValues) = (yield \Amp\any($promises));
|
||||||
$errors = array_merge($errors, $errors);
|
|
||||||
|
$errors += $chunkErrors;
|
||||||
|
$values += $chunkValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($errors)) {
|
$status = [
|
||||||
|
"no_change" => count(array_filter($values, function($value) { return $value === self::STATUS_NO_CHANGE; })),
|
||||||
|
"renewed" => count(array_filter($values, function($value) { return $value === self::STATUS_RENEWED; })),
|
||||||
|
"failure" => count($errors),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($status["failure"] > 0) {
|
||||||
foreach ($errors as $i => $error) {
|
foreach ($errors as $i => $error) {
|
||||||
$certificate = $config["certificates"][$i];
|
$certificate = $config["certificates"][$i];
|
||||||
$this->climate->error("Issuance for the following domains failed: " . implode(", ", array_keys($this->toDomainPathMap($certificate["paths"]))));
|
$this->climate->error("Issuance for the following domains failed: " . implode(", ", array_keys($this->toDomainPathMap($certificate["paths"]))));
|
||||||
$this->climate->error("Reason: {$error}");
|
$this->climate->error("Reason: {$error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
yield new CoroutineResult(3);
|
$exitCode = $status["renewed"] > 0
|
||||||
|
? self::EXIT_ISSUANCE_PARTIAL
|
||||||
|
: self::EXIT_ISSUANCE_ERROR;
|
||||||
|
|
||||||
|
yield new CoroutineResult($exitCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,10 +156,11 @@ class Auto implements Command {
|
|||||||
$command = implode(" ", array_map("escapeshellarg", $args));
|
$command = implode(" ", array_map("escapeshellarg", $args));
|
||||||
|
|
||||||
$process = new Process($command);
|
$process = new Process($command);
|
||||||
$result = (yield $process->exec());
|
$result = (yield $process->exec(Process::BUFFER_ALL));
|
||||||
|
|
||||||
if ($result->exit === 0) {
|
if ($result->exit === 0) {
|
||||||
// No need for renewal
|
// No need for renewal
|
||||||
|
yield new CoroutineResult(self::STATUS_NO_CHANGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,16 +193,17 @@ class Auto implements Command {
|
|||||||
$command = implode(" ", array_map("escapeshellarg", $args));
|
$command = implode(" ", array_map("escapeshellarg", $args));
|
||||||
|
|
||||||
$process = new Process($command);
|
$process = new Process($command);
|
||||||
$result = (yield $process->exec());
|
$result = (yield $process->exec(Process::BUFFER_ALL));
|
||||||
|
|
||||||
if ($result->exit !== 0) {
|
if ($result->exit !== 0) {
|
||||||
throw new AcmeException("Unexpected exit code ({$result->exit}) for '{$command}'.");
|
throw new AcmeException("Unexpected exit code ({$result->exit}) for '{$command}'." . PHP_EOL . $result->out . PHP_EOL . PHP_EOL . $result->err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield new CoroutineResult(self::STATUS_RENEWED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AcmeException("Unexpected exit code ({$result->exit}) for '{$command}'.");
|
throw new AcmeException("Unexpected exit code ({$result->exit}) for '{$command}'." . PHP_EOL . $result->out . PHP_EOL . PHP_EOL . $result->err);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function toDomainPathMap(array $paths) {
|
private function toDomainPathMap(array $paths) {
|
||||||
|
|||||||
Reference in New Issue
Block a user