mirror of
https://github.com/TECHNOFAB11/kubenix.git
synced 2025-12-12 16:10:05 +01:00
feat(k8s): allow to define customTypes from CRDs
This commit is contained in:
parent
baea3cc3b3
commit
086780088c
4 changed files with 144 additions and 59 deletions
|
|
@ -85,4 +85,37 @@ rec {
|
||||||
binOp = lhs: rhs: [];
|
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));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.kubernetes;
|
cfg = config.kubernetes;
|
||||||
|
|
||||||
|
gvkKeyFn = type: "${type.group}/${type.version}/${type.kind}";
|
||||||
|
|
||||||
getDefaults = resource: group: version: kind:
|
getDefaults = resource: group: version: kind:
|
||||||
catAttrs "default" (filter (default:
|
catAttrs "default" (filter (default:
|
||||||
(resource == null || default.resource == null || default.resource == resource) &&
|
(resource == null || default.resource == null || default.resource == resource) &&
|
||||||
|
|
@ -76,13 +78,8 @@ let
|
||||||
|
|
||||||
types = mkOption {
|
types = mkOption {
|
||||||
description = "List of registered kubernetes types";
|
description = "List of registered kubernetes types";
|
||||||
type = types.listOf (types.submodule {
|
type = coerceListOfSubmodulesToAttrs {
|
||||||
options = {
|
options = {
|
||||||
name = mkOption {
|
|
||||||
description = "Resource type name";
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
group = mkOption {
|
||||||
description = "Resource type group";
|
description = "Resource type group";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
|
@ -98,13 +95,18 @@ let
|
||||||
type = types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
description = "Resource type name";
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
};
|
||||||
|
|
||||||
attrName = mkOption {
|
attrName = mkOption {
|
||||||
description = "Name of the nixified attribute";
|
description = "Name of the nixified attribute";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
} gvkKeyFn;
|
||||||
default = [];
|
default = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -126,18 +128,18 @@ let
|
||||||
v2 = if length splittedVer2 == 1 then "${getVersion ver2}prod" else getVersion ver2;
|
v2 = if length splittedVer2 == 1 then "${getVersion ver2}prod" else getVersion ver2;
|
||||||
in builtins.compareVersions v1 v2;
|
in builtins.compareVersions v1 v2;
|
||||||
|
|
||||||
customResourceTypesByKind = zipAttrs (map (resourceType: {
|
customResourceTypesByAttrName = zipAttrs (mapAttrsToList (_: resourceType: {
|
||||||
${resourceType.kind} = resourceType;
|
${resourceType.attrName} = resourceType;
|
||||||
}) cfg.customTypes);
|
}) cfg.customTypes);
|
||||||
|
|
||||||
customResourceTypesByKindSortByVersion = mapAttrs (_: resourceTypes:
|
customResourceTypesByAttrNameSortByVersion = mapAttrs (_: resourceTypes:
|
||||||
reverseList (sort (r1: r2:
|
reverseList (sort (r1: r2:
|
||||||
compareVersions r1.version r2.version > 0
|
compareVersions r1.version r2.version > 0
|
||||||
) resourceTypes)
|
) resourceTypes)
|
||||||
) customResourceTypesByKind;
|
) customResourceTypesByAttrName;
|
||||||
|
|
||||||
latestCustomResourceTypes =
|
latestCustomResourceTypes =
|
||||||
mapAttrsToList (_: resources: last resources) customResourceTypesByKindSortByVersion;
|
mapAttrsToList (_: resources: last resources) customResourceTypesByAttrNameSortByVersion;
|
||||||
|
|
||||||
customResourceModuleForType = config: ct: { name, ... }: {
|
customResourceModuleForType = config: ct: { name, ... }: {
|
||||||
imports = getDefaults ct.name ct.group ct.version ct.kind;
|
imports = getDefaults ct.name ct.group ct.version ct.kind;
|
||||||
|
|
@ -171,7 +173,7 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
customResourceOptions = (map (ct: {config, ...}: let
|
customResourceOptions = (mapAttrsToList (_: ct: {config, ...}: let
|
||||||
module = customResourceModuleForType config ct;
|
module = customResourceModuleForType config ct;
|
||||||
in {
|
in {
|
||||||
options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption {
|
options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption {
|
||||||
|
|
@ -182,14 +184,14 @@ let
|
||||||
}) cfg.customTypes) ++ (map (ct: { options, config, ... }: let
|
}) cfg.customTypes) ++ (map (ct: { options, config, ... }: let
|
||||||
module = customResourceModuleForType config ct;
|
module = customResourceModuleForType config ct;
|
||||||
in {
|
in {
|
||||||
options.resources.${ct.name} = mkOption {
|
options.resources.${ct.attrName} = mkOption {
|
||||||
description = ct.description;
|
description = ct.description;
|
||||||
type = types.attrsOf (types.submodule module);
|
type = types.attrsOf (types.submodule module);
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
config.resources.${ct.group}.${ct.version}.${ct.kind} =
|
config.resources.${ct.group}.${ct.version}.${ct.kind} =
|
||||||
mkAliasDefinitions options.resources.${ct.name};
|
mkAliasDefinitions options.resources.${ct.attrName};
|
||||||
}) latestCustomResourceTypes);
|
}) latestCustomResourceTypes);
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
|
@ -238,45 +240,56 @@ in {
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createCustomTypesFromCRDs = mkOption {
|
||||||
|
description = "Whether to create customTypes from custom resource definitions";
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
|
||||||
customTypes = mkOption {
|
customTypes = mkOption {
|
||||||
default = [];
|
|
||||||
description = "List of custom resource types to make API for";
|
description = "List of custom resource types to make API for";
|
||||||
type = types.listOf (types.submodule ({config, ...}: {
|
type = coerceListOfSubmodulesToAttrs {
|
||||||
options = {
|
options = {
|
||||||
group = mkOption {
|
group = mkOption {
|
||||||
description = "Custom resource definition group";
|
description = "Custom type group";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
|
|
||||||
version = mkOption {
|
version = mkOption {
|
||||||
description = "Custom resource definition version";
|
description = "Custom type version";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
|
|
||||||
kind = mkOption {
|
kind = mkOption {
|
||||||
description = "Custom resource definition kind";
|
description = "Custom type kind";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
|
|
||||||
name = mkOption {
|
name = mkOption {
|
||||||
description = "Custom resource definition resource name";
|
description = "Custom type resource name";
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
attrName = mkOption {
|
||||||
|
description = "Name of the nixified attribute";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
|
||||||
description = mkOption {
|
description = mkOption {
|
||||||
description = "Custom resource definition description";
|
description = "Custom type description";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "";
|
default = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
module = mkOption {
|
module = mkOption {
|
||||||
description = "Custom resource definition module";
|
description = "Custom type module";
|
||||||
type = types.unspecified;
|
type = types.unspecified;
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}));
|
} gvkKeyFn;
|
||||||
|
default = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
objects = mkOption {
|
objects = mkOption {
|
||||||
|
|
@ -334,8 +347,8 @@ in {
|
||||||
|
|
||||||
kubernetes.api = mkMerge ([{
|
kubernetes.api = mkMerge ([{
|
||||||
# register custom types
|
# register custom types
|
||||||
types = map (cr: {
|
types = mapAttrsToList (_: cr: {
|
||||||
inherit (cr) name group version kind;
|
inherit (cr) name group version kind attrName;
|
||||||
}) cfg.customTypes;
|
}) cfg.customTypes;
|
||||||
|
|
||||||
defaults = [{
|
defaults = [{
|
||||||
|
|
@ -364,11 +377,22 @@ in {
|
||||||
resources.${group}.${version}.${kind}.${name} = object;
|
resources.${group}.${version}.${kind}.${name} = object;
|
||||||
}) cfg.imports));
|
}) cfg.imports));
|
||||||
|
|
||||||
kubernetes.objects = flatten (map (type:
|
kubernetes.objects = flatten (mapAttrsToList (_: type:
|
||||||
mapAttrsToList (name: resource: moduleToAttrs resource)
|
mapAttrsToList (name: resource: moduleToAttrs resource)
|
||||||
cfg.api.resources.${type.group}.${type.version}.${type.kind}
|
cfg.api.resources.${type.group}.${type.version}.${type.kind}
|
||||||
) cfg.api.types);
|
) cfg.api.types);
|
||||||
|
|
||||||
|
# custom types created from customResourceDefinitions
|
||||||
|
kubernetes.customTypes = mkIf cfg.createCustomTypesFromCRDs (
|
||||||
|
mapAttrsToList (_: crd: {
|
||||||
|
group = crd.spec.group;
|
||||||
|
version = crd.spec.version;
|
||||||
|
kind = crd.spec.names.kind;
|
||||||
|
name = crd.spec.names.plural;
|
||||||
|
attrName = mkDefault crd.spec.names.plural;
|
||||||
|
}) (cfg.resources.customResourceDefinitions or {})
|
||||||
|
);
|
||||||
|
|
||||||
kubernetes.generated = k8s.mkHashedList {
|
kubernetes.generated = k8s.mkHashedList {
|
||||||
items = config.kubernetes.objects;
|
items = config.kubernetes.objects;
|
||||||
labels."kubenix/project-name" = config.kubenix.project;
|
labels."kubenix/project-name" = config.kubenix.project;
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ in {
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
kubernetes.api.defaults = mapAttrsToList (attrName: default: let
|
kubernetes.api.defaults = mapAttrsToList (attrName: default: let
|
||||||
type = head (filter (type: type.attrName == attrName) config.kubernetes.api.types);
|
type = head (mapAttrsToList (_: v: v) (filterAttrs (_: type: type.attrName == attrName) config.kubernetes.api.types));
|
||||||
in {
|
in {
|
||||||
default = { imports = default; };
|
default = { imports = default; };
|
||||||
} // (if (attrName == "all") then {} else {
|
} // (if (attrName == "all") then {} else {
|
||||||
|
|
@ -184,6 +184,9 @@ in {
|
||||||
# custom resources are now included in normal resources, so just make an alias
|
# custom resources are now included in normal resources, so just make an alias
|
||||||
kubernetes.customResources = mkAliasDefinitions options.kubernetes.resources;
|
kubernetes.customResources = mkAliasDefinitions options.kubernetes.resources;
|
||||||
|
|
||||||
|
# create custom types from CRDs was old behavior
|
||||||
|
kubernetes.createCustomTypesFromCRDs = true;
|
||||||
|
|
||||||
kubernetes.defaultModuleConfiguration.all = {
|
kubernetes.defaultModuleConfiguration.all = {
|
||||||
_file = head options.kubernetes.defaultModuleConfiguration.files;
|
_file = head options.kubernetes.defaultModuleConfiguration.files;
|
||||||
config.kubernetes.version = mkDefault config.kubernetes.version;
|
config.kubernetes.version = mkDefault config.kubernetes.version;
|
||||||
|
|
|
||||||
|
|
@ -61,39 +61,64 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
kubernetes.customTypes = [{
|
kubernetes.createCustomTypesFromCRDs = true;
|
||||||
name = "crontabs";
|
|
||||||
group = "stable.example.com";
|
|
||||||
version = "v1";
|
|
||||||
kind = "CronTab";
|
|
||||||
description = "CronTabs resources";
|
|
||||||
module = {
|
|
||||||
options.schedule = mkOption {
|
|
||||||
description = "Crontab schedule script";
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} {
|
|
||||||
name = "crontabs";
|
|
||||||
group = "stable.example.com";
|
|
||||||
version = "v2";
|
|
||||||
kind = "CronTab";
|
|
||||||
description = "CronTabs resources";
|
|
||||||
module = {
|
|
||||||
options = {
|
|
||||||
schedule = mkOption {
|
|
||||||
description = "Crontab schedule script";
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
|
|
||||||
command = mkOption {
|
kubernetes.customTypes = mkMerge [
|
||||||
description = "Command to run";
|
{
|
||||||
type = types.str;
|
"stable.example.com/v1/CronTab" = {
|
||||||
|
attrName = "cronTabs";
|
||||||
|
description = "CronTabs resources";
|
||||||
|
module = {
|
||||||
|
options.schedule = mkOption {
|
||||||
|
description = "Crontab schedule script";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}];
|
{
|
||||||
|
"stable.example.com/v2/CronTab" = {
|
||||||
|
description = "CronTabs resources";
|
||||||
|
attrName = "cronTabs";
|
||||||
|
module = {
|
||||||
|
options = {
|
||||||
|
schedule = mkOption {
|
||||||
|
description = "Crontab schedule script";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
command = mkOption {
|
||||||
|
description = "Command to run";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[{
|
||||||
|
group = "stable.example.com";
|
||||||
|
version = "v3";
|
||||||
|
kind = "CronTab";
|
||||||
|
description = "CronTabs resources";
|
||||||
|
attrName = "cronTabsV3";
|
||||||
|
module = {
|
||||||
|
options = {
|
||||||
|
schedule = mkOption {
|
||||||
|
description = "Crontab schedule script";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
command = mkOption {
|
||||||
|
description = "Command to run";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}]
|
||||||
|
];
|
||||||
|
|
||||||
kubernetes.resources."stable.example.com"."v1".CronTab.versioned.spec.schedule = "* * * * *";
|
kubernetes.resources."stable.example.com"."v1".CronTab.versioned.spec.schedule = "* * * * *";
|
||||||
kubernetes.resources.crontabs.latest.spec.schedule = "* * * * *";
|
kubernetes.resources.cronTabs.latest.spec.schedule = "* * * * *";
|
||||||
|
kubernetes.resources.cronTabsV3.latest.spec.schedule = "* * * * *";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue