mirror of
https://github.com/TECHNOFAB11/kubenix.git
synced 2025-12-12 08:00:06 +01:00
ref: move source code to ./src (clean top level)
This commit is contained in:
parent
6ae1e2eb15
commit
09e268920b
35 changed files with 2 additions and 2 deletions
|
|
@ -1,39 +0,0 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options = {
|
||||
kubenix.project = mkOption {
|
||||
description = "Name of the project";
|
||||
type = types.str;
|
||||
default = "kubenix";
|
||||
};
|
||||
|
||||
_m.features = mkOption {
|
||||
description = "List of features exposed by module";
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
_m.propagate = mkOption {
|
||||
description = "Module propagation options";
|
||||
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 = [ ];
|
||||
};
|
||||
|
||||
module = mkOption {
|
||||
description = "Module to propagate";
|
||||
type = types.unspecified;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
k8s = ./k8s.nix;
|
||||
istio = ./istio.nix;
|
||||
submodules = ./submodules.nix;
|
||||
submodule = ./submodule.nix;
|
||||
helm = ./helm.nix;
|
||||
docker = ./docker.nix;
|
||||
testing = ./testing;
|
||||
test = ./testing/test-options.nix;
|
||||
base = ./base.nix;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
{ config, lib, pkgs, docker, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.docker;
|
||||
in
|
||||
{
|
||||
imports = [ ./base.nix ];
|
||||
|
||||
options.docker = {
|
||||
registry.url = mkOption {
|
||||
description = "Default registry url where images are published";
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
|
||||
images = mkOption {
|
||||
description = "Attribute set of docker images that should be published";
|
||||
type = types.attrsOf (types.submodule ({ name, config, ... }: {
|
||||
options = {
|
||||
image = mkOption {
|
||||
description = "Docker image to publish";
|
||||
type = types.nullOr types.package;
|
||||
default = null;
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
description = "Desired docker image name";
|
||||
type = types.str;
|
||||
default = builtins.unsafeDiscardStringContext config.image.imageName;
|
||||
};
|
||||
|
||||
tag = mkOption {
|
||||
description = "Desired docker image tag";
|
||||
type = types.str;
|
||||
default = builtins.unsafeDiscardStringContext config.image.imageTag;
|
||||
};
|
||||
|
||||
registry = mkOption {
|
||||
description = "Docker registry url where image is published";
|
||||
type = types.str;
|
||||
default = cfg.registry.url;
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
description = "Full docker image path";
|
||||
type = types.str;
|
||||
default =
|
||||
if config.registry != ""
|
||||
then "${config.registry}/${config.name}:${config.tag}"
|
||||
else "${config.name}:${config.tag}";
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = { };
|
||||
};
|
||||
|
||||
export = mkOption {
|
||||
description = "List of images to export";
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
copyScript = mkOption {
|
||||
description = "Image copy script";
|
||||
type = types.package;
|
||||
default = docker.copyDockerImages {
|
||||
dest = "docker://${cfg.registry.url}";
|
||||
images = cfg.export;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# define docker feature
|
||||
_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;
|
||||
};
|
||||
}];
|
||||
|
||||
# pass docker library as param
|
||||
_module.args.docker = import ../lib/docker { inherit lib pkgs; };
|
||||
|
||||
# list of exported docker images
|
||||
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
118
modules/helm.nix
118
modules/helm.nix
|
|
@ -1,118 +0,0 @@
|
|||
# helm defines kubenix module with options for using helm charts
|
||||
# with kubenix
|
||||
|
||||
{ config, lib, pkgs, helm, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.kubernetes.helm;
|
||||
|
||||
globalConfig = config;
|
||||
|
||||
recursiveAttrs = mkOptionType {
|
||||
name = "recursive-attrs";
|
||||
description = "recursive attribute set";
|
||||
check = isAttrs;
|
||||
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 ];
|
||||
|
||||
options.kubernetes.helm = {
|
||||
instances = mkOption {
|
||||
description = "Attribute set of helm instances";
|
||||
type = types.attrsOf (types.submodule ({ config, name, ... }: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
description = "Helm release name";
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
|
||||
chart = mkOption {
|
||||
description = "Helm chart to use";
|
||||
type = types.package;
|
||||
};
|
||||
|
||||
namespace = mkOption {
|
||||
description = "Namespace to install helm chart to";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
values = mkOption {
|
||||
description = "Values to pass to chart";
|
||||
type = recursiveAttrs;
|
||||
default = { };
|
||||
};
|
||||
|
||||
kubeVersion = mkOption {
|
||||
description = "Kubernetes version to build chart for";
|
||||
type = types.str;
|
||||
default = globalConfig.kubernetes.version;
|
||||
};
|
||||
|
||||
overrides = mkOption {
|
||||
description = "Overrides to apply to all chart resources";
|
||||
type = types.listOf types.unspecified;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
overrideNamespace = mkOption {
|
||||
description = "Whether to apply namespace override";
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
objects = mkOption {
|
||||
description = "Generated kubernetes objects";
|
||||
type = types.listOf types.attrs;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
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 = { };
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# expose helm helper methods as module argument
|
||||
_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 ([
|
||||
object
|
||||
] ++ instance.overrides);
|
||||
})
|
||||
instance.objects
|
||||
)
|
||||
cfg.instances));
|
||||
};
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{ 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
|
||||
definitions."istio_networking_v1alpha3_PortSelector_Name"
|
||||
definitions."istio_networking_v1alpha3_PortSelector_Number";
|
||||
}
|
||||
4963
modules/istio.nix
4963
modules/istio.nix
File diff suppressed because it is too large
Load diff
478
modules/k8s.nix
478
modules/k8s.nix
|
|
@ -1,478 +0,0 @@
|
|||
# K8S module defines kubernetes definitions for kubenix
|
||||
|
||||
{ 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)
|
||||
)
|
||||
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, ... }: {
|
||||
options = {
|
||||
definitions = mkOption {
|
||||
description = "Attribute set of kubernetes definitions";
|
||||
};
|
||||
|
||||
defaults = mkOption {
|
||||
description = "Kubernetes defaults to apply to resources";
|
||||
type = types.listOf (types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
group = mkOption {
|
||||
description = "Group to apply default to (all by default)";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
version = mkOption {
|
||||
description = "Version to apply default to (all by default)";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
kind = mkOption {
|
||||
description = "Kind to apply default to (all by default)";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
resource = mkOption {
|
||||
description = "Resource to apply default to (all by default)";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
propagate = mkOption {
|
||||
description = "Whether to propagate defaults";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
default = mkOption {
|
||||
description = "Default to apply";
|
||||
type = types.unspecified;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = [ ];
|
||||
apply = unique;
|
||||
};
|
||||
|
||||
types = mkOption {
|
||||
description = "List of registered kubernetes types";
|
||||
type = coerceListOfSubmodulesToAttrs
|
||||
{
|
||||
options = {
|
||||
group = mkOption {
|
||||
description = "Resource type group";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
version = mkOption {
|
||||
description = "Resoruce type version";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
kind = mkOption {
|
||||
description = "Resource type kind";
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
||||
gvkKeyFn;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# apply aliased option
|
||||
resources = mkAliasDefinitions options.kubernetes.resources;
|
||||
};
|
||||
};
|
||||
|
||||
indexOf = lst: value:
|
||||
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);
|
||||
|
||||
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
|
||||
(_: resourceType: {
|
||||
${resourceType.attrName} = resourceType;
|
||||
})
|
||||
cfg.customTypes);
|
||||
|
||||
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, ... }: {
|
||||
imports = getDefaults ct.name ct.group ct.version ct.kind;
|
||||
options = {
|
||||
apiVersion = mkOption {
|
||||
description = "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources";
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
|
||||
kind = mkOption {
|
||||
description = "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds";
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
|
||||
metadata = mkOption {
|
||||
description = "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata.";
|
||||
type = types.nullOr (types.submodule config.definitions."io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta");
|
||||
};
|
||||
|
||||
spec = mkOption {
|
||||
description = "Module spec";
|
||||
type = types.either types.attrs (types.submodule ct.module);
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
apiVersion = mkOptionDefault "${ct.group}/${ct.version}";
|
||||
kind = mkOptionDefault ct.kind;
|
||||
metadata.name = mkDefault name;
|
||||
};
|
||||
};
|
||||
|
||||
customResourceOptions = (mapAttrsToList
|
||||
(_: ct: { config, ... }:
|
||||
let
|
||||
module = customResourceModuleForType config ct;
|
||||
in
|
||||
{
|
||||
options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption {
|
||||
description = ct.description;
|
||||
type = types.attrsOf (types.submodule module);
|
||||
default = { };
|
||||
};
|
||||
})
|
||||
cfg.customTypes) ++ (map
|
||||
(ct: { options, config, ... }:
|
||||
let
|
||||
module = customResourceModuleForType config ct;
|
||||
in
|
||||
{
|
||||
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.attrName};
|
||||
})
|
||||
latestCustomResourceTypes);
|
||||
|
||||
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));
|
||||
|
||||
in
|
||||
{
|
||||
imports = [ ./base.nix ];
|
||||
|
||||
options.kubernetes = {
|
||||
version = mkOption {
|
||||
description = "Kubernetes version to use";
|
||||
type = types.enum [ "1.19" "1.20" "1.21" ];
|
||||
default = "1.21";
|
||||
};
|
||||
|
||||
namespace = mkOption {
|
||||
description = "Default namespace where to deploy kubernetes resources";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
resourceOrder = mkOption {
|
||||
description = "Preffered resource order";
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"CustomResourceDefinition"
|
||||
"Namespace"
|
||||
];
|
||||
};
|
||||
|
||||
api = mkOption {
|
||||
type = types.submodule {
|
||||
imports = [
|
||||
(./generated + ''/v'' + cfg.version + ".nix")
|
||||
apiOptions
|
||||
] ++ customResourceOptions;
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
|
||||
imports = mkOption {
|
||||
type = types.listOf (types.either types.package types.path);
|
||||
description = "List of resources to import";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
resources = mkOption {
|
||||
description = "Alias for `config.kubernetes.api.resources` options";
|
||||
default = { };
|
||||
type = types.attrsOf types.attrs;
|
||||
};
|
||||
|
||||
customTypes = mkOption {
|
||||
description = "List of custom resource types to make API for";
|
||||
type = coerceListOfSubmodulesToAttrs
|
||||
{
|
||||
options = {
|
||||
group = mkOption {
|
||||
description = "Custom type group";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
version = mkOption {
|
||||
description = "Custom type version";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
kind = mkOption {
|
||||
description = "Custom type kind";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
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 type description";
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
|
||||
module = mkOption {
|
||||
description = "Custom type module";
|
||||
type = types.unspecified;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
gvkKeyFn;
|
||||
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
|
||||
)
|
||||
(unique items);
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
generated = mkOption {
|
||||
description = "Generated kubernetes list object";
|
||||
type = types.attrs;
|
||||
};
|
||||
|
||||
result = mkOption {
|
||||
description = "Generated kubernetes JSON file";
|
||||
type = types.package;
|
||||
};
|
||||
|
||||
resultYAML = mkOption {
|
||||
description = "Genrated kubernetes YAML file";
|
||||
type = types.package;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# features that module is defining
|
||||
_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;
|
||||
};
|
||||
}
|
||||
{
|
||||
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) ++
|
||||
|
||||
[
|
||||
# set module name and version for all kuberentes resources
|
||||
{
|
||||
default.metadata.labels = {
|
||||
"kubenix/module-name" = config.submodule.name;
|
||||
"kubenix/module-version" = config.submodule.version;
|
||||
};
|
||||
}
|
||||
]
|
||||
);
|
||||
};
|
||||
}];
|
||||
|
||||
# expose k8s helper methods as module argument
|
||||
_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;
|
||||
|
||||
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
|
||||
# load yaml file
|
||||
object = importYAML i;
|
||||
groupVersion = splitString "/" object.apiVersion;
|
||||
name = object.metadata.name;
|
||||
version = last groupVersion;
|
||||
group =
|
||||
if version == (head groupVersion)
|
||||
then "core" else head groupVersion;
|
||||
kind = object.kind;
|
||||
in
|
||||
{
|
||||
resources.${group}.${version}.${kind}.${name} = object;
|
||||
})
|
||||
cfg.imports));
|
||||
|
||||
kubernetes.objects = flatten (mapAttrsToList
|
||||
(_: type:
|
||||
mapAttrsToList (name: resource: moduleToAttrs resource)
|
||||
cfg.api.resources.${type.group}.${type.version}.${type.kind}
|
||||
)
|
||||
cfg.api.types);
|
||||
|
||||
kubernetes.generated = k8s.mkHashedList {
|
||||
items = config.kubernetes.objects;
|
||||
labels."kubenix/project-name" = config.kubenix.project;
|
||||
labels."kubenix/k8s-version" = config.kubernetes.version;
|
||||
};
|
||||
|
||||
kubernetes.result =
|
||||
pkgs.writeText "${config.kubenix.project}-generated.json" (builtins.toJSON cfg.generated);
|
||||
|
||||
kubernetes.resultYAML =
|
||||
toMultiDocumentYaml "${config.kubenix.project}-generated.yaml" (config.kubernetes.objects);
|
||||
};
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
imports = [ ./base.nix ];
|
||||
|
||||
options.submodule = {
|
||||
name = mkOption {
|
||||
description = "Module name";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
description = "Module description";
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
|
||||
version = mkOption {
|
||||
description = "Module version";
|
||||
type = types.str;
|
||||
default = "1.0.0";
|
||||
};
|
||||
|
||||
tags = mkOption {
|
||||
description = "List of submodule tags";
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
exports = mkOption {
|
||||
description = "Attribute set of functions to export";
|
||||
type = types.attrs;
|
||||
default = { };
|
||||
};
|
||||
|
||||
passthru = mkOption {
|
||||
description = "Attribute set to passthru";
|
||||
type = types.attrs;
|
||||
default = { };
|
||||
};
|
||||
|
||||
args._empty = mkOption { };
|
||||
};
|
||||
|
||||
config._module.args.args = config.submodule.args;
|
||||
config._m.features = [ "submodule" ];
|
||||
}
|
||||
|
|
@ -1,361 +0,0 @@
|
|||
{ 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);
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
{ nixosPath, config, pkgs, lib, kubenix, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.testing;
|
||||
|
||||
testModule = {
|
||||
imports = [ ./evalTest.nix ];
|
||||
|
||||
# passthru testing configuration
|
||||
config._module.args = {
|
||||
inherit pkgs kubenix;
|
||||
testing = cfg;
|
||||
};
|
||||
};
|
||||
|
||||
isTestEnabled = test:
|
||||
(cfg.enabledTests == null || elem test.name cfg.enabledTests) && test.enable;
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./docker.nix
|
||||
./driver/kubetest.nix
|
||||
./runtime/local.nix
|
||||
./runtime/nixos-k8s.nix
|
||||
];
|
||||
|
||||
options.testing = {
|
||||
name = mkOption {
|
||||
description = "Testing suite name";
|
||||
type = types.str;
|
||||
default = "default";
|
||||
};
|
||||
|
||||
throwError = mkOption {
|
||||
description = "Whether to throw error";
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
defaults = mkOption {
|
||||
description = "List of defaults to apply to tests";
|
||||
type = types.listOf (types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
features = mkOption {
|
||||
description = "List of features that test has to have to apply defaults";
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
default = mkOption {
|
||||
description = "Default to apply to test";
|
||||
type = types.unspecified;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
tests = mkOption {
|
||||
description = "List of test cases";
|
||||
default = [ ];
|
||||
type = types.listOf (types.coercedTo types.path
|
||||
(module: {
|
||||
inherit module;
|
||||
})
|
||||
(types.submodule testModule));
|
||||
apply = tests: filter isTestEnabled tests;
|
||||
};
|
||||
|
||||
testsByName = mkOption {
|
||||
description = "Tests by name";
|
||||
type = types.attrsOf types.attrs;
|
||||
default = listToAttrs (map (test: nameValuePair test.name test) cfg.tests);
|
||||
};
|
||||
|
||||
enabledTests = mkOption {
|
||||
description = "List of enabled tests (by default all tests are enabled)";
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
};
|
||||
|
||||
args = mkOption {
|
||||
description = "Attribute set of extra args passed to tests";
|
||||
type = types.attrs;
|
||||
default = { };
|
||||
};
|
||||
|
||||
success = mkOption {
|
||||
internal = true; # read only property
|
||||
description = "Whether testing was a success";
|
||||
type = types.bool;
|
||||
default = all (test: test.success) cfg.tests;
|
||||
};
|
||||
|
||||
testScript = mkOption {
|
||||
internal = true; # set by test driver
|
||||
type = types.package;
|
||||
description = "Script to run e2e tests";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
with import ../../lib/docker { inherit lib pkgs; };
|
||||
let
|
||||
testing = config.testing;
|
||||
|
||||
allImages = unique ( flatten (map (t: t.evaled.config.docker.export or [ ]) testing.tests));
|
||||
|
||||
cfg = config.testing.docker;
|
||||
|
||||
in
|
||||
{
|
||||
options.testing.docker = {
|
||||
registryUrl = mkOption {
|
||||
description = "Docker registry url";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
images = mkOption {
|
||||
description = "List of images to export";
|
||||
type = types.listOf types.package;
|
||||
};
|
||||
|
||||
copyScript = mkOption {
|
||||
description = "Script to copy images to registry";
|
||||
type = types.package;
|
||||
};
|
||||
};
|
||||
|
||||
config.testing.docker = {
|
||||
images = allImages;
|
||||
|
||||
copyScript = copyDockerImages {
|
||||
images = cfg.images;
|
||||
dest = "docker://" + cfg.registryUrl;
|
||||
};
|
||||
};
|
||||
|
||||
config.testing.defaults = [{
|
||||
features = [ "docker" ];
|
||||
default = {
|
||||
docker.registry.url = cfg.registryUrl;
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
testing = config.testing;
|
||||
cfg = testing.driver.kubetest;
|
||||
|
||||
kubetest = import ./kubetestdrv.nix { inherit pkgs; };
|
||||
|
||||
pythonEnv = pkgs.python38.withPackages (ps: with ps; [
|
||||
pytest
|
||||
kubetest
|
||||
kubernetes
|
||||
] ++ cfg.extraPackages);
|
||||
|
||||
toTestScript = t:
|
||||
if isString t.script
|
||||
then
|
||||
pkgs.writeText "${t.name}.py" ''
|
||||
${cfg.defaultHeader}
|
||||
${t.script}
|
||||
''
|
||||
else t.script;
|
||||
|
||||
tests = pkgs.linkFarm "${testing.name}-tests" (
|
||||
map
|
||||
(t: {
|
||||
path = toTestScript t;
|
||||
name = "${t.name}_test.py";
|
||||
})
|
||||
(filter (t: t.script != null) testing.tests)
|
||||
);
|
||||
|
||||
testScript = pkgs.writeScript "test-${testing.name}.sh" ''
|
||||
#!/usr/bin/env bash
|
||||
${pythonEnv}/bin/pytest -p no:cacheprovider ${tests} $@
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
options.testing.driver.kubetest = {
|
||||
defaultHeader = mkOption {
|
||||
type = types.lines;
|
||||
description = "Default test header";
|
||||
default = ''
|
||||
import pytest
|
||||
'';
|
||||
};
|
||||
|
||||
extraPackages = mkOption {
|
||||
type = types.listOf types.package;
|
||||
description = "Extra packages to pass to tests";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config.testing.testScript = testScript;
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{ 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;
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
{ lib, config, testing, kubenix, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
modules = [
|
||||
# testing module
|
||||
config.module
|
||||
|
||||
./test-options.nix
|
||||
../base.nix
|
||||
|
||||
# passthru some options to test
|
||||
{
|
||||
config = {
|
||||
kubenix.project = mkDefault config.name;
|
||||
_module.args = {
|
||||
inherit kubenix;
|
||||
test = config;
|
||||
} // testing.args;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# eval without checking
|
||||
evaled' = kubenix.evalModules {
|
||||
check = false;
|
||||
inherit modules;
|
||||
};
|
||||
|
||||
# test configuration
|
||||
testConfig = evaled'.config.test;
|
||||
|
||||
# test features
|
||||
testFeatures = evaled'.config._m.features;
|
||||
|
||||
# defaults that can be applied on tests
|
||||
defaults =
|
||||
filter
|
||||
(d:
|
||||
(intersectLists d.features testFeatures) == d.features ||
|
||||
(length d.features) == 0
|
||||
)
|
||||
testing.defaults;
|
||||
|
||||
# add default modules to all modules
|
||||
modulesWithDefaults = modules ++ (map (d: d.default) defaults);
|
||||
|
||||
# evaled test
|
||||
evaled =
|
||||
let
|
||||
evaled' = kubenix.evalModules {
|
||||
modules = modulesWithDefaults;
|
||||
};
|
||||
in
|
||||
if testing.throwError then evaled'
|
||||
else if (builtins.tryEval evaled'.config.test.assertions).success
|
||||
then evaled' else null;
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
module = mkOption {
|
||||
description = "Module defining kubenix test";
|
||||
type = types.unspecified;
|
||||
};
|
||||
|
||||
evaled = mkOption {
|
||||
description = "Test evaulation result";
|
||||
type = types.nullOr types.attrs;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
success = mkOption {
|
||||
description = "Whether test assertions were successfull";
|
||||
type = types.bool;
|
||||
internal = true;
|
||||
default = false;
|
||||
};
|
||||
|
||||
# transparently forwarded from the test's `test` attribute for ease of access
|
||||
name = mkOption {
|
||||
description = "test name";
|
||||
type = types.str;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
description = "test description";
|
||||
type = types.str;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
enable = mkOption {
|
||||
description = "Whether to enable test";
|
||||
type = types.bool;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
assertions = mkOption {
|
||||
description = "Test result";
|
||||
type = types.unspecified;
|
||||
internal = true;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
script = mkOption {
|
||||
description = "Test script to use for e2e test";
|
||||
type = types.nullOr (types.either types.lines types.path);
|
||||
internal = true;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
{
|
||||
inherit evaled;
|
||||
inherit (testConfig) name description enable;
|
||||
}
|
||||
|
||||
# if test is evaled check assertions
|
||||
(mkIf (config.evaled != null) {
|
||||
inherit (evaled.config.test) assertions script;
|
||||
|
||||
# if all assertions are true, test is successfull
|
||||
success = all (el: el.assertion) config.assertions;
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
testing = config.testing;
|
||||
|
||||
script = pkgs.writeScript "run-local-k8s-tests-${testing.name}.sh" ''
|
||||
#!${pkgs.runtimeShell}
|
||||
|
||||
set -e
|
||||
|
||||
KUBECONFIG=''${KUBECONFIG:-~/.kube/config}
|
||||
SKOPEOARGS=""
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
--kubeconfig)
|
||||
KUBECONFIG=$2
|
||||
shift 2
|
||||
;;
|
||||
--skopeo-args)
|
||||
SKOPEOARGS=$2
|
||||
shift 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "--> copying docker images to registry"
|
||||
${testing.docker.copyScript} $SKOPEOARGS
|
||||
|
||||
echo "--> running tests"
|
||||
${testing.testScript} --kube-config=$KUBECONFIG
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.testing.runtime.local = {
|
||||
script = mkOption {
|
||||
type = types.package;
|
||||
description = "Runtime script";
|
||||
};
|
||||
};
|
||||
|
||||
config.testing.runtime.local.script = script;
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
# nixos-k8s implements nixos kubernetes testing runtime
|
||||
|
||||
{ nixosPath
|
||||
, config
|
||||
, pkgs
|
||||
, lib
|
||||
, ...
|
||||
}:
|
||||
|
||||
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, ... }: {
|
||||
|
||||
virtualisation = {
|
||||
memorySize = 2048;
|
||||
};
|
||||
|
||||
networking = {
|
||||
nameservers = [ "10.0.0.254" ];
|
||||
firewall = {
|
||||
trustedInterfaces = [ "docker0" "cni0" ];
|
||||
};
|
||||
};
|
||||
|
||||
services.kubernetes = {
|
||||
flannel.enable = false;
|
||||
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";
|
||||
}];
|
||||
};
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
extraConfig = "DefaultLimitNOFILE=1048576";
|
||||
# Host tools should have a chance to access guest's kube api
|
||||
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" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
script = ''
|
||||
machine1.succeed("${testing.testScript} --kube-config=${kubeconfig}")
|
||||
'';
|
||||
|
||||
test =
|
||||
with import "${nixosPath}/tests/kubernetes/base.nix" { inherit pkgs; inherit (pkgs) system; };
|
||||
mkKubernetesSingleNodeTest {
|
||||
inherit extraConfiguration;
|
||||
inherit (config.testing) name;
|
||||
test = script;
|
||||
};
|
||||
|
||||
|
||||
in
|
||||
{
|
||||
options.testing.runtime.nixos-k8s = {
|
||||
driver = mkOption {
|
||||
description = "Test driver";
|
||||
type = types.package;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
config.testing.runtime.nixos-k8s.driver = test.driver;
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.test;
|
||||
|
||||
in
|
||||
{
|
||||
options.test = {
|
||||
name = mkOption {
|
||||
description = "Test name";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
description = "Test description";
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
enable = mkOption {
|
||||
description = "Whether to enable test";
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
assertions = mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
assertion = mkOption {
|
||||
description = "assertion value";
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
message = mkOption {
|
||||
description = "assertion message";
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
});
|
||||
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
|
||||
succeed, along with associated error messages for the user.
|
||||
'';
|
||||
};
|
||||
|
||||
script = mkOption {
|
||||
description = "Test script to use for e2e test";
|
||||
type = types.nullOr (types.either types.lines types.path);
|
||||
default = null;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue