From 9076f68f7b0b346bc2656d08ff2d66005803e473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Tue, 12 Nov 2019 20:17:28 +0100 Subject: [PATCH] rpmmd: Add error reporting support to dnf-json Previously dnf-json hasn't been able to report any reports. This commit tries to fix it with introduction of somewhat flexible error format. --- dnf-json | 24 ++++++++++++++++++++++-- internal/rpmmd/repository.go | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/dnf-json b/dnf-json index 7e043b317..3f649119b 100644 --- a/dnf-json +++ b/dnf-json @@ -5,6 +5,8 @@ import dnf import json import sys +DNF_ERROR_EXIT_CODE = 10 + def timestamp_to_rfc3339(timestamp): d = datetime.datetime.utcfromtimestamp(package.buildtime) @@ -39,6 +41,11 @@ def create_base(repos): return base +def exit_with_dnf_error(kind: str, reason: str): + json.dump({"kind": kind, "reason": reason}, sys.stdout) + sys.exit(DNF_ERROR_EXIT_CODE) + + call = json.load(sys.stdin) command = call["command"] arguments = call.get("arguments", {}) @@ -63,8 +70,21 @@ if command == "dump": elif command == "depsolve": base = create_base(arguments.get("repos", {})) - base.install_specs(arguments["package-specs"]) - base.resolve() + errors = [] + for pkgspec in arguments["package-specs"]: + try: + base.install_specs([pkgspec]) + except dnf.exceptions.MarkingError as e: + errors.append((pkgspec, str(e))) + if errors: + formatted_errors = ", ".join((f"{package} ({err})" for package, err in errors)) + exit_with_dnf_error("MarkingError", f"The following package(s) had problems: {formatted_errors}") + + try: + base.resolve() + except dnf.exceptions.DepsolveError as e: + exit_with_dnf_error("DepsolveError", f"There was a problem depsolving {arguments['package-specs']}: {e}") + packages = [] for package in base.transaction.install_set: packages.append({ diff --git a/internal/rpmmd/repository.go b/internal/rpmmd/repository.go index 5f5f3690d..291924c91 100644 --- a/internal/rpmmd/repository.go +++ b/internal/rpmmd/repository.go @@ -2,6 +2,8 @@ package rpmmd import ( "encoding/json" + "fmt" + "io/ioutil" "os" "os/exec" "sort" @@ -44,6 +46,15 @@ type RPMMD interface { Depsolve(specs []string, repos []RepoConfig) ([]PackageSpec, error) } +type DNFError struct { + Kind string `json:"kind"` + Reason string `json:"reason"` +} + +func (err *DNFError) Error() string { + return fmt.Sprintf("DNF error occured: %s: %s", err.Kind, err.Reason) +} + func runDNF(command string, arguments interface{}, result interface{}) error { var call = struct { Command string `json:"command"` @@ -77,12 +88,31 @@ func runDNF(command string, arguments interface{}, result interface{}) error { } stdin.Close() - err = json.NewDecoder(stdout).Decode(result) + output, err := ioutil.ReadAll(stdout) if err != nil { return err } - return cmd.Wait() + err = cmd.Wait() + + const DnfErrorExitCode = 10 + if runError, ok := err.(*exec.ExitError); ok && runError.ExitCode() == DnfErrorExitCode { + var dnfError DNFError + err = json.Unmarshal(output, &dnfError) + if err != nil { + return err + } + + return &dnfError + } + + err = json.Unmarshal(output, result) + + if err != nil { + return err + } + + return nil } type rpmmdImpl struct{}