feat(k8s): allow to define customTypes from CRDs

This commit is contained in:
Jaka Hudoklin 2019-10-20 13:36:10 +02:00
parent baea3cc3b3
commit 086780088c
No known key found for this signature in database
GPG key ID: D1F18234B07BD6E2
4 changed files with 144 additions and 59 deletions

View file

@ -85,4 +85,37 @@ rec {
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));
}

View file

@ -7,6 +7,8 @@ with lib;
let
cfg = config.kubernetes;
gvkKeyFn = type: "${type.group}/${type.version}/${type.kind}";
getDefaults = resource: group: version: kind:
catAttrs "default" (filter (default:
(resource == null || default.resource == null || default.resource == resource) &&
@ -76,13 +78,8 @@ let
types = mkOption {
description = "List of registered kubernetes types";
type = types.listOf (types.submodule {
type = coerceListOfSubmodulesToAttrs {
options = {
name = mkOption {
description = "Resource type name";
type = types.nullOr types.str;
};
group = mkOption {
description = "Resource type group";
type = types.str;
@ -98,13 +95,18 @@ let
type = types.str;
};
name = mkOption {
description = "Resource type name";
type = types.nullOr types.str;
};
attrName = mkOption {
description = "Name of the nixified attribute";
type = types.str;
};
};
});
default = [];
} gvkKeyFn;
default = {};
};
};
@ -126,18 +128,18 @@ let
v2 = if length splittedVer2 == 1 then "${getVersion ver2}prod" else getVersion ver2;
in builtins.compareVersions v1 v2;
customResourceTypesByKind = zipAttrs (map (resourceType: {
${resourceType.kind} = resourceType;
customResourceTypesByAttrName = zipAttrs (mapAttrsToList (_: resourceType: {
${resourceType.attrName} = resourceType;
}) cfg.customTypes);
customResourceTypesByKindSortByVersion = mapAttrs (_: resourceTypes:
customResourceTypesByAttrNameSortByVersion = mapAttrs (_: resourceTypes:
reverseList (sort (r1: r2:
compareVersions r1.version r2.version > 0
) resourceTypes)
) customResourceTypesByKind;
) customResourceTypesByAttrName;
latestCustomResourceTypes =
mapAttrsToList (_: resources: last resources) customResourceTypesByKindSortByVersion;
mapAttrsToList (_: resources: last resources) customResourceTypesByAttrNameSortByVersion;
customResourceModuleForType = config: ct: { name, ... }: {
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;
in {
options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption {
@ -182,14 +184,14 @@ let
}) cfg.customTypes) ++ (map (ct: { options, config, ... }: let
module = customResourceModuleForType config ct;
in {
options.resources.${ct.name} = mkOption {
options.resources.${ct.attrName} = mkOption {
description = ct.description;
type = types.attrsOf (types.submodule module);
default = {};
};
config.resources.${ct.group}.${ct.version}.${ct.kind} =
mkAliasDefinitions options.resources.${ct.name};
mkAliasDefinitions options.resources.${ct.attrName};
}) latestCustomResourceTypes);
in {
@ -238,45 +240,56 @@ in {
default = {};
};
createCustomTypesFromCRDs = mkOption {
description = "Whether to create customTypes from custom resource definitions";
type = types.bool;
default = false;
};
customTypes = mkOption {
default = [];
description = "List of custom resource types to make API for";
type = types.listOf (types.submodule ({config, ...}: {
type = coerceListOfSubmodulesToAttrs {
options = {
group = mkOption {
description = "Custom resource definition group";
description = "Custom type group";
type = types.str;
};
version = mkOption {
description = "Custom resource definition version";
description = "Custom type version";
type = types.str;
};
kind = mkOption {
description = "Custom resource definition kind";
description = "Custom type kind";
type = types.str;
};
name = mkOption {
description = "Custom resource definition resource name";
description = "Custom type resource name";
type = types.nullOr types.str;
default = null;
};
attrName = mkOption {
description = "Name of the nixified attribute";
type = types.str;
};
description = mkOption {
description = "Custom resource definition description";
description = "Custom type description";
type = types.str;
default = "";
};
module = mkOption {
description = "Custom resource definition module";
description = "Custom type module";
type = types.unspecified;
default = {};
};
};
}));
} gvkKeyFn;
default = {};
};
objects = mkOption {
@ -334,8 +347,8 @@ in {
kubernetes.api = mkMerge ([{
# register custom types
types = map (cr: {
inherit (cr) name group version kind;
types = mapAttrsToList (_: cr: {
inherit (cr) name group version kind attrName;
}) cfg.customTypes;
defaults = [{
@ -364,11 +377,22 @@ in {
resources.${group}.${version}.${kind}.${name} = object;
}) cfg.imports));
kubernetes.objects = flatten (map (type:
kubernetes.objects = flatten (mapAttrsToList (_: type:
mapAttrsToList (name: resource: moduleToAttrs resource)
cfg.api.resources.${type.group}.${type.version}.${type.kind}
) 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 {
items = config.kubernetes.objects;
labels."kubenix/project-name" = config.kubenix.project;

View file

@ -164,7 +164,7 @@ in {
config = {
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 {
default = { imports = default; };
} // (if (attrName == "all") then {} else {
@ -184,6 +184,9 @@ in {
# custom resources are now included in normal resources, so just make an alias
kubernetes.customResources = mkAliasDefinitions options.kubernetes.resources;
# create custom types from CRDs was old behavior
kubernetes.createCustomTypesFromCRDs = true;
kubernetes.defaultModuleConfiguration.all = {
_file = head options.kubernetes.defaultModuleConfiguration.files;
config.kubernetes.version = mkDefault config.kubernetes.version;

View file

@ -61,39 +61,64 @@ in {
};
};
kubernetes.customTypes = [{
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;
};
kubernetes.createCustomTypesFromCRDs = true;
command = mkOption {
description = "Command to run";
type = types.str;
kubernetes.customTypes = mkMerge [
{
"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.crontabs.latest.spec.schedule = "* * * * *";
kubernetes.resources.cronTabs.latest.spec.schedule = "* * * * *";
kubernetes.resources.cronTabsV3.latest.spec.schedule = "* * * * *";
}