1
0
Fork 0
mirror of https://git.sr.ht/~goorzhel/turboprop synced 2024-12-14 11:37:37 +00:00

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}
This commit is contained in:
Antonio Gurgel 2023-12-03 19:53:14 -08:00
parent 6b97777fe9
commit 97dbdec7f7
5 changed files with 106 additions and 53 deletions

View file

@ -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

View file

@ -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;};

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
};
}