mirror of
https://github.com/TECHNOFAB11/kubenix.git
synced 2025-12-12 16:10:05 +01:00
416 lines
12 KiB
Nix
416 lines
12 KiB
Nix
{
|
||
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;
|
||
|
||
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
|
||
)
|
||
)
|
||
config.submodules.defaults);
|
||
|
||
specialArgs =
|
||
cfg.specialArgs
|
||
// {
|
||
parentConfig = config;
|
||
};
|
||
|
||
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"]);
|
||
|
||
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];
|
||
|
||
options = {
|
||
submodules.specialArgs = mkOption {
|
||
description = "Special args to pass to submodules. These arguments can be used for imports";
|
||
type = types.attrs;
|
||
default = {};
|
||
};
|
||
|
||
submodules.defaults = mkOption {
|
||
description = "List of defaults to apply to submodule instances";
|
||
type = types.listOf (types.submodule ({config, ...}: {
|
||
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.*
|
||
'';
|
||
type = types.nullOr types.str;
|
||
default = null;
|
||
};
|
||
|
||
tags = mkOption {
|
||
description = "List of tags to apply defaults for";
|
||
type = types.listOf types.str;
|
||
default = [];
|
||
};
|
||
|
||
features = mkOption {
|
||
description = "List of features that submodule has to have to apply defaults";
|
||
type = types.listOf types.str;
|
||
default = [];
|
||
};
|
||
|
||
default = mkOption {
|
||
description = "Default to apply to submodule instance";
|
||
type = types.unspecified;
|
||
default = {};
|
||
};
|
||
};
|
||
}));
|
||
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
|
||
(module: {inherit module;})
|
||
(
|
||
types.submodule ({
|
||
name,
|
||
config,
|
||
...
|
||
}: let
|
||
evaledSubmodule' = evalModules {
|
||
inherit specialArgs;
|
||
modules = config.modules ++ [./base.nix];
|
||
check = false;
|
||
};
|
||
|
||
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;
|
||
};
|
||
};
|
||
|
||
config = {
|
||
definition = {
|
||
inherit (evaledSubmodule.config.submodule) name description version tags exports;
|
||
};
|
||
|
||
features = evaledSubmodule.config._m.features;
|
||
};
|
||
})
|
||
)
|
||
);
|
||
default = [];
|
||
};
|
||
|
||
submodules.instances = mkOption {
|
||
description = "Attribute set of submodule instances";
|
||
default = {};
|
||
type = types.attrsOf (types.submodule ({
|
||
name,
|
||
config,
|
||
options,
|
||
...
|
||
}: let
|
||
# submodule associated with
|
||
submodule = findSubmodule {
|
||
name = config.submodule;
|
||
version = config.version;
|
||
};
|
||
|
||
# definition of a submodule
|
||
submoduleDefinition = submodule.definition;
|
||
|
||
# submodule defaults
|
||
defaults = getDefaults {
|
||
name = submoduleDefinition.name;
|
||
version = submoduleDefinition.version;
|
||
tags = submoduleDefinition.tags;
|
||
features = submodule.features;
|
||
};
|
||
in {
|
||
options = {
|
||
name = mkOption {
|
||
description = "Submodule instance name";
|
||
type = types.str;
|
||
default = name;
|
||
};
|
||
|
||
submodule = mkOption {
|
||
description = "Name of the submodule to use";
|
||
type = types.str;
|
||
default = name;
|
||
};
|
||
|
||
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;
|
||
};
|
||
|
||
passthru.enable = mkOption {
|
||
description = "Whether to passthru submodule resources";
|
||
type = types.bool;
|
||
default = true;
|
||
};
|
||
|
||
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 = {};
|
||
};
|
||
|
||
args = mkOption {
|
||
description = "Submodule arguments (alias of config.submodule.args)";
|
||
};
|
||
};
|
||
}));
|
||
};
|
||
default = {};
|
||
};
|
||
|
||
config = mkMerge ([
|
||
{
|
||
# register exported functions as args
|
||
_module.args = mkMerge (map
|
||
(submodule: {
|
||
${submodule.exportAs} = submodule.definition.exports;
|
||
})
|
||
(filter (submodule: submodule.exportAs != null) cfg.imports));
|
||
|
||
_m.features = ["submodules"];
|
||
|
||
submodules.specialArgs.kubenix = kubenix;
|
||
|
||
# passthru kubenix.project to submodules
|
||
submodules.defaults = mkMerge [
|
||
[
|
||
{
|
||
default = {
|
||
kubenix.project = parentConfig.kubenix.project;
|
||
};
|
||
}
|
||
]
|
||
|
||
(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 = [
|
||
{
|
||
features = ["submodules"];
|
||
default = {
|
||
submodules = {
|
||
defaults = cfg.defaults;
|
||
imports = cfg.imports;
|
||
};
|
||
};
|
||
}
|
||
];
|
||
})
|
||
]
|
||
++ passthruConfig);
|
||
}
|