This commit is contained in:
GTrunSec 2022-04-02 12:40:35 -07:00
parent a0ce293db8
commit 60592d3096
No known key found for this signature in database
GPG key ID: 2368FAFA4ABDD2A0
55 changed files with 23668 additions and 30925 deletions

View file

@ -1,33 +1,36 @@
# K8S module defines kubernetes definitions for kubenix
{ options, config, lib, pkgs, k8s, ... }:
with lib;
let
{
options,
config,
lib,
pkgs,
k8s,
...
}:
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) &&
(default.group == null || default.group == group) &&
(default.version == null || default.version == version) &&
(default.kind == null || default.kind == kind)
(
default:
(resource == null || default.resource == null || default.resource == resource)
&& (default.group == null || default.group == group)
&& (default.version == null || default.version == version)
&& (default.kind == null || default.kind == kind)
)
cfg.api.defaults);
moduleToAttrs = value:
if isAttrs value
then mapAttrs (n: v: moduleToAttrs v) (filterAttrs (n: v: v != null && !(hasPrefix "_" n)) value)
else if isList value
then map (v: moduleToAttrs v) value
else value;
apiOptions = { config, ... }: {
apiOptions = {config, ...}: {
options = {
definitions = mkOption {
description = "Attribute set of kubernetes definitions";
@ -35,7 +38,7 @@ let
defaults = mkOption {
description = "Kubernetes defaults to apply to resources";
type = types.listOf (types.submodule ({ config, ... }: {
type = types.listOf (types.submodule ({config, ...}: {
options = {
group = mkOption {
description = "Group to apply default to (all by default)";
@ -70,17 +73,18 @@ let
default = mkOption {
description = "Default to apply";
type = types.unspecified;
default = { };
default = {};
};
};
}));
default = [ ];
default = [];
apply = unique;
};
types = mkOption {
description = "List of registered kubernetes types";
type = coerceListOfSubmodulesToAttrs
type =
coerceListOfSubmodulesToAttrs
{
options = {
group = mkOption {
@ -110,7 +114,7 @@ let
};
}
gvkKeyFn;
default = { };
default = {};
};
};
@ -121,17 +125,26 @@ let
};
indexOf = lst: value:
head (filter (v: v != -1) (imap0 (i: v: if v == value then i else -1) lst));
head (filter (v: v != -1) (imap0 (i: v:
if v == value
then i
else -1)
lst));
compareVersions = ver1: ver2:
let
getVersion = v: substring 1 10 v;
splittedVer1 = builtins.splitVersion (getVersion ver1);
splittedVer2 = builtins.splitVersion (getVersion ver2);
compareVersions = ver1: ver2: let
getVersion = v: substring 1 10 v;
splittedVer1 = builtins.splitVersion (getVersion ver1);
splittedVer2 = builtins.splitVersion (getVersion ver2);
v1 = if length splittedVer1 == 1 then "${getVersion ver1}prod" else getVersion ver1;
v2 = if length splittedVer2 == 1 then "${getVersion ver2}prod" else getVersion ver2;
in
v1 =
if length splittedVer1 == 1
then "${getVersion ver1}prod"
else getVersion ver1;
v2 =
if length splittedVer2 == 1
then "${getVersion ver2}prod"
else getVersion ver2;
in
builtins.compareVersions v1 v2;
customResourceTypesByAttrName = zipAttrs (mapAttrsToList
@ -140,20 +153,23 @@ let
})
cfg.customTypes);
customResourceTypesByAttrNameSortByVersion = mapAttrs
(_: resourceTypes:
reverseList (sort
(r1: r2:
compareVersions r1.version r2.version > 0
)
resourceTypes)
customResourceTypesByAttrNameSortByVersion =
mapAttrs
(
_: resourceTypes:
reverseList (sort
(
r1: r2:
compareVersions r1.version r2.version > 0
)
resourceTypes)
)
customResourceTypesByAttrName;
latestCustomResourceTypes =
mapAttrsToList (_: resources: last resources) customResourceTypesByAttrNameSortByVersion;
customResourceModuleForType = config: ct: { name, ... }: {
customResourceModuleForType = config: ct: {name, ...}: {
imports = getDefaults ct.name ct.group ct.version ct.kind;
options = {
apiVersion = mkOption {
@ -174,7 +190,7 @@ let
spec = mkOption {
description = "Module spec";
type = types.either types.attrs (types.submodule ct.module);
default = { };
default = {};
};
};
@ -185,81 +201,78 @@ let
};
};
customResourceOptions = (mapAttrsToList
(_: ct: { config, ... }:
let
customResourceOptions =
(mapAttrsToList
(_: ct: {config, ...}: let
module = customResourceModuleForType config ct;
in
{
in {
options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption {
description = ct.description;
type = types.attrsOf (types.submodule module);
default = { };
default = {};
};
})
cfg.customTypes) ++ (map
(ct: { options, config, ... }:
let
cfg.customTypes)
++ (map
(ct: {
options,
config,
...
}: let
module = customResourceModuleForType config ct;
in
{
in {
options.resources.${ct.attrName} = mkOption {
description = ct.description;
type = types.attrsOf (types.submodule module);
default = { };
default = {};
};
config.resources.${ct.group}.${ct.version}.${ct.kind} =
mkAliasDefinitions options.resources.${ct.attrName};
})
latestCustomResourceTypes);
latestCustomResourceTypes);
coerceListOfSubmodulesToAttrs = submodule: keyFn:
let
mergeValuesByFn = keyFn: values:
listToAttrs (map
(value:
coerceListOfSubmodulesToAttrs = submodule: keyFn: let
mergeValuesByFn = keyFn: values:
listToAttrs (map
(
value:
nameValuePair (toString (keyFn value)) value
)
values);
)
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
# 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));
in
{
imports = [ ./base.nix ];
(types.listOf (types.submodule submodule))
(mergeValuesByFn keyFn)
(types.attrsOf (types.submodule submodule));
in {
imports = [./base.nix];
options.kubernetes = {
version = mkOption {
description = "Kubernetes version to use";
type = types.enum [ "1.19" "1.20" "1.21" ];
type = types.enum ["1.19" "1.20" "1.21"];
default = "1.21";
};
@ -280,29 +293,32 @@ in
api = mkOption {
type = types.submodule {
imports = [
(./generated + ''/v'' + cfg.version + ".nix")
apiOptions
] ++ customResourceOptions;
imports =
[
(./generated + ''/v'' + cfg.version + ".nix")
apiOptions
]
++ customResourceOptions;
};
default = { };
default = {};
};
imports = mkOption {
type = types.listOf (types.either types.package types.path);
description = "List of resources to import";
default = [ ];
default = [];
};
resources = mkOption {
description = "Alias for `config.kubernetes.api.resources` options";
default = { };
default = {};
type = types.attrsOf types.attrs;
};
customTypes = mkOption {
description = "List of custom resource types to make API for";
type = coerceListOfSubmodulesToAttrs
type =
coerceListOfSubmodulesToAttrs
{
options = {
group = mkOption {
@ -340,25 +356,29 @@ in
module = mkOption {
description = "Custom type module";
type = types.unspecified;
default = { };
default = {};
};
};
}
gvkKeyFn;
default = { };
default = {};
};
objects = mkOption {
description = "List of generated kubernetes objects";
type = types.listOf types.attrs;
apply = items: sort
(r1: r2:
if elem r1.kind cfg.resourceOrder && elem r2.kind cfg.resourceOrder
then indexOf cfg.resourceOrder r1.kind < indexOf cfg.resourceOrder r2.kind
else if elem r1.kind cfg.resourceOrder then true else false
apply = items:
sort
(
r1: r2:
if elem r1.kind cfg.resourceOrder && elem r2.kind cfg.resourceOrder
then indexOf cfg.resourceOrder r1.kind < indexOf cfg.resourceOrder r2.kind
else if elem r1.kind cfg.resourceOrder
then true
else false
)
(unique items);
default = [ ];
default = [];
};
generated = mkOption {
@ -379,26 +399,26 @@ in
config = {
# features that module is defining
_m.features = [ "k8s" ];
_m.features = ["k8s"];
# module propagation options
_m.propagate = [{
features = [ "k8s" ];
module = { config, ... }: {
# propagate kubernetes version and namespace
kubernetes.version = mkDefault cfg.version;
kubernetes.namespace = mkDefault cfg.namespace;
};
}
_m.propagate = [
{
features = [ "k8s" "submodule" ];
module = { config, ... }: {
features = ["k8s"];
module = {config, ...}: {
# propagate kubernetes version and namespace
kubernetes.version = mkDefault cfg.version;
kubernetes.namespace = mkDefault cfg.namespace;
};
}
{
features = ["k8s" "submodule"];
module = {config, ...}: {
# set module defaults
kubernetes.api.defaults = (
# propagate defaults if default propagation is enabled
(filter (default: default.propagate) cfg.api.defaults) ++
[
(filter (default: default.propagate) cfg.api.defaults)
++ [
# set module name and version for all kuberentes resources
{
default.metadata.labels = {
@ -409,38 +429,44 @@ in
]
);
};
}];
}
];
# expose k8s helper methods as module argument
_module.args.k8s = import ../lib/k8s { inherit lib; };
_module.args.k8s = import ../lib/k8s {inherit lib;};
kubernetes.api = mkMerge ([{
# register custom types
types = mapAttrsToList
(_: cr: {
inherit (cr) name group version kind attrName;
})
cfg.customTypes;
kubernetes.api = mkMerge ([
{
# register custom types
types =
mapAttrsToList
(_: cr: {
inherit (cr) name group version kind attrName;
})
cfg.customTypes;
defaults = [{
default = {
# set default kubernetes namespace to all resources
metadata.namespace = mkIf (config.kubernetes.namespace != null)
(mkDefault config.kubernetes.namespace);
defaults = [
{
default = {
# set default kubernetes namespace to all resources
metadata.namespace =
mkIf (config.kubernetes.namespace != null)
(mkDefault config.kubernetes.namespace);
# set project name to all resources
metadata.annotations = {
"kubenix/project-name" = config.kubenix.project;
"kubenix/k8s-version" = cfg.version;
};
};
}];
}] ++
# import of yaml files
(map
(i:
let
# set project name to all resources
metadata.annotations = {
"kubenix/project-name" = config.kubenix.project;
"kubenix/k8s-version" = cfg.version;
};
};
}
];
}
]
++
# import of yaml files
(map
(i: let
# load yaml file
object = importYAML i;
groupVersion = splitString "/" object.apiVersion;
@ -448,17 +474,18 @@ in
version = last groupVersion;
group =
if version == (head groupVersion)
then "core" else head groupVersion;
then "core"
else head groupVersion;
kind = object.kind;
in
{
in {
resources.${group}.${version}.${kind}.${name} = object;
})
cfg.imports));
cfg.imports));
kubernetes.objects = flatten (mapAttrsToList
(_: type:
mapAttrsToList (name: resource: moduleToAttrs resource)
(
_: type:
mapAttrsToList (name: resource: moduleToAttrs resource)
cfg.api.resources.${type.group}.${type.version}.${type.kind}
)
cfg.api.types);