feat(nix): Return structured errors if packages are not found

Changes the return format of Nixery's build procedure to return a JSON
structure that can indicate which errors have occured.

The server can use this information to send appropriate status codes
back to clients.
This commit is contained in:
Vincent Ambo 2019-07-31 21:23:44 +01:00 committed by Vincent Ambo
parent ec8e9eed5d
commit 3070d88051

View file

@ -99,6 +99,7 @@ in
# Since this is essentially a re-wrapping of some of the functionality that is # Since this is essentially a re-wrapping of some of the functionality that is
# implemented in the dockerTools, we need all of its components in our top-level # implemented in the dockerTools, we need all of its components in our top-level
# namespace. # namespace.
with builtins;
with pkgs; with pkgs;
with dockerTools; with dockerTools;
@ -115,16 +116,29 @@ let
# For example, `deepFetch pkgs "xorg.xev"` retrieves `pkgs.xorg.xev`. # For example, `deepFetch pkgs "xorg.xev"` retrieves `pkgs.xorg.xev`.
deepFetch = s: n: deepFetch = s: n:
let path = lib.strings.splitString "." n; let path = lib.strings.splitString "." n;
err = builtins.throw "Could not find '${n}' in package set"; err = { error = "not_found"; pkg = n; };
in lib.attrsets.attrByPath path err s; in lib.attrsets.attrByPath path err s;
# allContents is the combination of all derivations and store paths passed in # allContents is the combination of all derivations and store paths passed in
# directly, as well as packages referred to by name. # directly, as well as packages referred to by name.
allContents = contents ++ (map (deepFetch pkgs) (builtins.fromJSON packages)); #
# It accumulates potential errors about packages that could not be found to
# return this information back to the server.
allContents =
# Folds over the results of 'deepFetch' on all requested packages to
# separate them into errors and content. This allows the program to
# terminate early and return only the errors if any are encountered.
let splitter = attrs: res:
if hasAttr "error" res
then attrs // { errors = attrs.errors ++ [ res ]; }
else attrs // { contents = attrs.contents ++ [ res ]; };
init = { inherit contents; errors = []; };
fetched = (map (deepFetch pkgs) (fromJSON packages));
in foldl' splitter init fetched;
contentsEnv = symlinkJoin { contentsEnv = symlinkJoin {
name = "bulk-layers"; name = "bulk-layers";
paths = allContents; paths = allContents.contents;
}; };
# The image build infrastructure expects to be outputting a slightly different # The image build infrastructure expects to be outputting a slightly different
@ -176,7 +190,7 @@ let
cat fs-layers | jq -s -c '.' > $out cat fs-layers | jq -s -c '.' > $out
''; '';
allLayers = builtins.fromJSON (builtins.readFile allLayersJson); allLayers = fromJSON (readFile allLayersJson);
# Image configuration corresponding to the OCI specification for the file type # Image configuration corresponding to the OCI specification for the file type
# 'application/vnd.oci.image.config.v1+json' # 'application/vnd.oci.image.config.v1+json'
@ -188,8 +202,8 @@ let
# Required to let Kubernetes import Nixery images # Required to let Kubernetes import Nixery images
config = {}; config = {};
}; };
configJson = writeText "${baseName}-config.json" (builtins.toJSON config); configJson = writeText "${baseName}-config.json" (toJSON config);
configMetadata = with builtins; fromJSON (readFile (runCommand "config-meta" { configMetadata = fromJSON (readFile (runCommand "config-meta" {
buildInputs = [ jq openssl ]; buildInputs = [ jq openssl ];
} '' } ''
size=$(wc -c ${configJson} | cut -d ' ' -f1) size=$(wc -c ${configJson} | cut -d ' ' -f1)
@ -228,7 +242,7 @@ let
path = configJson; path = configJson;
md5 = configMetadata.md5; md5 = configMetadata.md5;
}; };
} // (builtins.listToAttrs (map (layer: { } // (listToAttrs (map (layer: {
name = "${layer.sha256}"; name = "${layer.sha256}";
value = { value = {
path = layer.path; path = layer.path;
@ -236,6 +250,19 @@ let
}; };
}) allLayers)); }) allLayers));
in writeText "manifest-output.json" (builtins.toJSON { # Final output structure returned to the controller in the case of a
inherit manifest layerLocations; # successful build.
}) manifestOutput = {
inherit manifest layerLocations;
};
# Output structure returned if errors occured during the build. Currently the
# only error type that is returned in a structured way is 'not_found'.
errorOutput = {
error = "not_found";
pkgs = map (err: err.pkg) allContents.errors;
};
in writeText "manifest-output.json" (if (length allContents.errors) == 0
then toJSON (trace manifestOutput manifestOutput)
else toJSON (trace errorOutput errorOutput)
)