diff --git a/default.nix b/default.nix index b36da1a..d0f4ebe 100644 --- a/default.nix +++ b/default.nix @@ -22,8 +22,8 @@ let , ... }@attrs: let - lib' = lib.extend (lib: self: import ./lib/extra.nix { inherit lib pkgs; }); - attrs' = lib.filterAttrs (n: _: n != "module") attrs; + lib' = lib.extend (lib: self: import ./lib/upstreamables.nix { inherit lib pkgs; }); + attrs' = builtins.removeAttrs attrs [ "module" ]; in lib'.evalModules (lib.recursiveUpdate { diff --git a/lib/extra.nix b/lib/extra.nix deleted file mode 100644 index ec3f68a..0000000 --- a/lib/extra.nix +++ /dev/null @@ -1,142 +0,0 @@ -{ lib, pkgs }: - -with lib; - -rec { - moduleToAttrs = value: - if isAttrs value - then mapAttrs (n: v: moduleToAttrs v) (filterAttrs (n: v: !(hasPrefix "_" n) && v != null) value) - - else if isList value - then map (v: moduleToAttrs v) value - - else value; - - mkAllDefault = value: priority: - if isAttrs value - then mapAttrs (n: v: mkAllDefault v priority) value - - else if isList value - then map (v: mkAllDefault v priority) value - - else mkOverride priority value; - - loadYAML = path: importJSON (pkgs.runCommand "yaml-to-json" - { } "${pkgs.remarshal}/bin/remarshal -i ${path} -if yaml -of json > $out"); - - toYAML = config: builtins.readFile (pkgs.runCommand "to-yaml" - { - buildInputs = [ pkgs.remarshal ]; - } '' - remarshal -i ${pkgs.writeText "to-json" (builtins.toJSON config)} -if json -of yaml > $out - ''); - - toMultiDocumentYaml = name: documents: pkgs.runCommand name - { - buildInputs = [ pkgs.remarshal ]; - } - (concatMapStringsSep "\necho --- >> $out\n" - (d: - "remarshal -i ${builtins.toFile "doc" (builtins.toJSON d)} -if json -of yaml >> $out" - ) - documents); - - toBase64 = value: - builtins.readFile - (pkgs.runCommand "value-to-b64" { } "echo -n '${value}' | ${pkgs.coreutils}/bin/base64 -w0 > $out"); - - exp = base: exp: foldr (value: acc: acc * base) 1 (range 1 exp); - - octalToDecimal = value: (foldr - (char: acc: { - i = acc.i + 1; - value = acc.value + (toInt char) * (exp 8 acc.i); - }) - { i = 0; value = 0; } - (stringToCharacters value)).value; - - submoduleWithSpecialArgs = opts: specialArgs: - let - opts' = toList opts; - inherit (lib.modules) evalModules; - in - mkOptionType rec { - name = "submodule"; - check = x: isAttrs x || isFunction x; - merge = loc: defs: - let - coerce = def: if isFunction def then def else { config = def; }; - modules = opts' ++ map (def: { _file = def.file; imports = [ (coerce def.value) ]; }) defs; - in - (evalModules { - inherit modules specialArgs; - prefix = loc; - }).config; - getSubOptions = prefix: (evalModules - { - modules = opts'; inherit prefix specialArgs; - # This is a work-around due to the fact that some sub-modules, - # such as the one included in an attribute set, expects a "args" - # attribute to be given to the sub-module. As the option - # evaluation does not have any specific attribute name, we - # provide a default one for the documentation. - # - # This is mandatory as some option declaration might use the - # "name" attribute given as argument of the submodule and use it - # as the default of option declarations. - # - # Using lookalike unicode single angle quotation marks because - # of the docbook transformation the options receive. In all uses - # > and < wouldn't be encoded correctly so the encoded values - # would be used, and use of `<` and `>` would break the XML document. - # It shouldn't cause an issue since this is cosmetic for the manual. - args.name = "‹name›"; - }).options; - getSubModules = opts'; - substSubModules = m: submoduleWithSpecialArgs m specialArgs; - functor = (defaultFunctor name) // { - # Merging of submodules is done as part of mergeOptionDecls, as we have to annotate - # each submodule with its location. - payload = [ ]; - binOp = lhs: rhs: [ ]; - }; - }; - - coerceListOfSubmodulesToAttrs = submodule: keyFn: - let - mergeValuesByFn = keyFn: values: - listToAttrs (map - (value: - nameValuePair (toString (keyFn value)) value - ) - values); - - # Either value of type `finalType` or `coercedType`, the latter is - # converted to `finalType` using `coerceFunc`. - coercedTo = coercedType: coerceFunc: finalType: - mkOptionType rec { - name = "coercedTo"; - description = "${finalType.description} or ${coercedType.description}"; - check = x: finalType.check x || coercedType.check x; - merge = loc: defs: - let - coerceVal = val: - if finalType.check val then - val - else - let coerced = coerceFunc val; in assert finalType.check coerced; coerced; - - in - finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); - getSubOptions = finalType.getSubOptions; - getSubModules = finalType.getSubModules; - substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); - typeMerge = t1: t2: null; - functor = (defaultFunctor name) // { wrapped = finalType; }; - }; - in - coercedTo - (types.listOf (types.submodule submodule)) - (mergeValuesByFn keyFn) - (types.attrsOf (types.submodule submodule)); -} diff --git a/lib/upstreamables.nix b/lib/upstreamables.nix new file mode 100644 index 0000000..d08c93f --- /dev/null +++ b/lib/upstreamables.nix @@ -0,0 +1,36 @@ +{ lib, pkgs }: + +with lib; + +let self = { + + importYAML = path: importJSON (pkgs.runCommand "yaml-to-json" { } '' + ${pkgs.remarshal}/bin/remarshal -i ${path} -if yaml -of json > $out + ''); + + toYAML = config: builtins.readFile (pkgs.runCommand "to-yaml" { } '' + ${pkgs.remarshal}/bin/remarshal -i ${pkgs.writeText "to-json" (builtins.toJSON config)} -if json -of yaml > $out + ''); + + toMultiDocumentYaml = name: documents: pkgs.runCommand name { } + (concatMapStringsSep "\necho --- >> $out\n" + (d: + "${pkgs.remarshal}/bin/remarshal -i ${builtins.toFile "doc" (builtins.toJSON d)} -if json -of yaml >> $out" + ) + documents); + + toBase64 = value: + builtins.readFile + (pkgs.runCommand "value-to-b64" { } "echo -n '${value}' | ${pkgs.coreutils}/bin/base64 -w0 > $out"); + + exp = base: exp: foldr (value: acc: acc * base) 1 (range 1 exp); + + octalToDecimal = value: (foldr + (char: acc: { + i = acc.i + 1; + value = acc.value + (toInt char) * (self.exp 8 acc.i); + }) + { i = 0; value = 0; } + (stringToCharacters value)).value; +}; +in self diff --git a/modules/k8s.nix b/modules/k8s.nix index 9bfa0b0..8d4d630 100644 --- a/modules/k8s.nix +++ b/modules/k8s.nix @@ -214,6 +214,44 @@ let }) latestCustomResourceTypes); + coerceListOfSubmodulesToAttrs = submodule: keyFn: + let + mergeValuesByFn = keyFn: values: + listToAttrs (map + (value: + nameValuePair (toString (keyFn value)) value + ) + values); + + # Either value of type `finalType` or `coercedType`, the latter is + # converted to `finalType` using `coerceFunc`. + coercedTo = coercedType: coerceFunc: finalType: + mkOptionType rec { + name = "coercedTo"; + description = "${finalType.description} or ${coercedType.description}"; + check = x: finalType.check x || coercedType.check x; + merge = loc: defs: + let + coerceVal = val: + if finalType.check val then + val + else + let coerced = coerceFunc val; in assert finalType.check coerced; coerced; + + in + finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); + getSubOptions = finalType.getSubOptions; + getSubModules = finalType.getSubModules; + substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); + typeMerge = t1: t2: null; + functor = (defaultFunctor name) // { wrapped = finalType; }; + }; + in + coercedTo + (types.listOf (types.submodule submodule)) + (mergeValuesByFn keyFn) + (types.attrsOf (types.submodule submodule)); + in { imports = [ ./base.nix ]; @@ -404,7 +442,7 @@ in (i: let # load yaml file - object = loadYAML i; + object = importYAML i; groupVersion = splitString "/" object.apiVersion; name = object.metadata.name; version = last groupVersion; diff --git a/modules/submodule.nix b/modules/submodule.nix index 2ae82bb..77c609c 100644 --- a/modules/submodule.nix +++ b/modules/submodule.nix @@ -1,7 +1,6 @@ { config, lib, ... }: with lib; - { imports = [ ./base.nix ]; diff --git a/modules/submodules.nix b/modules/submodules.nix index 1b66972..08b060b 100644 --- a/modules/submodules.nix +++ b/modules/submodules.nix @@ -74,6 +74,53 @@ let config.submodules.instances); }) (removeAttrs options [ "_definedNames" "_module" "_m" "submodules" ]); + + submoduleWithSpecialArgs = opts: specialArgs: + let + opts' = toList opts; + inherit (lib.modules) evalModules; + in + mkOptionType rec { + name = "submodule"; + check = x: isAttrs x || isFunction x; + merge = loc: defs: + let + coerce = def: if isFunction def then def else { config = def; }; + modules = opts' ++ map (def: { _file = def.file; imports = [ (coerce def.value) ]; }) defs; + in + (evalModules { + inherit modules specialArgs; + prefix = loc; + }).config; + getSubOptions = prefix: (evalModules + { + modules = opts'; inherit prefix specialArgs; + # This is a work-around due to the fact that some sub-modules, + # such as the one included in an attribute set, expects a "args" + # attribute to be given to the sub-module. As the option + # evaluation does not have any specific attribute name, we + # provide a default one for the documentation. + # + # This is mandatory as some option declaration might use the + # "name" attribute given as argument of the submodule and use it + # as the default of option declarations. + # + # Using lookalike unicode single angle quotation marks because + # of the docbook transformation the options receive. In all uses + # > and < wouldn't be encoded correctly so the encoded values + # would be used, and use of `<` and `>` would break the XML document. + # It shouldn't cause an issue since this is cosmetic for the manual. + args.name = "‹name›"; + }).options; + getSubModules = opts'; + substSubModules = m: submoduleWithSpecialArgs m specialArgs; + functor = (defaultFunctor name) // { + # Merging of submodules is done as part of mergeOptionDecls, as we have to annotate + # each submodule with its location. + payload = [ ]; + binOp = lhs: rhs: [ ]; + }; + }; in { imports = [ ./base.nix ];