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,8 +1,9 @@
{ config, lib, ... }:
with lib;
{
config,
lib,
...
}:
with lib; {
options = {
kubenix.project = mkOption {
description = "Name of the project";
@ -13,27 +14,27 @@ with lib;
_m.features = mkOption {
description = "List of features exposed by module";
type = types.listOf types.str;
default = [ ];
default = [];
};
_m.propagate = mkOption {
description = "Module propagation options";
type = types.listOf (types.submodule ({ config, ... }: {
type = types.listOf (types.submodule ({config, ...}: {
options = {
features = mkOption {
description = "List of features that submodule has to have to propagate module";
type = types.listOf types.str;
default = [ ];
default = [];
};
module = mkOption {
description = "Module to propagate";
type = types.unspecified;
default = { };
default = {};
};
};
}));
default = [ ];
default = [];
};
};
}

View file

@ -1,11 +1,14 @@
{ config, lib, pkgs, docker, ... }:
with lib;
let
cfg = config.docker;
in
{
imports = [ ./base.nix ];
config,
lib,
pkgs,
docker,
...
}:
with lib; let
cfg = config.docker;
in {
imports = [./base.nix];
options.docker = {
registry.url = mkOption {
@ -16,7 +19,11 @@ in
images = mkOption {
description = "Attribute set of docker images that should be published";
type = types.attrsOf (types.submodule ({ name, config, ... }: {
type = types.attrsOf (types.submodule ({
name,
config,
...
}: {
options = {
image = mkOption {
description = "Docker image to publish";
@ -52,13 +59,13 @@ in
};
};
}));
default = { };
default = {};
};
export = mkOption {
description = "List of images to export";
type = types.listOf types.package;
default = [ ];
default = [];
};
copyScript = mkOption {
@ -73,22 +80,29 @@ in
config = {
# define docker feature
_m.features = [ "docker" ];
_m.features = ["docker"];
# propagate docker options if docker feature is enabled
_m.propagate = [{
features = [ "docker" ];
module = { config, name, ... }: {
# propagate registry options
docker.registry = cfg.registry;
};
}];
_m.propagate = [
{
features = ["docker"];
module = {
config,
name,
...
}: {
# propagate registry options
docker.registry = cfg.registry;
};
}
];
# pass docker library as param
_module.args.docker = import ../lib/docker { inherit lib pkgs; };
_module.args.docker = import ../lib/docker {inherit lib pkgs;};
# list of exported docker images
docker.export = mapAttrsToList (_: i: i.image)
docker.export =
mapAttrsToList (_: i: i.image)
(filterAttrs (_: i: i.registry != null) config.docker.images);
};
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,13 @@
# helm defines kubenix module with options for using helm charts
# with kubenix
{ config, lib, pkgs, helm, ... }:
with lib;
let
{
config,
lib,
pkgs,
helm,
...
}:
with lib; let
cfg = config.kubernetes.helm;
globalConfig = config;
@ -13,26 +16,29 @@ let
name = "recursive-attrs";
description = "recursive attribute set";
check = isAttrs;
merge = loc: foldl' (res: def: recursiveUpdate res def.value) { };
merge = loc: foldl' (res: def: recursiveUpdate res def.value) {};
};
parseApiVersion = apiVersion:
let
splitted = splitString "/" apiVersion;
in
{
group = if length splitted == 1 then "core" else head splitted;
version = last splitted;
};
in
{
imports = [ ./k8s.nix ];
parseApiVersion = apiVersion: let
splitted = splitString "/" apiVersion;
in {
group =
if length splitted == 1
then "core"
else head splitted;
version = last splitted;
};
in {
imports = [./k8s.nix];
options.kubernetes.helm = {
instances = mkOption {
description = "Attribute set of helm instances";
type = types.attrsOf (types.submodule ({ config, name, ... }: {
type = types.attrsOf (types.submodule ({
config,
name,
...
}: {
options = {
name = mkOption {
description = "Helm release name";
@ -54,7 +60,7 @@ in
values = mkOption {
description = "Values to pass to chart";
type = recursiveAttrs;
default = { };
default = {};
};
kubeVersion = mkOption {
@ -66,7 +72,7 @@ in
overrides = mkOption {
description = "Overrides to apply to all chart resources";
type = types.listOf types.unspecified;
default = [ ];
default = [];
};
overrideNamespace = mkOption {
@ -78,39 +84,41 @@ in
objects = mkOption {
description = "Generated kubernetes objects";
type = types.listOf types.attrs;
default = [ ];
default = [];
};
};
config.overrides = mkIf (config.overrideNamespace && config.namespace != null) [{
metadata.namespace = config.namespace;
}];
config.overrides = mkIf (config.overrideNamespace && config.namespace != null) [
{
metadata.namespace = config.namespace;
}
];
config.objects = importJSON (helm.chart2json {
inherit (config) chart name namespace values kubeVersion;
});
}));
default = { };
default = {};
};
};
config = {
# expose helm helper methods as module argument
_module.args.helm = import ../lib/helm { inherit pkgs; };
_module.args.helm = import ../lib/helm {inherit pkgs;};
kubernetes.api.resources = mkMerge (flatten (mapAttrsToList
(_: instance:
map
(object:
let
apiVersion = parseApiVersion object.apiVersion;
name = object.metadata.name;
in
{
"${apiVersion.group}"."${apiVersion.version}".${object.kind}."${name}" = mkMerge ([
(
_: instance:
map
(object: let
apiVersion = parseApiVersion object.apiVersion;
name = object.metadata.name;
in {
"${apiVersion.group}"."${apiVersion.version}".${object.kind}."${name}" = mkMerge ([
object
] ++ instance.overrides);
})
]
++ instance.overrides);
})
instance.objects
)
cfg.instances));

View file

@ -1,16 +1,19 @@
{ lib, definitions }:
with lib;
{
"istio_networking_v1alpha3_StringMatch" = recursiveUpdate
(recursiveUpdate
lib,
definitions,
}:
with lib; {
"istio_networking_v1alpha3_StringMatch" =
recursiveUpdate
(
recursiveUpdate
definitions."istio_networking_v1alpha3_StringMatch_Exact"
definitions."istio_networking_v1alpha3_StringMatch_Prefix"
)
definitions."istio_networking_v1alpha3_StringMatch_Regex";
"istio_networking_v1alpha3_PortSelector" = recursiveUpdate
"istio_networking_v1alpha3_PortSelector" =
recursiveUpdate
definitions."istio_networking_v1alpha3_PortSelector_Name"
definitions."istio_networking_v1alpha3_PortSelector_Number";
}

File diff suppressed because it is too large Load diff

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);

View file

@ -1,8 +1,10 @@
{ config, lib, ... }:
with lib;
{
imports = [ ./base.nix ];
config,
lib,
...
}:
with lib; {
imports = [./base.nix];
options.submodule = {
name = mkOption {
@ -25,24 +27,24 @@ with lib;
tags = mkOption {
description = "List of submodule tags";
type = types.listOf types.str;
default = [ ];
default = [];
};
exports = mkOption {
description = "Attribute set of functions to export";
type = types.attrs;
default = { };
default = {};
};
passthru = mkOption {
description = "Attribute set to passthru";
type = types.attrs;
default = { };
default = {};
};
args._empty = mkOption { };
args._empty = mkOption {};
};
config._module.args.args = config.submodule.args;
config._m.features = [ "submodule" ];
config._m.features = ["submodule"];
}

View file

@ -1,140 +1,183 @@
{ config, options, kubenix, pkgs, lib, ... }:
with lib;
let
{
config,
options,
kubenix,
pkgs,
lib,
...
}:
with lib; let
cfg = config.submodules;
parentConfig = config;
matchesVersion = requiredVersion: version:
if requiredVersion != null then
if requiredVersion != null
then
if hasPrefix "~" requiredVersion
then (builtins.match (removePrefix "~" requiredVersion) version) != null
else requiredVersion == version
else true;
getDefaults = { name, version, tags, features }:
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
)
(
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;
};
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;
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:
versionSortedSubmodules =
sort
(
s1: s2:
if builtins.compareVersions s1.definition.version s2.definition.version > 0
then true else false
)
matchingSubmodules;
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 =
if length versionSortedSubmodules == 0
then
throw "No module found ${name}/${
if version == null
then "latest"
else version
}"
else head versionSortedSubmodules;
in
matchingModule;
passthruConfig = mapAttrsToList
passthruConfig =
mapAttrsToList
(name: opt: {
${name} = mkMerge (mapAttrsToList
(_: inst:
if inst.passthru.enable
then inst.config.submodule.passthru.${name} or { }
else { }
(
_: 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 { }
(
_: inst:
if inst.passthru.enable
then inst.config.submodule.passthru._module.args or {}
else {}
)
config.submodules.instances);
})
(removeAttrs options [ "_definedNames" "_module" "_m" "submodules" ]);
(removeAttrs options ["_definedNames" "_module" "_m" "submodules"]);
submoduleWithSpecialArgs = opts: specialArgs:
let
opts' = toList opts;
inherit (lib.modules) evalModules;
in
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
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
# &gt; and &lt; 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;
})
.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
# &gt; and &lt; 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: [ ];
};
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 ];
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 = { };
default = {};
};
submodules.defaults = mkOption {
description = "List of defaults to apply to submodule instances";
type = types.listOf (types.submodule ({ config, ... }: {
type = types.listOf (types.submodule ({config, ...}: {
options = {
name = mkOption {
description = "Name of the submodule to apply defaults for";
@ -154,23 +197,23 @@ in
tags = mkOption {
description = "List of tags to apply defaults for";
type = types.listOf types.str;
default = [ ];
default = [];
};
features = mkOption {
description = "List of features that submodule has to have to apply defaults";
type = types.listOf types.str;
default = [ ];
default = [];
};
default = mkOption {
description = "Default to apply to submodule instance";
type = types.unspecified;
default = { };
default = {};
};
};
}));
default = [ ];
default = [];
};
submodules.propagate.enable = mkOption {
@ -183,179 +226,191 @@ in
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;
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;
};
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;
};
modules = mkOption {
description = "List of modules defining submodule";
type = types.listOf types.unspecified;
default = [config.module];
};
config = {
definition = {
inherit (evaledSubmodule.config.submodule) name description version tags exports;
};
features = evaledSubmodule.config._m.features;
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 = [ ];
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;
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;
};
# definition of a submodule
submoduleDefinition = submodule.definition;
# submodule defaults
defaults = getDefaults {
name = submoduleDefinition.name;
version = submoduleDefinition.version;
tags = submoduleDefinition.tags;
features = submodule.features;
submodule = mkOption {
description = "Name of the submodule to use";
type = types.str;
default = name;
};
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)";
};
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 = { };
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;
{
# register exported functions as args
_module.args = mkMerge (map
(submodule: {
${submodule.exportAs} = submodule.definition.exports;
})
config._m.propagate)
];
}
(filter (submodule: submodule.exportAs != null) cfg.imports));
(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);
_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);
}

View file

@ -1,11 +1,15 @@
{ config, pkgs, lib, kubenix, ... }:
with lib;
let
{
config,
pkgs,
lib,
kubenix,
...
}:
with lib; let
cfg = config.testing;
testModule = {
imports = [ ./evalTest.nix ];
imports = [./evalTest.nix];
# passthru testing configuration
config._module.args = {
@ -16,9 +20,7 @@ let
isTestEnabled = test:
(cfg.enabledTests == null || elem test.name cfg.enabledTests) && test.enable;
in
{
in {
imports = [
./docker.nix
./driver/kubetest.nix
@ -41,28 +43,28 @@ in
common = mkOption {
description = "List of common options to apply to tests";
type = types.listOf (types.submodule ({ config, ... }: {
type = types.listOf (types.submodule ({config, ...}: {
options = {
features = mkOption {
description = "List of features that test has to have to apply options";
type = types.listOf types.str;
default = [ ];
default = [];
};
options = mkOption {
description = "Options to apply to test";
type = types.unspecified;
default = { };
apply = default: { _file = "testing.common"; } // default;
default = {};
apply = default: {_file = "testing.common";} // default;
};
};
}));
default = [ ];
default = [];
};
tests = mkOption {
description = "List of test cases";
default = [ ];
default = [];
type = types.listOf (types.coercedTo types.path
(module: {
inherit module;
@ -86,7 +88,7 @@ in
args = mkOption {
description = "Attribute set of extra args passed to tests";
type = types.attrs;
default = { };
default = {};
};
success = mkOption {

View file

@ -1,16 +1,17 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
with lib;
with import ../../lib/docker { inherit lib pkgs; };
let
with import ../../lib/docker {inherit lib pkgs;}; let
testing = config.testing;
allImages = unique (flatten (map (t: t.evaled.config.docker.export or [ ]) testing.tests));
allImages = unique (flatten (map (t: t.evaled.config.docker.export or []) testing.tests));
cfg = config.testing.docker;
in
{
in {
options.testing.docker = {
registryUrl = mkOption {
description = "Docker registry url";
@ -37,11 +38,13 @@ in
};
};
config.testing.common = [{
features = [ "docker" ];
options = {
_file = "testing.docker.registryUrl";
docker.registry.url = cfg.registryUrl;
};
}];
config.testing.common = [
{
features = ["docker"];
options = {
_file = "testing.docker.registryUrl";
docker.registry.url = cfg.registryUrl;
};
}
];
}

View file

@ -1,15 +1,14 @@
{ pkgs ? import <nixpkgs> { } }:
{pkgs ? import <nixpkgs> {}}:
with pkgs;
with pkgs.python38Packages;
with pkgs.python38;
pkgs.python38Packages.buildPythonPackage rec {
pname = "kubetest";
version = "0.9.5";
src = fetchPypi {
inherit pname version;
sha256 = "sha256-TqDHMciAEXv4vMWLJY1YdtXsP4ho+INgdFB3xQQNoZU=";
};
propagatedBuildInputs = [ pytest kubernetes ];
doCheck = false;
}
pkgs.python38Packages.buildPythonPackage rec {
pname = "kubetest";
version = "0.9.5";
src = fetchPypi {
inherit pname version;
sha256 = "sha256-TqDHMciAEXv4vMWLJY1YdtXsP4ho+INgdFB3xQQNoZU=";
};
propagatedBuildInputs = [pytest kubernetes];
doCheck = false;
}

View file

@ -1,7 +1,11 @@
{ lib, config, testing, kubenix, ... }:
with lib;
let
{
lib,
config,
testing,
kubenix,
...
}:
with lib; let
modules = [
# testing module
config.module
@ -13,10 +17,12 @@ let
{
config = {
kubenix.project = mkDefault config.name;
_module.args = {
inherit kubenix;
test = evaled.config;
} // testing.args;
_module.args =
{
inherit kubenix;
test = evaled.config;
}
// testing.args;
};
}
];
@ -36,28 +42,29 @@ let
# common options that can be applied on this test
commonOpts =
filter
(d:
(intersectLists d.features testFeatures) == d.features ||
(length d.features) == 0
)
testing.common;
(
d:
(intersectLists d.features testFeatures)
== d.features
|| (length d.features) == 0
)
testing.common;
# add common options modules to all modules
modulesWithCommonOptions = modules ++ (map (d: d.options) commonOpts);
# evaled test
evaled =
let
evaled' = kubenix.evalModules {
modules = modulesWithCommonOptions;
};
in
if testing.doThrowError then evaled'
evaled = let
evaled' = kubenix.evalModules {
modules = modulesWithCommonOptions;
};
in
if testing.doThrowError
then evaled'
else if (builtins.tryEval evaled'.config.test.assertions).success
then evaled' else null;
in
{
then evaled'
else null;
in {
options = {
module = mkOption {
description = "Module defining kubenix test";
@ -100,7 +107,7 @@ in
description = "Test result";
type = types.unspecified;
internal = true;
default = [ ];
default = [];
};
script = mkOption {
@ -108,7 +115,6 @@ in
type = types.nullOr (types.either types.lines types.path);
internal = true;
};
};
config = mkMerge [

View file

@ -1,7 +1,10 @@
{ lib, config, pkgs, ... }:
with lib;
let
{
lib,
config,
pkgs,
...
}:
with lib; let
testing = config.testing;
script = pkgs.writeScript "run-local-k8s-tests-${testing.name}.sh" ''
@ -31,8 +34,7 @@ let
echo "--> running tests"
${testing.testScript} --kube-config=$KUBECONFIG
'';
in
{
in {
options.testing.runtime.local = {
script = mkOption {
type = types.package;

View file

@ -1,29 +1,32 @@
# nixos-k8s implements nixos kubernetes testing runtime
{ config
, pkgs
, lib
, ...
{
config,
pkgs,
lib,
...
}:
with lib;
let
with lib; let
testing = config.testing;
# kubeconfig = "/etc/${config.services.kubernetes.pki.etcClusterAdminKubeconfig}";
kubeconfig = "/etc/kubernetes/cluster-admin.kubeconfig";
kubecerts = "/var/lib/kubernetes/secrets";
# how we differ from the standard configuration of mkKubernetesBaseTest
extraConfiguration = { config, pkgs, lib, nodes, ... }: {
extraConfiguration = {
config,
pkgs,
lib,
nodes,
...
}: {
virtualisation = {
memorySize = 2048;
};
networking = {
nameservers = [ "10.0.0.254" ];
nameservers = ["10.0.0.254"];
firewall = {
trustedInterfaces = [ "docker0" "cni0" ];
trustedInterfaces = ["docker0" "cni0"];
};
};
@ -32,22 +35,26 @@ let
kubelet = {
seedDockerImages = testing.docker.images;
networkPlugin = "cni";
cni.config = [{
name = "mynet";
type = "bridge";
bridge = "cni0";
addIf = true;
ipMasq = true;
isGateway = true;
ipam = {
type = "host-local";
subnet = "10.1.0.0/16";
gateway = "10.1.0.1";
routes = [{
dst = "0.0.0.0/0";
}];
};
}];
cni.config = [
{
name = "mynet";
type = "bridge";
bridge = "cni0";
addIf = true;
ipMasq = true;
isGateway = true;
ipam = {
type = "host-local";
subnet = "10.1.0.0/16";
gateway = "10.1.0.1";
routes = [
{
dst = "0.0.0.0/0";
}
];
};
}
];
};
};
@ -57,32 +64,30 @@ let
services.copy-certs = {
description = "Share k8s certificates with host";
script = "cp -rf ${kubecerts} /tmp/xchg/; cp -f ${kubeconfig} /tmp/xchg/;";
after = [ "kubernetes.target" ];
wantedBy = [ "multi-user.target" ];
after = ["kubernetes.target"];
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
};
};
};
script = ''
machine1.succeed("${testing.testScript} --kube-config=${kubeconfig}")
'';
test =
with import "${pkgs.path}/nixos/tests/kubernetes/base.nix" { inherit pkgs; inherit (pkgs) system; };
test = with import "${pkgs.path}/nixos/tests/kubernetes/base.nix" {
inherit pkgs;
inherit (pkgs) system;
};
mkKubernetesSingleNodeTest {
inherit extraConfiguration;
inherit (config.testing) name;
test = script;
};
in
{
in {
options.testing.runtime.nixos-k8s = {
driver = mkOption {
description = "Test driver";

View file

@ -1,11 +1,12 @@
{ lib, config, pkgs, ... }:
with lib;
let
cfg = config.test;
in
{
lib,
config,
pkgs,
...
}:
with lib; let
cfg = config.test;
in {
options.test = {
name = mkOption {
description = "Test name";
@ -38,8 +39,13 @@ in
};
};
});
default = [ ];
example = [{ assertion = false; message = "you can't enable this for some reason"; }];
default = [];
example = [
{
assertion = false;
message = "you can't enable this for some reason";
}
];
description = ''
This option allows modules to express conditions that must
hold for the evaluation of the system configuration to
@ -52,6 +58,5 @@ in
type = types.nullOr (types.either types.lines types.path);
default = null;
};
};
}