From 97dbdec7f72c3c30cb492ce49d3347d9312ee9e1 Mon Sep 17 00:00:00 2001 From: Antonio Gurgel Date: Sun, 3 Dec 2023 19:53:14 -0800 Subject: [PATCH] Re-implement service-builder `apiVersions` gave me a headache while writing the foldl so I made it a separate input. Now the service modules have this signature, without my having to change any code in the modules themselves: {charts, etc, ...} -> [apiVersion] -> {out, extra} --- flake.nix | 10 ++------ lib/builders.nix | 4 +++ lib/default.nix | 47 ++++++++++++++++++++++++++++------ src/default.nix | 40 ++++++++++++++++++++--------- src/flake-builders.nix | 58 ++++++++++++++++++++++++------------------ 5 files changed, 106 insertions(+), 53 deletions(-) diff --git a/flake.nix b/flake.nix index 81680f9..64111de 100644 --- a/flake.nix +++ b/flake.nix @@ -36,15 +36,9 @@ in { packages = { inherit charts lib; - default = { - # TODO: move to default.nix - charts ? charts, - lib ? lib, - pkgs ? pkgs, - user ? {}, - }: + default = user: import ./src { - inherit charts lib pkgs user; + inherit charts haumea lib pkgs user; }; }; # TODO: make a template diff --git a/lib/builders.nix b/lib/builders.nix index 6304575..4d407ca 100644 --- a/lib/builders.nix +++ b/lib/builders.nix @@ -38,6 +38,10 @@ in rec { # Some charts lack diligence in setting `Release.Namespace` # on the objects they create, necessitating a round-trip through # yamlStream, which sets namespaces on all objects lacking one. + + # BUG: However, this creates a derivation with no knowledge of + # the Helm chart derivation that is its input, which would be + # useful to debug with. in yamlStream {inherit name namespace objs;}; diff --git a/lib/default.nix b/lib/default.nix index 779d143..6dc25ff 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -11,6 +11,13 @@ parseYAMLsFile = p: kubelib.fromYAML (builtins.readFile p); parseYAMLFile = p: builtins.head (parseYAMLsFile p); + liftDefault = _: mod: + # h.lib.transformers.liftDefault uses pkgs.lib.attrsets.unionOfDisjoint, + # which fails here because mod.default is a function, not a set. + if mod ? default + then mod.default + else mod; + # Helm cannot see my cluster from within the sandbox, so it cannot # infer capabilities from it. Therefore, API versions must be gathered # from charts that provide them. @@ -60,15 +67,39 @@ # and remove this transformer }; - mkStages = map (src: + mkSvcTrees = src: haumea.lib.load { inherit src; loader = haumea.lib.loaders.verbatim; - transformer = _: mod: - # transformers.liftDefault uses pkgs.lib.attrsets.unionOfDisjoint, - # which fails here because mod.default is a function, not a set. - if mod ? default - then mod.default - else mod; - }); + transformer = liftDefault; + }; + + # Borrowed from divnix/digga, with minor modifications: + # https://github.com/divnix/digga/blob/baa54f8/src/importers.nix#L2-L59 + flattenTree = tree: let + op = sum: path: val: let + pathStr = builtins.concatStringsSep "/" path; + in + if builtins.isFunction val + then + # builtins.trace "${toString val} is a path" + (sum + // { + "${pathStr}" = val; + }) + else if builtins.isAttrs val + then + # builtins.trace "${builtins.toJSON val} is an attrset" + (recurse sum path val) + else + # builtins.trace "${toString path} is something else" + sum; # ignore val + + recurse = sum: path: val: + builtins.foldl' + (sum: key: op sum (path ++ [key]) val.${key}) + sum + (builtins.attrNames val); + in + recurse {} [] tree; } diff --git a/src/default.nix b/src/default.nix index 87c220f..64e4d27 100644 --- a/src/default.nix +++ b/src/default.nix @@ -1,20 +1,40 @@ { charts, + haumea, lib, pkgs, user ? {}, }: let - mkNamespace = name: extraMetadata: { - }; - flakeBuilders = (import ./flake-builders.nix) {inherit charts lib pkgs user;}; - - buildServices = roots: kubeVersion: {}; in { inherit flakeBuilders; # TODO: remove after debugging inherit (lib) mkCharts; + mkServices = src: kubeVersion: let + modules = haumea.lib.load { + inherit src; + inputs = {inherit charts lib pkgs user kubeVersion;}; + loader = flakeBuilders.serviceLoader; + transformer = lib.liftDefault; + }; + folder = acc: path: mod: let + module = mod acc.apis; + outPath = "${module.srcPath}/SERVICE.yaml:${module.out.outPath}"; + outPaths = + if module ? extra + then [outPath "${module.srcPath}/EXTRA.yaml:${module.extra.outPath}"] + else [outPath]; + in { + apis = acc.apis ++ (lib.gatherApis module.out.outPath); + paths = acc.paths ++ outPaths; + }; + in + pkgs.lib.attrsets.foldlAttrs + folder + { apis = []; paths = []; } + (lib.flattenTree modules); + mkNamespaces = { roots, extraMetadata ? {}, @@ -48,15 +68,11 @@ in { namespaceData, kubeVersion, src, - }: let - builtServices = buildServices serviceData kubeVersion; - in + }: pkgs.stdenv.mkDerivation { inherit pname version src; - derivation_paths = flakeBuilders.paths { - services = builtServices; - }; - namespace_drv = flakeBuilders.namespaces namespaceData; + derivation_paths = []; # TODO + namespace_drv = []; # TODO: phases = ["installPhase"]; installPhase = builtins.readFile ./src/output.sh; diff --git a/src/flake-builders.nix b/src/flake-builders.nix index caf156a..71d5fb5 100644 --- a/src/flake-builders.nix +++ b/src/flake-builders.nix @@ -10,32 +10,40 @@ mapAttrs = builtins.mapAttrs; in { serviceLoader = { - inputs, # {charts, lib, pkgs, user, kubeVersion, apiVersions} + charts, + lib, + pkgs, + user, + kubeVersion, ... - }: p: let + } @ inputs: p: let module = import p inputs; - name = builtins.baseNameOf p; - namespace = builtins.baseNameOf (builtins.dirOf p); - in { - out = module.builder (module.args - // { - # By injecting `name` and `namespace` here, I remove - # the need to specify it in every service in the flake; - # the directory structure can speak for me instead. - inherit name namespace; - - # Values understood by lib.builders.helmChart - # and ignored by the rest. - inherit (inputs) kubeVersion apiVersions; - }); - - extra = - if module ? extraObjects - then - lib.builders.yamlStream { + # p is `.../${ns}/${name}/default.nix` + name = with builtins; baseNameOf (dirOf p); + namespace = with builtins; baseNameOf (dirOf (dirOf p)); + in + apiVersions: { + out = module.builder (module.args + // { + # By injecting `name` and `namespace` here, I remove + # the need to specify it in every service in the flake; + # the directory structure can speak for me instead. inherit name namespace; - objs = module.extraObjects; - } - else null; - }; + + # Values understood by lib.builders.helmChart + # and ignored by the rest. + inherit (inputs) kubeVersion; + }); + + extra = + if module ? extraObjects + then + lib.builders.yamlStream { + inherit name namespace; + objs = module.extraObjects; + } + else null; + + srcPath = builtins.dirOf p; + }; }