kubenix/modules/submodules.nix

315 lines
9.3 KiB
Nix
Raw Normal View History

{ config, options, kubenix, pkgs, lib, ... }:
with lib;
let
cfg = config.submodules;
parentConfig = config;
matchesVersion = requiredVersion: version:
if requiredVersion != null then
if hasPrefix "~" requiredVersion
then (builtins.match (removePrefix "~" requiredVersion) version) != null
else requiredVersion == version
else true;
2021-05-13 17:27:08 -04:00
getDefaults = { name, version, tags, features }:
catAttrs "default" (filter
(submoduleDefault:
(submoduleDefault.name == null || submoduleDefault.name == name) &&
(matchesVersion submoduleDefault.version version) &&
(
(length submoduleDefault.tags == 0) ||
(length (intersectLists submoduleDefault.tags tags)) > 0
) &&
(
(length submoduleDefault.features == 0) ||
(length (intersectLists submoduleDefault.features features)) > 0
)
2019-02-26 21:23:14 +01:00
)
2021-05-13 17:27:08 -04:00
config.submodules.defaults);
2019-02-26 21:23:14 +01:00
specialArgs = cfg.specialArgs // {
parentConfig = config;
};
2021-05-13 17:27:08 -04:00
findSubmodule = { name, version ? null, latest ? true }:
let
matchingSubmodules = filter
(el:
el.definition.name == name &&
(matchesVersion version el.definition.version)
)
cfg.imports;
versionSortedSubmodules = sort
(s1: s2:
if builtins.compareVersions s1.definition.version s2.definition.version > 0
then true else false
)
matchingSubmodules;
matchingModule =
if length versionSortedSubmodules == 0
then throw "No module found ${name}/${if version == null then "latest" else version}"
else head versionSortedSubmodules;
in
matchingModule;
passthruConfig = mapAttrsToList
(name: opt: {
${name} = mkMerge (mapAttrsToList
(_: inst:
if inst.passthru.enable
then inst.config.submodule.passthru.${name} or { }
else { }
)
config.submodules.instances);
_module.args = mkMerge (mapAttrsToList
(_: inst:
if inst.passthru.enable
then inst.config.submodule.passthru._module.args or { }
else { }
)
config.submodules.instances);
})
(removeAttrs options [ "_definedNames" "_module" "_m" "submodules" ]);
in
{
imports = [ ./base.nix ];
options = {
submodules.specialArgs = mkOption {
description = "Special args to pass to submodules. These arguments can be used for imports";
type = types.attrs;
2021-05-13 17:27:08 -04:00
default = { };
};
submodules.defaults = mkOption {
2019-02-26 21:23:14 +01:00
description = "List of defaults to apply to submodule instances";
2021-05-13 17:27:08 -04:00
type = types.listOf (types.submodule ({ config, ... }: {
2019-02-26 21:23:14 +01:00
options = {
name = mkOption {
description = "Name of the submodule to apply defaults for";
type = types.nullOr types.str;
default = null;
};
version = mkOption {
description = ''
Version of submodule to apply defaults for. If version starts with
"~" it is threated as regex pattern for example "~1.0.*
'';
2019-02-26 21:23:14 +01:00
type = types.nullOr types.str;
default = null;
};
2019-02-26 21:23:14 +01:00
tags = mkOption {
description = "List of tags to apply defaults for";
2019-02-26 21:23:14 +01:00
type = types.listOf types.str;
2021-05-13 17:27:08 -04:00
default = [ ];
2019-02-26 21:23:14 +01:00
};
features = mkOption {
description = "List of features that submodule has to have to apply defaults";
type = types.listOf types.str;
2021-05-13 17:27:08 -04:00
default = [ ];
};
2019-02-26 21:23:14 +01:00
default = mkOption {
description = "Default to apply to submodule instance";
type = types.unspecified;
2021-05-13 17:27:08 -04:00
default = { };
2019-02-26 21:23:14 +01:00
};
};
}));
2021-05-13 17:27:08 -04:00
default = [ ];
};
submodules.propagate.enable = mkOption {
description = "Whether to propagate defaults and imports from parent to child";
type = types.bool;
default = true;
};
submodules.imports = mkOption {
description = "List of submodule imports";
type = types.listOf (
types.coercedTo
types.path
2021-05-13 17:27:08 -04:00
(module: { inherit module; })
(types.submodule ({ name, config, ... }:
let
evaledSubmodule' = evalModules {
inherit specialArgs;
modules = config.modules ++ [ ./base.nix ];
check = false;
};
2021-05-13 17:27:08 -04:00
evaledSubmodule =
if (!(elem "submodule" evaledSubmodule'.config._m.features))
then throw "no submodule defined"
else evaledSubmodule';
in
{
options = {
module = mkOption {
description = "Module defining submodule";
type = types.unspecified;
};
modules = mkOption {
description = "List of modules defining submodule";
type = types.listOf types.unspecified;
default = [ config.module ];
};
features = mkOption {
description = "List of features exposed by submodule";
type = types.listOf types.str;
};
definition = mkOption {
description = "Submodule definition";
type = types.attrs;
};
exportAs = mkOption {
description = "Name under which to register exports";
type = types.nullOr types.str;
default = null;
};
};
2021-05-13 17:27:08 -04:00
config = {
definition = {
inherit (evaledSubmodule.config.submodule) name description version tags exports;
};
2021-05-13 17:27:08 -04:00
features = evaledSubmodule.config._m.features;
};
2021-05-13 17:27:08 -04:00
})
)
);
2021-05-13 17:27:08 -04:00
default = [ ];
};
submodules.instances = mkOption {
description = "Attribute set of submodule instances";
2021-05-13 17:27:08 -04:00
default = { };
type = types.attrsOf (types.submodule ({ name, config, options, ... }:
let
# submodule associated with
submodule = findSubmodule {
name = config.submodule;
version = config.version;
};
2021-05-13 17:27:08 -04:00
# definition of a submodule
submoduleDefinition = submodule.definition;
2019-02-26 21:23:14 +01:00
2021-05-13 17:27:08 -04:00
# submodule defaults
defaults = getDefaults {
name = submoduleDefinition.name;
version = submoduleDefinition.version;
tags = submoduleDefinition.tags;
features = submodule.features;
};
2021-05-13 17:27:08 -04:00
in
{
options = {
name = mkOption {
description = "Submodule instance name";
type = types.str;
default = name;
};
2021-05-13 17:27:08 -04:00
submodule = mkOption {
description = "Name of the submodule to use";
type = types.str;
default = name;
};
2021-05-13 17:27:08 -04:00
version = mkOption {
description = ''
Version of submodule to use, if version starts with "~" it is
threated as regex pattern for example "~1.0.*"
'';
type = types.nullOr types.str;
default = null;
};
2021-05-13 17:27:08 -04:00
passthru.enable = mkOption {
description = "Whether to passthru submodule resources";
type = types.bool;
default = true;
};
2021-05-13 17:27:08 -04:00
config = mkOption {
description = "Submodule instance ${config.name} for ${submoduleDefinition.name}:${submoduleDefinition.version} config";
type = submoduleWithSpecialArgs
({ ... }: {
imports = submodule.modules ++ defaults ++ [ ./base.nix ];
_module.args.pkgs = pkgs;
_module.args.name = config.name;
_module.args.submodule = config;
submodule.args = mkAliasDefinitions options.args;
})
specialArgs;
default = { };
};
2021-05-13 17:27:08 -04:00
args = mkOption {
description = "Submodule arguments (alias of config.submodule.args)";
};
};
2021-05-13 17:27:08 -04:00
}));
};
2021-05-13 17:27:08 -04:00
default = { };
};
config = mkMerge ([
{
# register exported functions as args
2021-05-13 17:27:08 -04:00
_module.args = mkMerge (map
(submodule: {
${submodule.exportAs} = submodule.definition.exports;
})
(filter (submodule: submodule.exportAs != null) cfg.imports));
2021-05-13 17:27:08 -04:00
_m.features = [ "submodules" ];
submodules.specialArgs.kubenix = kubenix;
# passthru kubenix.project to submodules
submodules.defaults = mkMerge [
[{
default = {
kubenix.project = parentConfig.kubenix.project;
};
}]
2021-05-13 17:27:08 -04:00
(map
(propagate: {
features = propagate.features;
default = propagate.module;
})
config._m.propagate)
];
}
(mkIf cfg.propagate.enable {
# if propagate is enabled and submodule has submodules included propagage defaults and imports
submodules.defaults = [{
2021-05-13 17:27:08 -04:00
features = [ "submodules" ];
default = {
submodules = {
defaults = cfg.defaults;
imports = cfg.imports;
};
};
}];
})
] ++ passthruConfig);
}