This commit is contained in:
David Arnold 2021-05-13 17:27:08 -04:00
parent c3fa598922
commit db6d83c61e
No known key found for this signature in database
GPG key ID: 6D6A936E69C59D08
53 changed files with 1916 additions and 1599 deletions

3
ci.nix
View file

@ -8,4 +8,5 @@ let
inherit pkgs lib; inherit pkgs lib;
nixosPath = "${nixpkgsSrc}/nixos"; nixosPath = "${nixpkgsSrc}/nixos";
}; };
in pkgs.recurseIntoAttrs release in
pkgs.recurseIntoAttrs release

View file

@ -7,7 +7,6 @@ in
}: }:
with lib; with lib;
let let
kubenixLib = import ./lib { inherit lib pkgs; }; kubenixLib = import ./lib { inherit lib pkgs; };
lib' = lib.extend (lib: self: import ./lib/extra.nix { inherit lib pkgs; }); lib' = lib.extend (lib: self: import ./lib/extra.nix { inherit lib pkgs; });
@ -18,28 +17,34 @@ let
# evalModules with same interface as lib.evalModules and kubenix as # evalModules with same interface as lib.evalModules and kubenix as
# special argument # special argument
evalModules = { evalModules =
module ? null, { module ? null
modules ? [module], , modules ? [ module ]
specialArgs ? defaultSpecialArgs, ... , specialArgs ? defaultSpecialArgs
}@attrs: let , ...
}@attrs:
let
attrs' = filterAttrs (n: _: n != "module") attrs; attrs' = filterAttrs (n: _: n != "module") attrs;
in lib'.evalModules (recursiveUpdate { in
lib'.evalModules (recursiveUpdate
{
inherit specialArgs modules; inherit specialArgs modules;
args = { args = {
inherit pkgs; inherit pkgs;
name = "default"; name = "default";
}; };
} attrs'); }
attrs');
modules = import ./modules; modules = import ./modules;
# legacy support for buildResources # legacy support for buildResources
buildResources = { buildResources =
configuration ? {}, { configuration ? { }
writeJSON ? true, , writeJSON ? true
writeHash ? true , writeHash ? true
}: let }:
let
evaled = evalModules { evaled = evalModules {
modules = [ modules = [
configuration configuration
@ -53,11 +58,13 @@ let
if writeJSON if writeJSON
then pkgs.writeText "resources.json" (builtins.toJSON generated) then pkgs.writeText "resources.json" (builtins.toJSON generated)
else generated; else generated;
in result; in
result;
kubenix = { kubenix = {
inherit evalModules buildResources modules; inherit evalModules buildResources modules;
lib = kubenixLib; lib = kubenixLib;
}; };
in kubenix in
kubenix

View file

@ -1,10 +1,10 @@
{ config, lib, pkgs, kubenix, ... }: { config, lib, pkgs, kubenix, ... }:
with lib; with lib;
let let
nginx = pkgs.callPackage ./image.nix { }; nginx = pkgs.callPackage ./image.nix { };
in { in
{
imports = with kubenix.modules; [ k8s docker ]; imports = with kubenix.modules; [ k8s docker ];
docker.images.nginx.image = nginx; docker.images.nginx.image = nginx;

View file

@ -1,14 +1,16 @@
{ pkgs ? import <nixpkgs> { }, lib ? pkgs.lib, spec ? ./istio-schema.json }: { pkgs ? import <nixpkgs> { }, lib ? pkgs.lib, spec ? ./istio-schema.json }:
with lib; with lib;
let let
gen = rec { gen = rec {
mkMerge = values: ''mkMerge [${concatMapStrings (value: " mkMerge = values: ''mkMerge [${concatMapStrings
(value: "
${value} ${value}
") values}]''; ")
values}]'';
toNixString = value: if isAttrs value || isList value toNixString = value:
if isAttrs value || isList value
then builtins.toJSON value then builtins.toJSON value
else if isString value else if isString value
then ''"${value}"'' then ''"${value}"''
@ -18,11 +20,11 @@ let
removeEmptyLines = str: concatStringsSep "\n" (filter (l: (builtins.match "( |)+" l) == null) (splitString "\n" str)); removeEmptyLines = str: concatStringsSep "\n" (filter (l: (builtins.match "( |)+" l) == null) (splitString "\n" str));
mkOption = { mkOption =
description ? null, { description ? null
type ? null, , type ? null
default ? null, , default ? null
apply ? null , apply ? null
}: removeEmptyLines ''mkOption { }: removeEmptyLines ''mkOption {
${optionalString (description != null) "description = ${builtins.toJSON description};"} ${optionalString (description != null) "description = ${builtins.toJSON description};"}
${optionalString (type != null) ''type = ${type};''} ${optionalString (type != null) ''type = ${type};''}
@ -81,7 +83,8 @@ let
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path)); fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
genDefinitions = swagger: with gen; (mapAttrs (name: definition: genDefinitions = swagger: with gen; (mapAttrs
(name: definition:
# if $ref is in definition it means it's an alias of other definition # if $ref is in definition it means it's an alias of other definition
if hasAttr "$ref" definition if hasAttr "$ref" definition
then definitions."${refDefinition definition}" then definitions."${refDefinition definition}"
@ -92,7 +95,8 @@ let
} }
else { else {
options = mapAttrs (propName: property: options = mapAttrs
(propName: property:
let let
isRequired = elem propName (definition.required or [ ]); isRequired = elem propName (definition.required or [ ]);
requiredOrNot = type: if isRequired then type else types.nullOr type; requiredOrNot = type: if isRequired then type else types.nullOr type;
@ -129,9 +133,11 @@ let
# if x-kubernetes-patch-merge-key is set then make it an # if x-kubernetes-patch-merge-key is set then make it an
# attribute set of submodules # attribute set of submodules
if hasAttr "x-kubernetes-patch-merge-key" property if hasAttr "x-kubernetes-patch-merge-key" property
then let then
let
mergeKey = property."x-kubernetes-patch-merge-key"; mergeKey = property."x-kubernetes-patch-merge-key";
in { in
{
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey); type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey);
apply = attrsToList; apply = attrsToList;
} }
@ -175,45 +181,59 @@ let
else { else {
type = requiredOrNot (mapType property); type = requiredOrNot (mapType property);
}; };
in mkOption ({ in
mkOption ({
description = property.description or ""; description = property.description or "";
} // optionProperties) } // optionProperties)
) definition.properties; )
definition.properties;
config = config =
let let
optionalProps = filterAttrs (propName: property: optionalProps = filterAttrs
(propName: property:
!(elem propName (definition.required or [ ])) !(elem propName (definition.required or [ ]))
) definition.properties; )
in mapAttrs (name: property: mkOverride 1002 null) optionalProps; definition.properties;
in
mapAttrs (name: property: mkOverride 1002 null) optionalProps;
} }
) swagger.definitions); )
swagger.definitions);
genResources = swagger: (mapAttrsToList (name: property: rec { genResources = swagger: (mapAttrsToList
(name: property: rec {
splittedType = splitString "." (removePrefix "me.snowdrop.istio.api." property.javaType); splittedType = splitString "." (removePrefix "me.snowdrop.istio.api." property.javaType);
group = (concatStringsSep "." (take ((length splittedType) - 2) splittedType)) + ".istio.io"; group = (concatStringsSep "." (take ((length splittedType) - 2) splittedType)) + ".istio.io";
kind = removeSuffix "Spec" (last splittedType); kind = removeSuffix "Spec" (last splittedType);
version = last (take ((length splittedType) - 1) splittedType); version = last (take ((length splittedType) - 1) splittedType);
ref = removePrefix "#/definitions/" property."$ref"; ref = removePrefix "#/definitions/" property."$ref";
}) })
(filterAttrs (name: property: (filterAttrs
(name: property:
(hasPrefix "me.snowdrop.istio.api" property.javaType) && (hasPrefix "me.snowdrop.istio.api" property.javaType) &&
hasSuffix "Spec" property.javaType hasSuffix "Spec" property.javaType
) swagger.properties)) ++ (mapAttrsToList (name: property: rec { )
swagger.properties)) ++ (mapAttrsToList
(name: property: rec {
splittedType = splitString "." (removePrefix "me.snowdrop.istio.mixer." property.javaType); splittedType = splitString "." (removePrefix "me.snowdrop.istio.mixer." property.javaType);
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = head (tail splittedType); kind = head (tail splittedType);
ref = removePrefix "#/definitions/" property."$ref"; ref = removePrefix "#/definitions/" property."$ref";
}) (filterAttrs (name: property: })
(filterAttrs
(name: property:
(hasPrefix "me.snowdrop.istio.mixer" property.javaType) && (hasPrefix "me.snowdrop.istio.mixer" property.javaType) &&
hasSuffix "Spec" property.javaType hasSuffix "Spec" property.javaType
) swagger.properties)); )
swagger.properties));
swagger = fetchSpecs spec; swagger = fetchSpecs spec;
definitions = genDefinitions swagger; definitions = genDefinitions swagger;
in pkgs.writeText "gen.nix" in
"# This file was generated with kubenix k8s generator, do not edit pkgs.writeText "gen.nix" ''
# This file was generated with kubenix k8s generator, do not edit
{lib, config, ... }: {lib, config, ... }:
with lib; with lib;
@ -306,27 +326,35 @@ let
); );
definitions = { definitions = {
${concatStrings (mapAttrsToList (name: value: " ${concatStrings (mapAttrsToList
(name: value: "
\"${name}\" = {${optionalString (hasAttr "options" value) " \"${name}\" = {${optionalString (hasAttr "options" value) "
options = {${concatStrings (mapAttrsToList (name: value: " options = {${concatStrings (mapAttrsToList
(name: value: "
\"${name}\" = ${value}; \"${name}\" = ${value};
") value.options)}}; ")
value.options)}};
"}${optionalString (hasAttr "config" value) " "}${optionalString (hasAttr "config" value) "
config = {${concatStrings (mapAttrsToList (name: value: " config = {${concatStrings (mapAttrsToList
(name: value: "
\"${name}\" = ${value}; \"${name}\" = ${value};
") value.config)}}; ")
value.config)}};
"}}; "}};
") definitions)} ")
definitions)}
} // (import ./overrides.nix {inheirt definitions lib;})); } // (import ./overrides.nix {inheirt definitions lib;}));
in { in {
kubernetes.customResources = [ kubernetes.customResources = [
${concatMapStrings (resource: ''{ ${concatMapStrings
(resource: ''{
group = "${resource.group}"; group = "${resource.group}";
version = "${resource.version}"; version = "${resource.version}";
kind = "${resource.kind}"; kind = "${resource.kind}";
description = ""; description = "";
module = definitions."${resource.ref}"; module = definitions."${resource.ref}";
}'') (genResources swagger)} }'')
(genResources swagger)}
]; ];
} }
" ''

View file

@ -2,17 +2,20 @@
, pkgs ? import <nixpkgs> { } , pkgs ? import <nixpkgs> { }
, lib ? pkgs.lib , lib ? pkgs.lib
, spec ? ./specs/1.21/swagger.json , spec ? ./specs/1.21/swagger.json
, ... }: , ...
}:
with lib; with lib;
let let
gen = rec { gen = rec {
mkMerge = values: ''mkMerge [${concatMapStrings (value: " mkMerge = values: ''mkMerge [${concatMapStrings
(value: "
${value} ${value}
") values}]''; ")
values}]'';
toNixString = value: if isAttrs value || isList value toNixString = value:
if isAttrs value || isList value
then builtins.toJSON value then builtins.toJSON value
else if isString value else if isString value
then ''"${value}"'' then ''"${value}"''
@ -22,11 +25,11 @@ let
removeEmptyLines = str: concatStringsSep "\n" (filter (l: (builtins.match "( |)+" l) == null) (splitString "\n" str)); removeEmptyLines = str: concatStringsSep "\n" (filter (l: (builtins.match "( |)+" l) == null) (splitString "\n" str));
mkOption = { mkOption =
description ? null, { description ? null
type ? null, , type ? null
default ? null, , default ? null
apply ? null , apply ? null
}: removeEmptyLines ''mkOption { }: removeEmptyLines ''mkOption {
${optionalString (description != null) "description = ${builtins.toJSON description};"} ${optionalString (description != null) "description = ${builtins.toJSON description};"}
${optionalString (type != null) ''type = ${type};''} ${optionalString (type != null) ''type = ${type};''}
@ -83,7 +86,8 @@ let
refType = attr: head (tail (tail (splitString "/" attr."$ref"))); refType = attr: head (tail (tail (splitString "/" attr."$ref")));
compareVersions = ver1: ver2: let compareVersions = ver1: ver2:
let
getVersion = v: substring 1 10 v; getVersion = v: substring 1 10 v;
splitVersion = v: builtins.splitVersion (getVersion v); splitVersion = v: builtins.splitVersion (getVersion v);
isAlpha = v: elem "alpha" (splitVersion v); isAlpha = v: elem "alpha" (splitVersion v);
@ -94,13 +98,15 @@ let
v1 = patchVersion ver1; v1 = patchVersion ver1;
v2 = patchVersion ver2; v2 = patchVersion ver2;
in builtins.compareVersions v1 v2; in
builtins.compareVersions v1 v2;
fixJSON = content: replaceStrings [ "\\u" ] [ "u" ] content; fixJSON = content: replaceStrings [ "\\u" ] [ "u" ] content;
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path)); fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
genDefinitions = swagger: with gen; mapAttrs (name: definition: genDefinitions = swagger: with gen; mapAttrs
(name: definition:
# if $ref is in definition it means it's an alias of other definition # if $ref is in definition it means it's an alias of other definition
if hasAttr "$ref" definition if hasAttr "$ref" definition
then definitions."${refDefinition definition}" then definitions."${refDefinition definition}"
@ -110,7 +116,8 @@ let
# in other case it's an actual definition # in other case it's an actual definition
else { else {
options = mapAttrs (propName: property: options = mapAttrs
(propName: property:
let let
isRequired = elem propName (definition.required or [ ]); isRequired = elem propName (definition.required or [ ]);
requiredOrNot = type: if isRequired then type else types.nullOr type; requiredOrNot = type: if isRequired then type else types.nullOr type;
@ -144,9 +151,11 @@ let
# if x-kubernetes-patch-merge-key is set then make it an # if x-kubernetes-patch-merge-key is set then make it an
# attribute set of submodules # attribute set of submodules
if hasAttr "x-kubernetes-patch-merge-key" property if hasAttr "x-kubernetes-patch-merge-key" property
then let then
let
mergeKey = property."x-kubernetes-patch-merge-key"; mergeKey = property."x-kubernetes-patch-merge-key";
in { in
{
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey); type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey);
apply = attrsToList; apply = attrsToList;
} }
@ -190,39 +199,53 @@ let
else { else {
type = requiredOrNot (mapType property); type = requiredOrNot (mapType property);
}; };
in mkOption ({ in
mkOption ({
description = property.description or ""; description = property.description or "";
} // optionProperties) } // optionProperties)
) definition.properties; )
definition.properties;
config = config =
let let
optionalProps = filterAttrs (propName: property: optionalProps = filterAttrs
(propName: property:
!(elem propName (definition.required or [ ])) !(elem propName (definition.required or [ ]))
) definition.properties; )
in mapAttrs (name: property: mkOverride 1002 null) optionalProps; definition.properties;
in
mapAttrs (name: property: mkOverride 1002 null) optionalProps;
} }
) swagger.definitions; )
swagger.definitions;
mapCharPairs = f: s1: s2: concatStrings (imap0 (i: c1: mapCharPairs = f: s1: s2: concatStrings (imap0
(i: c1:
f i c1 (if i >= stringLength s2 then "" else elemAt (stringToCharacters s2) i) f i c1 (if i >= stringLength s2 then "" else elemAt (stringToCharacters s2) i)
) (stringToCharacters s1)); )
(stringToCharacters s1));
getAttrName = resource: kind: getAttrName = resource: kind:
mapCharPairs (i: c1: c2: mapCharPairs
(i: c1: c2:
if hasPrefix "API" kind && i == 0 then "A" if hasPrefix "API" kind && i == 0 then "A"
else if i == 0 then c1 else if i == 0 then c1
else if c2 == "" || (toLower c2) != c1 then c1 else if c2 == "" || (toLower c2) != c1 then c1
else c2 else c2
) resource kind; )
resource
kind;
genResourceTypes = swagger: mapAttrs' (name: path: let genResourceTypes = swagger: mapAttrs'
(name: path:
let
ref = refType (head path.post.parameters).schema; ref = refType (head path.post.parameters).schema;
group' = path.post."x-kubernetes-group-version-kind".group; group' = path.post."x-kubernetes-group-version-kind".group;
version' = path.post."x-kubernetes-group-version-kind".version; version' = path.post."x-kubernetes-group-version-kind".version;
kind' = path.post."x-kubernetes-group-version-kind".kind; kind' = path.post."x-kubernetes-group-version-kind".kind;
name' = last (splitString "/" name); name' = last (splitString "/" name);
attrName = getAttrName name' kind'; attrName = getAttrName name' kind';
in nameValuePair ref { in
nameValuePair ref {
inherit ref attrName; inherit ref attrName;
name = name'; name = name';
@ -232,33 +255,48 @@ let
description = swagger.definitions.${ref}.description; description = swagger.definitions.${ref}.description;
defintion = refDefinition (head path.post.parameters).schema; defintion = refDefinition (head path.post.parameters).schema;
}) })
(filterAttrs (name: path: (filterAttrs
(name: path:
hasAttr "post" path && hasAttr "post" path &&
path.post."x-kubernetes-action" == "post" path.post."x-kubernetes-action" == "post"
) swagger.paths); )
swagger.paths);
swagger = fetchSpecs spec; swagger = fetchSpecs spec;
definitions = genDefinitions swagger; definitions = genDefinitions swagger;
resourceTypes = genResourceTypes swagger; resourceTypes = genResourceTypes swagger;
resourceTypesByKind = zipAttrs (mapAttrsToList (name: resourceType: { resourceTypesByKind = zipAttrs (mapAttrsToList
(name: resourceType: {
${resourceType.kind} = resourceType; ${resourceType.kind} = resourceType;
}) resourceTypes); })
resourceTypes);
resourcesTypesByKindSortByVersion = mapAttrs (kind: resourceTypes: resourcesTypesByKindSortByVersion = mapAttrs
reverseList (sort (r1: r2: (kind: resourceTypes:
reverseList (sort
(r1: r2:
compareVersions r1.version r2.version > 0 compareVersions r1.version r2.version > 0
) resourceTypes) )
) resourceTypesByKind; resourceTypes)
)
resourceTypesByKind;
latestResourceTypesByKind = latestResourceTypesByKind =
mapAttrs (kind: resources: last resources) resourcesTypesByKindSortByVersion; mapAttrs (kind: resources: last resources) resourcesTypesByKindSortByVersion;
genResourceOptions = resource: with gen; let genResourceOptions = resource: with gen; let
submoduleForDefinition' = definition: let submoduleForDefinition' = definition:
in submoduleForDefinition let
definition.ref definition.name definition.kind definition.group definition.version; in
in mkOption { submoduleForDefinition
definition.ref
definition.name
definition.kind
definition.group
definition.version;
in
mkOption {
description = resource.description; description = resource.description;
type = types.attrsOf (submoduleForDefinition' resource); type = types.attrsOf (submoduleForDefinition' resource);
default = { }; default = { };
@ -415,7 +453,9 @@ let
}; };
} }
''; '';
in pkgs.runCommand "k8s-${name}-gen.nix" { in
pkgs.runCommand "k8s-${name}-gen.nix"
{
buildInputs = [ pkgs.haskellPackages.nixfmt ]; buildInputs = [ pkgs.haskellPackages.nixfmt ];
} '' } ''
cp ${builtins.toFile "k8s-${name}-gen-raw.nix" generated} $out cp ${builtins.toFile "k8s-${name}-gen-raw.nix" generated} $out

View file

@ -4,12 +4,14 @@ with lib;
{ {
copyDockerImages = { images, dest, args ? "" }: copyDockerImages = { images, dest, args ? "" }:
pkgs.writeScript "copy-docker-images.sh" (concatMapStrings (image: '' pkgs.writeScript "copy-docker-images.sh" (concatMapStrings
(image: ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
set -e set -e
echo "copying '${image.imageName}:${image.imageTag}' to '${dest}/${image.imageName}:${image.imageTag}'" echo "copying '${image.imageName}:${image.imageTag}' to '${dest}/${image.imageName}:${image.imageTag}'"
${pkgs.skopeo}/bin/skopeo copy ${args} $@ docker-archive:${image} ${dest}/${image.imageName}:${image.imageTag} ${pkgs.skopeo}/bin/skopeo copy ${args} $@ docker-archive:${image} ${dest}/${image.imageName}:${image.imageTag}
'') images); '')
images);
} }

View file

@ -21,20 +21,25 @@ rec {
else mkOverride priority value; else mkOverride priority value;
loadYAML = path: importJSON (pkgs.runCommand "yaml-to-json" { loadYAML = path: importJSON (pkgs.runCommand "yaml-to-json"
} "${pkgs.remarshal}/bin/remarshal -i ${path} -if yaml -of json > $out"); { } "${pkgs.remarshal}/bin/remarshal -i ${path} -if yaml -of json > $out");
toYAML = config: builtins.readFile (pkgs.runCommand "to-yaml" { toYAML = config: builtins.readFile (pkgs.runCommand "to-yaml"
{
buildInputs = [ pkgs.remarshal ]; buildInputs = [ pkgs.remarshal ];
} '' } ''
remarshal -i ${pkgs.writeText "to-json" (builtins.toJSON config)} -if json -of yaml > $out remarshal -i ${pkgs.writeText "to-json" (builtins.toJSON config)} -if json -of yaml > $out
''); '');
toMultiDocumentYaml = name: documents: pkgs.runCommand name { toMultiDocumentYaml = name: documents: pkgs.runCommand name
{
buildInputs = [ pkgs.remarshal ]; buildInputs = [ pkgs.remarshal ];
} (concatMapStringsSep "\necho --- >> $out\n" (d: }
(concatMapStringsSep "\necho --- >> $out\n"
(d:
"remarshal -i ${builtins.toFile "doc" (builtins.toJSON d)} -if json -of yaml >> $out" "remarshal -i ${builtins.toFile "doc" (builtins.toJSON d)} -if json -of yaml >> $out"
) documents); )
documents);
toBase64 = value: toBase64 = value:
builtins.readFile builtins.readFile
@ -42,10 +47,13 @@ rec {
exp = base: exp: foldr (value: acc: acc * base) 1 (range 1 exp); exp = base: exp: foldr (value: acc: acc * base) 1 (range 1 exp);
octalToDecimal = value: (foldr (char: acc: { octalToDecimal = value: (foldr
(char: acc: {
i = acc.i + 1; i = acc.i + 1;
value = acc.value + (toInt char) * (exp 8 acc.i); value = acc.value + (toInt char) * (exp 8 acc.i);
}) {i = 0; value = 0;} (stringToCharacters value)).value; })
{ i = 0; value = 0; }
(stringToCharacters value)).value;
submoduleWithSpecialArgs = opts: specialArgs: submoduleWithSpecialArgs = opts: specialArgs:
let let
@ -59,12 +67,14 @@ rec {
let let
coerce = def: if isFunction def then def else { config = def; }; coerce = def: if isFunction def then def else { config = def; };
modules = opts' ++ map (def: { _file = def.file; imports = [ (coerce def.value) ]; }) defs; modules = opts' ++ map (def: { _file = def.file; imports = [ (coerce def.value) ]; }) defs;
in (evalModules { in
(evalModules {
inherit modules specialArgs; inherit modules specialArgs;
prefix = loc; prefix = loc;
}).config; }).config;
getSubOptions = prefix: (evalModules getSubOptions = prefix: (evalModules
{ modules = opts'; inherit prefix specialArgs; {
modules = opts'; inherit prefix specialArgs;
# This is a work-around due to the fact that some sub-modules, # 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" # such as the one included in an attribute set, expects a "args"
# attribute to be given to the sub-module. As the option # attribute to be given to the sub-module. As the option
@ -92,11 +102,14 @@ rec {
}; };
}; };
coerceListOfSubmodulesToAttrs = submodule: keyFn: let coerceListOfSubmodulesToAttrs = submodule: keyFn:
let
mergeValuesByFn = keyFn: values: mergeValuesByFn = keyFn: values:
listToAttrs (map (value: listToAttrs (map
(value:
nameValuePair (toString (keyFn value)) value nameValuePair (toString (keyFn value)) value
) values); )
values);
# Either value of type `finalType` or `coercedType`, the latter is # Either value of type `finalType` or `coercedType`, the latter is
# converted to `finalType` using `coerceFunc`. # converted to `finalType` using `coerceFunc`.
@ -113,14 +126,16 @@ rec {
else else
let coerced = coerceFunc val; in assert finalType.check coerced; coerced; let coerced = coerceFunc val; in assert finalType.check coerced; coerced;
in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); in
finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
getSubOptions = finalType.getSubOptions; getSubOptions = finalType.getSubOptions;
getSubModules = finalType.getSubModules; getSubModules = finalType.getSubModules;
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
typeMerge = t1: t2: null; typeMerge = t1: t2: null;
functor = (defaultFunctor name) // { wrapped = finalType; }; functor = (defaultFunctor name) // { wrapped = finalType; };
}; };
in coercedTo in
coercedTo
(types.listOf (types.submodule submodule)) (types.listOf (types.submodule submodule))
(mergeValuesByFn keyFn) (mergeValuesByFn keyFn)
(types.attrsOf (types.submodule submodule)); (types.attrsOf (types.submodule submodule));

View file

@ -16,9 +16,12 @@ with lib;
, values ? { } , values ? { }
# kubernetes version to template chart for # kubernetes version to template chart for
, kubeVersion ? null }: let , kubeVersion ? null
}:
let
valuesJsonFile = builtins.toFile "${name}-values.json" (builtins.toJSON values); valuesJsonFile = builtins.toFile "${name}-values.json" (builtins.toJSON values);
in stdenvNoCC.mkDerivation { in
stdenvNoCC.mkDerivation {
name = "${name}.json"; name = "${name}.json";
buildCommand = '' buildCommand = ''
# template helm file and write resources to yaml # template helm file and write resources to yaml

View file

@ -1,9 +1,9 @@
{ stdenvNoCC, lib, kubernetes-helm, cacert }: { stdenvNoCC, lib, kubernetes-helm, cacert }:
let let
cleanName = name: lib.replaceStrings [ "/" ] [ "-" ] name; cleanName = name: lib.replaceStrings [ "/" ] [ "-" ] name;
in { in
{
# name of the chart # name of the chart
chart chart
@ -26,7 +26,8 @@ in {
, verify ? false , verify ? false
# pass --devel to helm chart # pass --devel to helm chart
, devel ? false }: stdenvNoCC.mkDerivation { , devel ? false
}: stdenvNoCC.mkDerivation {
name = "${cleanName chart}-${if version == null then "dev" else version}"; name = "${cleanName chart}-${if version == null then "dev" else version}";
buildCommand = '' buildCommand = ''

View file

@ -1,9 +1,9 @@
{ pkgs ? import <nixpkgs> { } }: { pkgs ? import <nixpkgs> { } }:
let let
fetchhelm = pkgs.callPackage ./fetchhelm.nix { }; fetchhelm = pkgs.callPackage ./fetchhelm.nix { };
chart2json = pkgs.callPackage ./chart2json.nix { }; chart2json = pkgs.callPackage ./chart2json.nix { };
in rec { in
rec {
postgresql-chart = fetchhelm { postgresql-chart = fetchhelm {
chart = "stable/postgresql"; chart = "stable/postgresql";
version = "0.18.1"; version = "0.18.1";

View file

@ -42,14 +42,18 @@ rec {
}; };
# Creates hashed kubernetes list from a list of kubernetes objects # Creates hashed kubernetes list from a list of kubernetes objects
mkHashedList = { items, labels ? {} }: let mkHashedList = { items, labels ? { } }:
let
hash = builtins.hashString "sha1" (builtins.toJSON items); hash = builtins.hashString "sha1" (builtins.toJSON items);
labeledItems = map (item: recursiveUpdate item { labeledItems = map
(item: recursiveUpdate item {
metadata.labels."kubenix/hash" = hash; metadata.labels."kubenix/hash" = hash;
}) items; })
items;
in mkList { in
mkList {
items = labeledItems; items = labeledItems;
labels = { labels = {
"kubenix/hash" = hash; "kubenix/hash" = hash;

View file

@ -1,10 +1,10 @@
{ config, lib, pkgs, docker, ... }: { config, lib, pkgs, docker, ... }:
with lib; with lib;
let let
cfg = config.docker; cfg = config.docker;
in { in
{
imports = [ ./base.nix ]; imports = [ ./base.nix ];
options.docker = { options.docker = {

View file

@ -4,7 +4,6 @@
{ config, lib, pkgs, helm, ... }: { config, lib, pkgs, helm, ... }:
with lib; with lib;
let let
cfg = config.kubernetes.helm; cfg = config.kubernetes.helm;
@ -17,14 +16,17 @@ let
merge = loc: foldl' (res: def: recursiveUpdate res def.value) { }; merge = loc: foldl' (res: def: recursiveUpdate res def.value) { };
}; };
parseApiVersion = apiVersion: let parseApiVersion = apiVersion:
let
splitted = splitString "/" apiVersion; splitted = splitString "/" apiVersion;
in { in
{
group = if length splitted == 1 then "core" else head splitted; group = if length splitted == 1 then "core" else head splitted;
version = last splitted; version = last splitted;
}; };
in { in
{
imports = [ ./k8s.nix ]; imports = [ ./k8s.nix ];
options.kubernetes.helm = { options.kubernetes.helm = {
@ -96,15 +98,21 @@ in {
# expose helm helper methods as module argument # 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: kubernetes.api.resources = mkMerge (flatten (mapAttrsToList
map (object: let (_: instance:
map
(object:
let
apiVersion = parseApiVersion object.apiVersion; apiVersion = parseApiVersion object.apiVersion;
name = object.metadata.name; name = object.metadata.name;
in { in
{
"${apiVersion.group}"."${apiVersion.version}".${object.kind}."${name}" = mkMerge ([ "${apiVersion.group}"."${apiVersion.version}".${object.kind}."${name}" = mkMerge ([
object object
] ++ instance.overrides); ] ++ instance.overrides);
}) instance.objects })
) cfg.instances)); instance.objects
)
cfg.instances));
}; };
} }

View file

@ -3,7 +3,8 @@
with lib; with lib;
{ {
"istio_networking_v1alpha3_StringMatch" = recursiveUpdate (recursiveUpdate "istio_networking_v1alpha3_StringMatch" = recursiveUpdate
(recursiveUpdate
definitions."istio_networking_v1alpha3_StringMatch_Exact" definitions."istio_networking_v1alpha3_StringMatch_Exact"
definitions."istio_networking_v1alpha3_StringMatch_Prefix" definitions."istio_networking_v1alpha3_StringMatch_Prefix"
) )

View file

@ -2,7 +2,6 @@
{ lib, config, ... }: { lib, config, ... }:
with lib; with lib;
let let
types = lib.types // rec { types = lib.types // rec {
str = mkOptionType { str = mkOptionType {
@ -23,11 +22,14 @@ let
let let
coerceVal = val: coerceVal = val:
if finalType.check val then val if finalType.check val then val
else let else
let
coerced = coerceFunc val; coerced = coerceFunc val;
in assert finalType.check coerced; coerced; in
assert finalType.check coerced; coerced;
in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); in
finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
getSubOptions = finalType.getSubOptions; getSubOptions = finalType.getSubOptions;
getSubModules = finalType.getSubModules; getSubModules = finalType.getSubModules;
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
@ -44,11 +46,13 @@ let
mergeValuesByKey = mergeKey: values: mergeValuesByKey = mergeKey: values:
listToAttrs (map listToAttrs (map
(value: nameValuePair ( (value: nameValuePair
(
if isAttrs value.${mergeKey} if isAttrs value.${mergeKey}
then toString value.${mergeKey}.content then toString value.${mergeKey}.content
else (toString value.${mergeKey}) else (toString value.${mergeKey})
) value) )
value)
values); values);
submoduleOf = ref: types.submodule ({ name, ... }: { submoduleOf = ref: types.submodule ({ name, ... }: {
@ -56,12 +60,14 @@ let
config = definitions."${ref}".config; config = definitions."${ref}".config;
}); });
submoduleWithMergeOf = ref: mergeKey: types.submodule ({name, ...}: let submoduleWithMergeOf = ref: mergeKey: types.submodule ({ name, ... }:
let
convertName = name: convertName = name:
if definitions."${ref}".options.${mergeKey}.type == types.int if definitions."${ref}".options.${mergeKey}.type == types.int
then toInt name then toInt name
else name; else name;
in { in
{
options = definitions."${ref}".options; options = definitions."${ref}".options;
config = definitions."${ref}".config // { config = definitions."${ref}".config // {
${mergeKey} = mkOverride 1002 (convertName name); ${mergeKey} = mkOverride 1002 (convertName name);
@ -4698,7 +4704,8 @@ let
}; };
} // (import ./istio-overrides.nix { inherit definitions lib; }); } // (import ./istio-overrides.nix { inherit definitions lib; });
in { in
{
kubernetes.customResources = [ kubernetes.customResources = [
{ {
group = "networking.istio.io"; group = "networking.istio.io";
@ -4706,211 +4713,246 @@ in {
kind = "DestinationRule"; kind = "DestinationRule";
description = ""; description = "";
module = definitions."istio_networking_v1alpha3_DestinationRule"; module = definitions."istio_networking_v1alpha3_DestinationRule";
}{ }
{
group = "networking.istio.io"; group = "networking.istio.io";
version = "v1alpha3"; version = "v1alpha3";
kind = "EnvoyFilter"; kind = "EnvoyFilter";
description = ""; description = "";
module = definitions."istio_networking_v1alpha3_EnvoyFilter"; module = definitions."istio_networking_v1alpha3_EnvoyFilter";
}{ }
{
group = "networking.istio.io"; group = "networking.istio.io";
version = "v1alpha3"; version = "v1alpha3";
kind = "Gateway"; kind = "Gateway";
description = ""; description = "";
module = definitions."istio_networking_v1alpha3_Gateway"; module = definitions."istio_networking_v1alpha3_Gateway";
}{ }
{
group = "authentication.istio.io"; group = "authentication.istio.io";
version = "v1alpha1"; version = "v1alpha1";
kind = "Policy"; kind = "Policy";
description = ""; description = "";
module = definitions."istio_authentication_v1alpha1_Policy"; module = definitions."istio_authentication_v1alpha1_Policy";
}{ }
{
group = "rbac.istio.io"; group = "rbac.istio.io";
version = "v1alpha1"; version = "v1alpha1";
kind = "RbacConfig"; kind = "RbacConfig";
description = ""; description = "";
module = definitions."istio_rbac_v1alpha1_RbacConfig"; module = definitions."istio_rbac_v1alpha1_RbacConfig";
}{ }
{
group = "policy.istio.io"; group = "policy.istio.io";
version = "v1beta1"; version = "v1beta1";
kind = "Rule"; kind = "Rule";
description = ""; description = "";
module = definitions."istio_policy_v1beta1_Rule"; module = definitions."istio_policy_v1beta1_Rule";
}{ }
{
group = "networking.istio.io"; group = "networking.istio.io";
version = "v1alpha3"; version = "v1alpha3";
kind = "ServiceEntry"; kind = "ServiceEntry";
description = ""; description = "";
module = definitions."istio_networking_v1alpha3_ServiceEntry"; module = definitions."istio_networking_v1alpha3_ServiceEntry";
}{ }
{
group = "rbac.istio.io"; group = "rbac.istio.io";
version = "v1alpha1"; version = "v1alpha1";
kind = "ServiceRole"; kind = "ServiceRole";
description = ""; description = "";
module = definitions."istio_rbac_v1alpha1_ServiceRole"; module = definitions."istio_rbac_v1alpha1_ServiceRole";
}{ }
{
group = "rbac.istio.io"; group = "rbac.istio.io";
version = "v1alpha1"; version = "v1alpha1";
kind = "ServiceRoleBinding"; kind = "ServiceRoleBinding";
description = ""; description = "";
module = definitions."istio_rbac_v1alpha1_ServiceRoleBinding"; module = definitions."istio_rbac_v1alpha1_ServiceRoleBinding";
}{ }
{
group = "networking.istio.io"; group = "networking.istio.io";
version = "v1alpha3"; version = "v1alpha3";
kind = "VirtualService"; kind = "VirtualService";
description = ""; description = "";
module = definitions."istio_networking_v1alpha3_VirtualService"; module = definitions."istio_networking_v1alpha3_VirtualService";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "apikey"; kind = "apikey";
description = ""; description = "";
module = definitions."istio_mixer_apikey_InstanceMsg"; module = definitions."istio_mixer_apikey_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "authorization"; kind = "authorization";
description = ""; description = "";
module = definitions."istio_mixer_authorization_InstanceMsg"; module = definitions."istio_mixer_authorization_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "bypass"; kind = "bypass";
description = ""; description = "";
module = definitions."istio_adapter_bypass_Params"; module = definitions."istio_adapter_bypass_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "checknothing"; kind = "checknothing";
description = ""; description = "";
module = definitions."istio_mixer_checknothing_InstanceMsg"; module = definitions."istio_mixer_checknothing_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "circonus"; kind = "circonus";
description = ""; description = "";
module = definitions."istio_adapter_circonus_Params"; module = definitions."istio_adapter_circonus_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "denier"; kind = "denier";
description = ""; description = "";
module = definitions."istio_adapter_denier_Params"; module = definitions."istio_adapter_denier_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "edge"; kind = "edge";
description = ""; description = "";
module = definitions."istio_mixer_edge_InstanceMsg"; module = definitions."istio_mixer_edge_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "fluentd"; kind = "fluentd";
description = ""; description = "";
module = definitions."istio_adapter_fluentd_Params"; module = definitions."istio_adapter_fluentd_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "kubernetesenv"; kind = "kubernetesenv";
description = ""; description = "";
module = definitions."istio_adapter_kubernetesenv_Params"; module = definitions."istio_adapter_kubernetesenv_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "listentry"; kind = "listentry";
description = ""; description = "";
module = definitions."istio_mixer_listentry_InstanceMsg"; module = definitions."istio_mixer_listentry_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "logentry"; kind = "logentry";
description = ""; description = "";
module = definitions."istio_mixer_logentry_InstanceMsg"; module = definitions."istio_mixer_logentry_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "memquota"; kind = "memquota";
description = ""; description = "";
module = definitions."istio_adapter_memquota_Params"; module = definitions."istio_adapter_memquota_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "metric"; kind = "metric";
description = ""; description = "";
module = definitions."istio_mixer_metric_InstanceMsg"; module = definitions."istio_mixer_metric_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "opa"; kind = "opa";
description = ""; description = "";
module = definitions."istio_adapter_opa_Params"; module = definitions."istio_adapter_opa_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "prometheus"; kind = "prometheus";
description = ""; description = "";
module = definitions."istio_adapter_prometheus_Params"; module = definitions."istio_adapter_prometheus_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "quota"; kind = "quota";
description = ""; description = "";
module = definitions."istio_mixer_quota_InstanceMsg"; module = definitions."istio_mixer_quota_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "rbac"; kind = "rbac";
description = ""; description = "";
module = definitions."istio_adapter_rbac_Params"; module = definitions."istio_adapter_rbac_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "redisquota"; kind = "redisquota";
description = ""; description = "";
module = definitions."istio_adapter_redisquota_Params"; module = definitions."istio_adapter_redisquota_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "reportnothing"; kind = "reportnothing";
description = ""; description = "";
module = definitions."istio_mixer_reportnothing_InstanceMsg"; module = definitions."istio_mixer_reportnothing_InstanceMsg";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "servicecontrol"; kind = "servicecontrol";
description = ""; description = "";
module = definitions."istio_adapter_servicecontrol_Params"; module = definitions."istio_adapter_servicecontrol_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "signalfx"; kind = "signalfx";
description = ""; description = "";
module = definitions."istio_adapter_signalfx_Params"; module = definitions."istio_adapter_signalfx_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "solarwinds"; kind = "solarwinds";
description = ""; description = "";
module = definitions."istio_adapter_solarwinds_Params"; module = definitions."istio_adapter_solarwinds_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "stackdriver"; kind = "stackdriver";
description = ""; description = "";
module = definitions."istio_adapter_stackdriver_Params"; module = definitions."istio_adapter_stackdriver_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "statsd"; kind = "statsd";
description = ""; description = "";
module = definitions."istio_adapter_statsd_Params"; module = definitions."istio_adapter_statsd_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "stdio"; kind = "stdio";
description = ""; description = "";
module = definitions."istio_adapter_stdio_Params"; module = definitions."istio_adapter_stdio_Params";
}{ }
{
group = "config.istio.io"; group = "config.istio.io";
version = "v1alpha2"; version = "v1alpha2";
kind = "tracespan"; kind = "tracespan";

View file

@ -4,9 +4,11 @@ stdenv.mkDerivation rec {
pname = "k3s-airgap-images"; pname = "k3s-airgap-images";
version = k3s.version; version = k3s.version;
src = let src =
let
throwError = throw "Unsupported system ${stdenv.hostPlatform.system}"; throwError = throw "Unsupported system ${stdenv.hostPlatform.system}";
in { in
{
x86_64-linux = fetchurl { x86_64-linux = fetchurl {
url = "https://github.com/rancher/k3s/releases/download/v${version}/k3s-airgap-images-amd64.tar"; url = "https://github.com/rancher/k3s/releases/download/v${version}/k3s-airgap-images-amd64.tar";
sha256 = "sha256-6kQmlpNV+4cU1Kn5lyZhutXYK5qYdey0jubzYRRF3vA="; sha256 = "sha256-6kQmlpNV+4cU1Kn5lyZhutXYK5qYdey0jubzYRRF3vA=";

View file

@ -3,19 +3,20 @@
{ options, config, lib, pkgs, k8s, ... }: { options, config, lib, pkgs, k8s, ... }:
with lib; with lib;
let let
cfg = config.kubernetes; cfg = config.kubernetes;
gvkKeyFn = type: "${type.group}/${type.version}/${type.kind}"; gvkKeyFn = type: "${type.group}/${type.version}/${type.kind}";
getDefaults = resource: group: version: kind: getDefaults = resource: group: version: kind:
catAttrs "default" (filter (default: catAttrs "default" (filter
(default:
(resource == null || default.resource == null || default.resource == resource) && (resource == null || default.resource == null || default.resource == resource) &&
(default.group == null || default.group == group) && (default.group == null || default.group == group) &&
(default.version == null || default.version == version) && (default.version == null || default.version == version) &&
(default.kind == null || default.kind == kind) (default.kind == null || default.kind == kind)
) cfg.api.defaults); )
cfg.api.defaults);
moduleToAttrs = value: moduleToAttrs = value:
if isAttrs value if isAttrs value
@ -79,7 +80,8 @@ let
types = mkOption { types = mkOption {
description = "List of registered kubernetes types"; description = "List of registered kubernetes types";
type = coerceListOfSubmodulesToAttrs { type = coerceListOfSubmodulesToAttrs
{
options = { options = {
group = mkOption { group = mkOption {
description = "Resource type group"; description = "Resource type group";
@ -106,7 +108,8 @@ let
type = types.str; type = types.str;
}; };
}; };
} gvkKeyFn; }
gvkKeyFn;
default = { }; default = { };
}; };
}; };
@ -120,24 +123,32 @@ let
indexOf = lst: value: 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 compareVersions = ver1: ver2:
let
getVersion = v: substring 1 10 v; getVersion = v: substring 1 10 v;
splittedVer1 = builtins.splitVersion (getVersion ver1); splittedVer1 = builtins.splitVersion (getVersion ver1);
splittedVer2 = builtins.splitVersion (getVersion ver2); splittedVer2 = builtins.splitVersion (getVersion ver2);
v1 = if length splittedVer1 == 1 then "${getVersion ver1}prod" else getVersion ver1; v1 = if length splittedVer1 == 1 then "${getVersion ver1}prod" else getVersion ver1;
v2 = if length splittedVer2 == 1 then "${getVersion ver2}prod" else getVersion ver2; v2 = if length splittedVer2 == 1 then "${getVersion ver2}prod" else getVersion ver2;
in builtins.compareVersions v1 v2; in
builtins.compareVersions v1 v2;
customResourceTypesByAttrName = zipAttrs (mapAttrsToList (_: resourceType: { customResourceTypesByAttrName = zipAttrs (mapAttrsToList
(_: resourceType: {
${resourceType.attrName} = resourceType; ${resourceType.attrName} = resourceType;
}) cfg.customTypes); })
cfg.customTypes);
customResourceTypesByAttrNameSortByVersion = mapAttrs (_: resourceTypes: customResourceTypesByAttrNameSortByVersion = mapAttrs
reverseList (sort (r1: r2: (_: resourceTypes:
reverseList (sort
(r1: r2:
compareVersions r1.version r2.version > 0 compareVersions r1.version r2.version > 0
) resourceTypes) )
) customResourceTypesByAttrName; resourceTypes)
)
customResourceTypesByAttrName;
latestCustomResourceTypes = latestCustomResourceTypes =
mapAttrsToList (_: resources: last resources) customResourceTypesByAttrNameSortByVersion; mapAttrsToList (_: resources: last resources) customResourceTypesByAttrNameSortByVersion;
@ -174,17 +185,24 @@ let
}; };
}; };
customResourceOptions = (mapAttrsToList (_: ct: {config, ...}: let customResourceOptions = (mapAttrsToList
(_: ct: { config, ... }:
let
module = customResourceModuleForType config ct; module = customResourceModuleForType config ct;
in { in
{
options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption { options.resources.${ct.group}.${ct.version}.${ct.kind} = mkOption {
description = ct.description; description = ct.description;
type = types.attrsOf (types.submodule module); 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; module = customResourceModuleForType config ct;
in { in
{
options.resources.${ct.attrName} = mkOption { options.resources.${ct.attrName} = mkOption {
description = ct.description; description = ct.description;
type = types.attrsOf (types.submodule module); type = types.attrsOf (types.submodule module);
@ -193,9 +211,11 @@ let
config.resources.${ct.group}.${ct.version}.${ct.kind} = config.resources.${ct.group}.${ct.version}.${ct.kind} =
mkAliasDefinitions options.resources.${ct.attrName}; mkAliasDefinitions options.resources.${ct.attrName};
}) latestCustomResourceTypes); })
latestCustomResourceTypes);
in { in
{
imports = [ ./base.nix ]; imports = [ ./base.nix ];
options.kubernetes = { options.kubernetes = {
@ -244,7 +264,8 @@ in {
customTypes = mkOption { customTypes = mkOption {
description = "List of custom resource types to make API for"; description = "List of custom resource types to make API for";
type = coerceListOfSubmodulesToAttrs { type = coerceListOfSubmodulesToAttrs
{
options = { options = {
group = mkOption { group = mkOption {
description = "Custom type group"; description = "Custom type group";
@ -284,18 +305,21 @@ in {
default = { }; default = { };
}; };
}; };
} gvkKeyFn; }
gvkKeyFn;
default = { }; default = { };
}; };
objects = mkOption { objects = mkOption {
description = "List of generated kubernetes objects"; description = "List of generated kubernetes objects";
type = types.listOf types.attrs; type = types.listOf types.attrs;
apply = items: sort (r1: r2: apply = items: sort
(r1: r2:
if elem r1.kind cfg.resourceOrder && elem r2.kind cfg.resourceOrder if elem r1.kind cfg.resourceOrder && elem r2.kind cfg.resourceOrder
then indexOf cfg.resourceOrder r1.kind < indexOf cfg.resourceOrder r2.kind then indexOf cfg.resourceOrder r1.kind < indexOf cfg.resourceOrder r2.kind
else if elem r1.kind cfg.resourceOrder then true else false else if elem r1.kind cfg.resourceOrder then true else false
) (unique items); )
(unique items);
default = [ ]; default = [ ];
}; };
@ -327,7 +351,8 @@ in {
kubernetes.version = mkDefault cfg.version; kubernetes.version = mkDefault cfg.version;
kubernetes.namespace = mkDefault cfg.namespace; kubernetes.namespace = mkDefault cfg.namespace;
}; };
} { }
{
features = [ "k8s" "submodule" ]; features = [ "k8s" "submodule" ];
module = { config, ... }: { module = { config, ... }: {
# set module defaults # set module defaults
@ -353,9 +378,11 @@ in {
kubernetes.api = mkMerge ([{ kubernetes.api = mkMerge ([{
# register custom types # register custom types
types = mapAttrsToList (_: cr: { types = mapAttrsToList
(_: cr: {
inherit (cr) name group version kind attrName; inherit (cr) name group version kind attrName;
}) cfg.customTypes; })
cfg.customTypes;
defaults = [{ defaults = [{
default = { default = {
@ -373,7 +400,9 @@ in {
}] ++ }] ++
# import of yaml files # import of yaml files
(map (i: let (map
(i:
let
# load yaml file # load yaml file
object = loadYAML i; object = loadYAML i;
groupVersion = splitString "/" object.apiVersion; groupVersion = splitString "/" object.apiVersion;
@ -383,14 +412,18 @@ in {
if version == (head groupVersion) if version == (head groupVersion)
then "core" else head groupVersion; then "core" else head groupVersion;
kind = object.kind; kind = object.kind;
in { in
{
resources.${group}.${version}.${kind}.${name} = object; resources.${group}.${version}.${kind}.${name} = object;
}) cfg.imports)); })
cfg.imports));
kubernetes.objects = flatten (mapAttrsToList (_: type: kubernetes.objects = flatten (mapAttrsToList
(_: type:
mapAttrsToList (name: resource: moduleToAttrs resource) mapAttrsToList (name: resource: moduleToAttrs resource)
cfg.api.resources.${type.group}.${type.version}.${type.kind} cfg.api.resources.${type.group}.${type.version}.${type.kind}
) cfg.api.types); )
cfg.api.types);
kubernetes.generated = k8s.mkHashedList { kubernetes.generated = k8s.mkHashedList {
items = config.kubernetes.objects; items = config.kubernetes.objects;

View file

@ -3,7 +3,6 @@
{ options, config, pkgs, lib, kubenix, ... }: { options, config, pkgs, lib, kubenix, ... }:
with lib; with lib;
let let
parentModule = module; parentModule = module;
globalConfig = config; globalConfig = config;
@ -29,7 +28,8 @@ let
else if isAttrs mkOptionDefault.module then module // attrs else if isAttrs mkOptionDefault.module then module // attrs
else module else module
); );
in [ in
[
{ {
_module.args.name = module.name; _module.args.name = module.name;
_module.args.module = module; _module.args.module = module;
@ -50,19 +50,22 @@ let
mapAttrs' (name: resource: nameValuePair "${serviceName}-${name}" resource) resources; mapAttrs' (name: resource: nameValuePair "${serviceName}-${name}" resource) resources;
# TODO: rewrite using mkOptionType # TODO: rewrite using mkOptionType
defaultModuleConfigurationOptions = mapAttrs (name: moduleDefinition: mkOption { defaultModuleConfigurationOptions = mapAttrs
(name: moduleDefinition: mkOption {
description = "Module default configuration for ${name} module"; description = "Module default configuration for ${name} module";
type = types.coercedTo types.unspecified (value: [ value ]) (types.listOf types.unspecified); type = types.coercedTo types.unspecified (value: [ value ]) (types.listOf types.unspecified);
default = [ ]; default = [ ];
apply = filter (v: v != [ ]); apply = filter (v: v != [ ]);
}) config.kubernetes.moduleDefinitions; })
config.kubernetes.moduleDefinitions;
getModuleDefinition = name: getModuleDefinition = name:
if hasAttr name config.kubernetes.moduleDefinitions if hasAttr name config.kubernetes.moduleDefinitions
then config.kubernetes.moduleDefinitions.${name} then config.kubernetes.moduleDefinitions.${name}
else throw ''requested kubernetes moduleDefinition with name "${name}" does not exist''; else throw ''requested kubernetes moduleDefinition with name "${name}" does not exist'';
in { in
{
imports = [ ./k8s.nix ]; imports = [ ./k8s.nix ];
options.kubernetes.moduleDefinitions = mkOption { options.kubernetes.moduleDefinitions = mkOption {
@ -135,9 +138,11 @@ in {
configuration = mkOption { configuration = mkOption {
description = "Module configuration"; description = "Module configuration";
type = submoduleWithSpecialArgs { type = submoduleWithSpecialArgs
{
imports = mkModuleOptions (getModuleDefinition config.module) config; imports = mkModuleOptions (getModuleDefinition config.module) config;
} { }
{
inherit kubenix; inherit kubenix;
}; };
default = { }; default = { };
@ -163,17 +168,24 @@ in {
config = { config = {
kubernetes = mkMerge [{ kubernetes = mkMerge [{
api.defaults = mapAttrsToList (attrName: default: let api.defaults = mapAttrsToList
(attrName: default:
let
type = head (mapAttrsToList (_: v: v) (filterAttrs (_: type: type.attrName == attrName) config.kubernetes.api.types)); type = head (mapAttrsToList (_: v: v) (filterAttrs (_: type: type.attrName == attrName) config.kubernetes.api.types));
in { in
{
default = { imports = default; }; default = { imports = default; };
} // (if (attrName == "all") then { } else { } // (if (attrName == "all") then { } else {
resource = type.name; resource = type.name;
})) config.kubernetes.defaults; }))
config.kubernetes.defaults;
resources = mkMerge ( resources = mkMerge (
mapAttrsToList (name: module: mapAttrsToList
mapAttrs' (_: type: let (name: module:
mapAttrs'
(_: type:
let
moduleDefinition = getModuleDefinition module.module; moduleDefinition = getModuleDefinition module.module;
moduleResources = module.configuration.kubernetes.api.resources.${type.attrName} or { }; moduleResources = module.configuration.kubernetes.api.resources.${type.attrName} or { };
@ -182,30 +194,36 @@ in {
if moduleDefinition.prefixResources && type.kind != "CustomResourceDefinition" if moduleDefinition.prefixResources && type.kind != "CustomResourceDefinition"
then prefixResources (moduleToAttrs moduleResources) name then prefixResources (moduleToAttrs moduleResources) name
else moduleToAttrs moduleResources; else moduleToAttrs moduleResources;
in nameValuePair type.attrName in
nameValuePair type.attrName
(if moduleDefinition.assignAsDefaults (if moduleDefinition.assignAsDefaults
then mkAllDefault moduleConfig 1000 then mkAllDefault moduleConfig 1000
else moduleConfig) else moduleConfig)
) module.configuration.kubernetes.api.types )
) config.kubernetes.modules module.configuration.kubernetes.api.types
)
config.kubernetes.modules
); );
# custom types created from customResourceDefinitions # custom types created from customResourceDefinitions
customTypes = customTypes =
mapAttrsToList (name: crd: { mapAttrsToList
(name: crd: {
group = crd.spec.group; group = crd.spec.group;
version = crd.spec.version; version = crd.spec.version;
kind = crd.spec.names.kind; kind = crd.spec.names.kind;
name = crd.spec.names.plural; name = crd.spec.names.plural;
attrName = mkOptionDefault name; attrName = mkOptionDefault name;
}) (config.kubernetes.resources.customResourceDefinitions or {}); })
(config.kubernetes.resources.customResourceDefinitions or { });
defaultModuleConfiguration.all = { defaultModuleConfiguration.all = {
_file = head options.kubernetes.defaultModuleConfiguration.files; _file = head options.kubernetes.defaultModuleConfiguration.files;
config.kubernetes.version = mkDefault config.kubernetes.version; config.kubernetes.version = mkDefault config.kubernetes.version;
config.kubernetes.moduleDefinitions = config.kubernetes.moduleDefinitions; config.kubernetes.moduleDefinitions = config.kubernetes.moduleDefinitions;
}; };
} { }
{
resources = mkAliasDefinitions options.kubernetes.customResources; resources = mkAliasDefinitions options.kubernetes.customResources;
}]; }];
}; };

View file

@ -1,7 +1,6 @@
{ config, options, kubenix, pkgs, lib, ... }: { config, options, kubenix, pkgs, lib, ... }:
with lib; with lib;
let let
cfg = config.submodules; cfg = config.submodules;
parentConfig = config; parentConfig = config;
@ -14,7 +13,8 @@ let
else true; else true;
getDefaults = { name, version, tags, features }: getDefaults = { name, version, tags, features }:
catAttrs "default" (filter (submoduleDefault: catAttrs "default" (filter
(submoduleDefault:
(submoduleDefault.name == null || submoduleDefault.name == name) && (submoduleDefault.name == null || submoduleDefault.name == name) &&
(matchesVersion submoduleDefault.version version) && (matchesVersion submoduleDefault.version version) &&
( (
@ -25,43 +25,57 @@ let
(length submoduleDefault.features == 0) || (length submoduleDefault.features == 0) ||
(length (intersectLists submoduleDefault.features features)) > 0 (length (intersectLists submoduleDefault.features features)) > 0
) )
) config.submodules.defaults); )
config.submodules.defaults);
specialArgs = cfg.specialArgs // { specialArgs = cfg.specialArgs // {
parentConfig = config; parentConfig = config;
}; };
findSubmodule = {name, version ? null, latest ? true}: let findSubmodule = { name, version ? null, latest ? true }:
matchingSubmodules = filter (el: let
matchingSubmodules = filter
(el:
el.definition.name == name && el.definition.name == name &&
(matchesVersion version el.definition.version) (matchesVersion version el.definition.version)
) cfg.imports; )
cfg.imports;
versionSortedSubmodules = sort (s1: s2: versionSortedSubmodules = sort
(s1: s2:
if builtins.compareVersions s1.definition.version s2.definition.version > 0 if builtins.compareVersions s1.definition.version s2.definition.version > 0
then true else false then true else false
) matchingSubmodules; )
matchingSubmodules;
matchingModule = matchingModule =
if length versionSortedSubmodules == 0 if length versionSortedSubmodules == 0
then throw "No module found ${name}/${if version == null then "latest" else version}" then throw "No module found ${name}/${if version == null then "latest" else version}"
else head versionSortedSubmodules; else head versionSortedSubmodules;
in matchingModule; in
matchingModule;
passthruConfig = mapAttrsToList (name: opt: { passthruConfig = mapAttrsToList
${name} = mkMerge (mapAttrsToList (_: inst: (name: opt: {
${name} = mkMerge (mapAttrsToList
(_: inst:
if inst.passthru.enable if inst.passthru.enable
then inst.config.submodule.passthru.${name} or { } then inst.config.submodule.passthru.${name} or { }
else { } else { }
) config.submodules.instances); )
config.submodules.instances);
_module.args = mkMerge (mapAttrsToList (_: inst: _module.args = mkMerge (mapAttrsToList
(_: inst:
if inst.passthru.enable if inst.passthru.enable
then inst.config.submodule.passthru._module.args or { } then inst.config.submodule.passthru._module.args or { }
else { } else { }
) config.submodules.instances); )
}) (removeAttrs options ["_definedNames" "_module" "_m" "submodules"]); config.submodules.instances);
in { })
(removeAttrs options [ "_definedNames" "_module" "_m" "submodules" ]);
in
{
imports = [ ./base.nix ]; imports = [ ./base.nix ];
options = { options = {
@ -124,7 +138,8 @@ in {
types.coercedTo types.coercedTo
types.path types.path
(module: { inherit module; }) (module: { inherit module; })
(types.submodule ({name, config, ...}: let (types.submodule ({ name, config, ... }:
let
evaledSubmodule' = evalModules { evaledSubmodule' = evalModules {
inherit specialArgs; inherit specialArgs;
modules = config.modules ++ [ ./base.nix ]; modules = config.modules ++ [ ./base.nix ];
@ -135,7 +150,8 @@ in {
if (!(elem "submodule" evaledSubmodule'.config._m.features)) if (!(elem "submodule" evaledSubmodule'.config._m.features))
then throw "no submodule defined" then throw "no submodule defined"
else evaledSubmodule'; else evaledSubmodule';
in { in
{
options = { options = {
module = mkOption { module = mkOption {
description = "Module defining submodule"; description = "Module defining submodule";
@ -181,7 +197,8 @@ in {
submodules.instances = mkOption { submodules.instances = mkOption {
description = "Attribute set of submodule instances"; description = "Attribute set of submodule instances";
default = { }; default = { };
type = types.attrsOf (types.submodule ({name, config, options, ...}: let type = types.attrsOf (types.submodule ({ name, config, options, ... }:
let
# submodule associated with # submodule associated with
submodule = findSubmodule { submodule = findSubmodule {
name = config.submodule; name = config.submodule;
@ -198,7 +215,8 @@ in {
tags = submoduleDefinition.tags; tags = submoduleDefinition.tags;
features = submodule.features; features = submodule.features;
}; };
in { in
{
options = { options = {
name = mkOption { name = mkOption {
description = "Submodule instance name"; description = "Submodule instance name";
@ -229,13 +247,15 @@ in {
config = mkOption { config = mkOption {
description = "Submodule instance ${config.name} for ${submoduleDefinition.name}:${submoduleDefinition.version} config"; description = "Submodule instance ${config.name} for ${submoduleDefinition.name}:${submoduleDefinition.version} config";
type = submoduleWithSpecialArgs ({...}: { type = submoduleWithSpecialArgs
({ ... }: {
imports = submodule.modules ++ defaults ++ [ ./base.nix ]; imports = submodule.modules ++ defaults ++ [ ./base.nix ];
_module.args.pkgs = pkgs; _module.args.pkgs = pkgs;
_module.args.name = config.name; _module.args.name = config.name;
_module.args.submodule = config; _module.args.submodule = config;
submodule.args = mkAliasDefinitions options.args; submodule.args = mkAliasDefinitions options.args;
}) specialArgs; })
specialArgs;
default = { }; default = { };
}; };
@ -251,9 +271,11 @@ in {
config = mkMerge ([ config = mkMerge ([
{ {
# register exported functions as args # register exported functions as args
_module.args = mkMerge (map (submodule: { _module.args = mkMerge (map
(submodule: {
${submodule.exportAs} = submodule.definition.exports; ${submodule.exportAs} = submodule.definition.exports;
}) (filter (submodule: submodule.exportAs != null) cfg.imports)); })
(filter (submodule: submodule.exportAs != null) cfg.imports));
_m.features = [ "submodules" ]; _m.features = [ "submodules" ];
@ -267,10 +289,12 @@ in {
}; };
}] }]
(map (propagate: { (map
(propagate: {
features = propagate.features; features = propagate.features;
default = propagate.module; default = propagate.module;
}) config._m.propagate) })
config._m.propagate)
]; ];
} }

View file

@ -1,7 +1,6 @@
{ nixosPath, config, pkgs, lib, kubenix, ... }: { nixosPath, config, pkgs, lib, kubenix, ... }:
with lib; with lib;
let let
cfg = config.testing; cfg = config.testing;
@ -18,7 +17,8 @@ let
isTestEnabled = test: isTestEnabled = test:
(cfg.enabledTests == null || elem test.name cfg.enabledTests) && test.enable; (cfg.enabledTests == null || elem test.name cfg.enabledTests) && test.enable;
in { in
{
imports = [ imports = [
./docker.nix ./docker.nix
./driver/kubetest.nix ./driver/kubetest.nix
@ -61,9 +61,11 @@ in {
tests = mkOption { tests = mkOption {
description = "List of test cases"; description = "List of test cases";
default = [ ]; default = [ ];
type = types.listOf (types.coercedTo types.path (module: { type = types.listOf (types.coercedTo types.path
(module: {
inherit module; inherit module;
}) (types.submodule testModule)); })
(types.submodule testModule));
apply = tests: filter isTestEnabled tests; apply = tests: filter isTestEnabled tests;
}; };

View file

@ -2,7 +2,6 @@
with lib; with lib;
with import ../../lib/docker.nix { inherit lib pkgs; }; with import ../../lib/docker.nix { inherit lib pkgs; };
let let
testing = config.testing; testing = config.testing;
@ -10,7 +9,8 @@ let
cfg = config.testing.docker; cfg = config.testing.docker;
in { in
{
options.testing.docker = { options.testing.docker = {
registryUrl = mkOption { registryUrl = mkOption {
description = "Docker registry url"; description = "Docker registry url";

View file

@ -1,7 +1,6 @@
{ lib, config, pkgs, ... }: { lib, config, pkgs, ... }:
with lib; with lib;
let let
testing = config.testing; testing = config.testing;
cfg = testing.driver.kubetest; cfg = testing.driver.kubetest;
@ -16,14 +15,16 @@ let
toTestScript = t: toTestScript = t:
if isString t.script if isString t.script
then pkgs.writeText "${t.name}.py" '' then
pkgs.writeText "${t.name}.py" ''
${cfg.defaultHeader} ${cfg.defaultHeader}
${t.script} ${t.script}
'' ''
else t.script; else t.script;
tests = pkgs.linkFarm "${testing.name}-tests" ( tests = pkgs.linkFarm "${testing.name}-tests" (
map (t: { map
(t: {
path = toTestScript t; path = toTestScript t;
name = "${t.name}_test.py"; name = "${t.name}_test.py";
}) })
@ -35,7 +36,8 @@ let
${pythonEnv}/bin/pytest -p no:cacheprovider ${tests} $@ ${pythonEnv}/bin/pytest -p no:cacheprovider ${tests} $@
''; '';
in { in
{
options.testing.driver.kubetest = { options.testing.driver.kubetest = {
defaultHeader = mkOption { defaultHeader = mkOption {
type = types.lines; type = types.lines;

View file

@ -1,7 +1,6 @@
{ lib, config, testing, kubenix, ... }: { lib, config, testing, kubenix, ... }:
with lib; with lib;
let let
modules = [ modules = [
# testing module # testing module
@ -36,16 +35,19 @@ let
# defaults that can be applied on tests # defaults that can be applied on tests
defaults = defaults =
filter (d: filter
(d:
(intersectLists d.features testFeatures) == d.features || (intersectLists d.features testFeatures) == d.features ||
(length d.features) == 0 (length d.features) == 0
) testing.defaults; )
testing.defaults;
# add default modules to all modules # add default modules to all modules
modulesWithDefaults = modules ++ (map (d: d.default) defaults); modulesWithDefaults = modules ++ (map (d: d.default) defaults);
# evaled test # evaled test
evaled = let evaled =
let
evaled' = kubenix.evalModules { evaled' = kubenix.evalModules {
modules = modulesWithDefaults; modules = modulesWithDefaults;
}; };
@ -54,7 +56,8 @@ let
else if (builtins.tryEval evaled'.config.test.assertions).success else if (builtins.tryEval evaled'.config.test.assertions).success
then evaled' else null; then evaled' else null;
in { in
{
options = { options = {
module = mkOption { module = mkOption {
description = "Module defining kubenix test"; description = "Module defining kubenix test";

View file

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

View file

@ -9,7 +9,6 @@
}: }:
with lib; with lib;
let let
testing = config.testing; testing = config.testing;
kubeconfig = "/etc/${config.services.kubernetes.pki.etcClusterAdminKubeconfig}"; kubeconfig = "/etc/${config.services.kubernetes.pki.etcClusterAdminKubeconfig}";

View file

@ -1,11 +1,11 @@
{ lib, config, pkgs, ... }: { lib, config, pkgs, ... }:
with lib; with lib;
let let
cfg = config.test; cfg = config.test;
in { in
{
options.test = { options.test = {
name = mkOption { name = mkOption {
description = "Test name"; description = "Test name";

View file

@ -27,4 +27,3 @@ stdenv.mkDerivation {
homepage = "https://github.com/kubernetes/kubectl"; homepage = "https://github.com/kubernetes/kubectl";
}; };
} }

View file

@ -8,7 +8,6 @@ in
}: }:
with lib; with lib;
let let
kubenix = import ./. { inherit pkgs; }; kubenix = import ./. { inherit pkgs; };

View file

@ -7,10 +7,10 @@
, k8sVersion ? "1.21" , k8sVersion ? "1.21"
, registryUrl ? throw "Registry url not defined" , registryUrl ? throw "Registry url not defined"
, throwError ? true # whether any testing error should throw an error , throwError ? true # whether any testing error should throw an error
, enabledTests ? null }: , enabledTests ? null
}:
with lib; with lib;
let let
images = pkgs.callPackage ./images.nix { }; images = pkgs.callPackage ./images.nix { };
@ -34,7 +34,7 @@ let
#./legacy/k8s.nix #./legacy/k8s.nix
#./legacy/crd.nix #./legacy/crd.nix
#./legacy/modules.nix #./legacy/modules.nix
./helm/simple.nix # ./helm/simple.nix
# ./istio/bookinfo.nix # infinite recursion # ./istio/bookinfo.nix # infinite recursion
./submodules/simple.nix ./submodules/simple.nix
./submodules/defaults.nix ./submodules/defaults.nix
@ -64,4 +64,5 @@ let
inherit kubenix nixosPath; inherit kubenix nixosPath;
}; };
}).config; }).config;
in pkgs.recurseIntoAttrs config.testing in
pkgs.recurseIntoAttrs config.testing

View file

@ -3,7 +3,6 @@
with lib; with lib;
with kubenix.lib; with kubenix.lib;
with pkgs.dockerTools; with pkgs.dockerTools;
let let
corev1 = config.kubernetes.api.resources.core.v1; corev1 = config.kubernetes.api.resources.core.v1;
appsv1 = config.kubernetes.api.resources.apps.v1; appsv1 = config.kubernetes.api.resources.apps.v1;
@ -31,7 +30,8 @@ let
finalImageName = "docker.io/bitnami/bitnami-shell"; finalImageName = "docker.io/bitnami/bitnami-shell";
finalImageTag = "10"; finalImageTag = "10";
}; };
in { in
{
imports = [ kubenix.modules.test kubenix.modules.helm kubenix.modules.k8s ]; imports = [ kubenix.modules.test kubenix.modules.helm kubenix.modules.k8s ];
test = { test = {
@ -44,10 +44,12 @@ in {
appsv1.StatefulSet ? "app-psql-postgresql-read" && appsv1.StatefulSet ? "app-psql-postgresql-read" &&
corev1.Secret ? "app-psql-postgresql" && corev1.Secret ? "app-psql-postgresql" &&
corev1.Service ? "app-psql-postgresql-headless"; corev1.Service ? "app-psql-postgresql-headless";
} { }
{
message = "should have values passed"; message = "should have values passed";
assertion = appsv1.StatefulSet.app-psql-postgresql-read.spec.replicas == 2; assertion = appsv1.StatefulSet.app-psql-postgresql-read.spec.replicas == 2;
} { }
{
message = "should have namespace defined"; message = "should have namespace defined";
assertion = assertion =
appsv1.StatefulSet.app-psql-postgresql-primary.metadata.namespace == "test"; appsv1.StatefulSet.app-psql-postgresql-primary.metadata.namespace == "test";

View file

@ -10,7 +10,8 @@ with lib;
contents = [ pkgs.bash pkgs.curl pkgs.cacert ]; contents = [ pkgs.bash pkgs.curl pkgs.cacert ];
}; };
nginx = let nginx =
let
nginxPort = "80"; nginxPort = "80";
nginxConf = pkgs.writeText "nginx.conf" '' nginxConf = pkgs.writeText "nginx.conf" ''
user nginx nginx; user nginx nginx;
@ -32,7 +33,8 @@ with lib;
nginxWebRoot = pkgs.writeTextDir "index.html" '' nginxWebRoot = pkgs.writeTextDir "index.html" ''
<html><body><h1>Hello from NGINX</h1></body></html> <html><body><h1>Hello from NGINX</h1></body></html>
''; '';
in dockerTools.buildLayeredImage { in
dockerTools.buildLayeredImage {
name = "xtruder/nginx"; name = "xtruder/nginx";
tag = "latest"; tag = "latest";
contents = [ pkgs.nginx ]; contents = [ pkgs.nginx ];

View file

@ -30,11 +30,14 @@
http = [{ http = [{
match = [{ match = [{
uri.exact = "/productpage"; uri.exact = "/productpage";
} { }
{
uri.exact = "/login"; uri.exact = "/login";
} { }
{
uri.exact = "/logout"; uri.exact = "/logout";
} { }
{
uri.prefix = "/api/v1/products"; uri.prefix = "/api/v1/products";
}]; }];
route = [{ route = [{
@ -63,10 +66,12 @@
subsets = [{ subsets = [{
name = "v1"; name = "v1";
labels.version = "v1"; labels.version = "v1";
} { }
{
name = "v2"; name = "v2";
labels.version = "v2"; labels.version = "v2";
} { }
{
name = "v3"; name = "v3";
labels.version = "v3"; labels.version = "v3";
}]; }];
@ -79,13 +84,16 @@
subsets = [{ subsets = [{
name = "v1"; name = "v1";
labels.version = "v1"; labels.version = "v1";
} { }
{
name = "v2"; name = "v2";
labels.version = "v2"; labels.version = "v2";
} { }
{
name = "v2-mysql"; name = "v2-mysql";
labels.version = "v2-mysql"; labels.version = "v2-mysql";
} { }
{
name = "v2-mysql-vm"; name = "v2-mysql-vm";
labels.version = "v2-mysql-vm"; labels.version = "v2-mysql-vm";
}]; }];
@ -98,7 +106,8 @@
subsets = [{ subsets = [{
name = "v1"; name = "v1";
labels.version = "v1"; labels.version = "v1";
} { }
{
name = "v2"; name = "v2";
labels.version = "v2"; labels.version = "v2";
}]; }];

View file

@ -1,10 +1,10 @@
{ config, lib, kubenix, pkgs, ... }: { config, lib, kubenix, pkgs, ... }:
with lib; with lib;
let let
latestCrontab = config.kubernetes.api.resources.cronTabs.latest; latestCrontab = config.kubernetes.api.resources.cronTabs.latest;
in { in
{
imports = with kubenix.modules; [ test k8s ]; imports = with kubenix.modules; [ test k8s ];
test = { test = {
@ -37,7 +37,8 @@ in {
}; };
}; };
} { }
{
group = "stable.example.com"; group = "stable.example.com";
version = "v2"; version = "v2";
kind = "CronTab"; kind = "CronTab";
@ -56,7 +57,8 @@ in {
}; };
}; };
}; };
} { }
{
group = "stable.example.com"; group = "stable.example.com";
version = "v3"; version = "v3";
kind = "CronTab"; kind = "CronTab";

View file

@ -1,11 +1,11 @@
{ config, lib, kubenix, ... }: { config, lib, kubenix, ... }:
with lib; with lib;
let let
pod1 = config.kubernetes.api.resources.pods.pod1; pod1 = config.kubernetes.api.resources.pods.pod1;
pod2 = config.kubernetes.api.resources.pods.pod2; pod2 = config.kubernetes.api.resources.pods.pod2;
in { in
{
imports = with kubenix.modules; [ test k8s ]; imports = with kubenix.modules; [ test k8s ];
test = { test = {
@ -14,10 +14,12 @@ in {
assertions = [{ assertions = [{
message = "Should have label set with resource"; message = "Should have label set with resource";
assertion = pod1.metadata.labels.resource-label == "value"; assertion = pod1.metadata.labels.resource-label == "value";
} { }
{
message = "Should have default label set with group, version, kind"; message = "Should have default label set with group, version, kind";
assertion = pod1.metadata.labels.gvk-label == "value"; assertion = pod1.metadata.labels.gvk-label == "value";
} { }
{
message = "Should have conditional annotation set"; message = "Should have conditional annotation set";
assertion = pod2.metadata.annotations.conditional-annotation == "value"; assertion = pod2.metadata.annotations.conditional-annotation == "value";
}]; }];
@ -32,12 +34,14 @@ in {
kubernetes.api.defaults = [{ kubernetes.api.defaults = [{
resource = "pods"; resource = "pods";
default.metadata.labels.resource-label = "value"; default.metadata.labels.resource-label = "value";
} { }
{
group = "core"; group = "core";
kind = "Pod"; kind = "Pod";
version = "v1"; version = "v1";
default.metadata.labels.gvk-label = "value"; default.metadata.labels.gvk-label = "value";
} { }
{
resource = "pods"; resource = "pods";
default = { config, ... }: { default = { config, ... }: {
config.metadata.annotations = mkIf (config.metadata.labels ? "custom-label") { config.metadata.annotations = mkIf (config.metadata.labels ? "custom-label") {

View file

@ -1,7 +1,6 @@
{ config, lib, pkgs, kubenix, images, ... }: { config, lib, pkgs, kubenix, images, ... }:
with lib; with lib;
let let
cfg = config.kubernetes.api.resources.deployments.nginx; cfg = config.kubernetes.api.resources.deployments.nginx;
image = images.nginx; image = images.nginx;
@ -21,7 +20,8 @@ let
spec.restartPolicy = "Never"; spec.restartPolicy = "Never";
}); });
in { in
{
imports = [ kubenix.modules.test kubenix.modules.k8s kubenix.modules.docker ]; imports = [ kubenix.modules.test kubenix.modules.k8s kubenix.modules.docker ];
test = { test = {
@ -35,10 +35,12 @@ in {
else if ((builtins.compareVersions config.kubernetes.version "1.8") <= 0) else if ((builtins.compareVersions config.kubernetes.version "1.8") <= 0)
then cfg.apiVersion == "apps/v1beta2" then cfg.apiVersion == "apps/v1beta2"
else cfg.apiVersion == "apps/v1"; else cfg.apiVersion == "apps/v1";
} { }
{
message = "should have corrent kind set"; message = "should have corrent kind set";
assertion = cfg.kind == "Deployment"; assertion = cfg.kind == "Deployment";
} { }
{
message = "should have replicas set"; message = "should have replicas set";
assertion = cfg.spec.replicas == 3; assertion = cfg.spec.replicas == 3;
}]; }];

View file

@ -1,11 +1,11 @@
{ config, lib, kubenix, ... }: { config, lib, kubenix, ... }:
with lib; with lib;
let let
pod = config.kubernetes.api.resources.core.v1.Pod.test; pod = config.kubernetes.api.resources.core.v1.Pod.test;
deployment = config.kubernetes.api.resources.apps.v1.Deployment.nginx-deployment; deployment = config.kubernetes.api.resources.apps.v1.Deployment.nginx-deployment;
in { in
{
imports = with kubenix.modules; [ test k8s ]; imports = with kubenix.modules; [ test k8s ];
test = { test = {
@ -15,7 +15,8 @@ in {
assertions = [{ assertions = [{
message = "Pod should have name set"; message = "Pod should have name set";
assertion = pod.metadata.name == "test"; assertion = pod.metadata.name == "test";
} { }
{
message = "Deployment should have name set"; message = "Deployment should have name set";
assertion = deployment.metadata.name == "nginx-deployment"; assertion = deployment.metadata.name == "nginx-deployment";
}]; }];

View file

@ -1,10 +1,10 @@
{ config, lib, kubenix, pkgs, ... }: { config, lib, kubenix, pkgs, ... }:
with lib; with lib;
let let
cfg = config.kubernetes.api.resources.customResourceDefinitions.crontabs; cfg = config.kubernetes.api.resources.customResourceDefinitions.crontabs;
in { in
{
imports = with kubenix.modules; [ test k8s ]; imports = with kubenix.modules; [ test k8s ];
test = { test = {

View file

@ -1,8 +1,8 @@
{ config, kubenix, ... }: { config, kubenix, ... }:
let let
cfg = config.kubernetes.api.resources.pods.nginx; cfg = config.kubernetes.api.resources.pods.nginx;
in { in
{
imports = [ kubenix.modules.test kubenix.modules.k8s ]; imports = [ kubenix.modules.test kubenix.modules.k8s ];
test = { test = {
@ -11,7 +11,8 @@ in {
assertions = [{ assertions = [{
message = "should have apiVersion and kind set"; message = "should have apiVersion and kind set";
assertion = cfg.apiVersion == "v1" && cfg.kind == "Pod"; assertion = cfg.apiVersion == "v1" && cfg.kind == "Pod";
} { }
{
message = "should have name set"; message = "should have name set";
assertion = cfg.metadata.name == "nginx"; assertion = cfg.metadata.name == "nginx";
}]; }];

View file

@ -1,10 +1,10 @@
{ name, config, lib, kubenix, images, ... }: { name, config, lib, kubenix, images, ... }:
with lib; with lib;
let let
cfg = config.submodules.instances.passthru; cfg = config.submodules.instances.passthru;
in { in
{
imports = with kubenix.modules; [ test submodules k8s docker ]; imports = with kubenix.modules; [ test submodules k8s docker ];
test = { test = {
@ -13,7 +13,8 @@ in {
assertions = [{ assertions = [{
message = "Submodule has correct name set"; message = "Submodule has correct name set";
assertion = (head config.kubernetes.objects).metadata.name == "passthru"; assertion = (head config.kubernetes.objects).metadata.name == "passthru";
} { }
{
message = "Should expose docker image"; message = "Should expose docker image";
assertion = (head config.docker.export).imageName == "xtruder/nginx"; assertion = (head config.docker.export).imageName == "xtruder/nginx";
}]; }];

View file

@ -1,16 +1,18 @@
{ options, config, lib, kubenix, pkgs, ... }: { options, config, lib, kubenix, pkgs, ... }:
with lib; with lib;
let let
findObject = { kind, name }: filter (object: findObject = { kind, name }: filter
(object:
object.kind == kind && object.metadata.name == name object.kind == kind && object.metadata.name == name
) config.kubernetes.objects; )
config.kubernetes.objects;
getObject = filter: head (findObject filter); getObject = filter: head (findObject filter);
hasObject = { kind, name }: length (findObject { inherit kind name; }) == 1; hasObject = { kind, name }: length (findObject { inherit kind name; }) == 1;
in { in
{
imports = with kubenix.modules; [ test k8s legacy ]; imports = with kubenix.modules; [ test k8s legacy ];
test = { test = {
@ -21,7 +23,8 @@ in {
message = "should define crd in module"; message = "should define crd in module";
assertion = assertion =
hasObject { kind = "SecretClaim"; name = "module-claim"; }; hasObject { kind = "SecretClaim"; name = "module-claim"; };
} { }
{
message = "should define crd in root"; message = "should define crd in root";
assertion = assertion =
hasObject { kind = "SecretClaim"; name = "root-claim"; }; hasObject { kind = "SecretClaim"; name = "root-claim"; };

View file

@ -1,10 +1,10 @@
{ config, lib, kubenix, pkgs, ... }: { config, lib, kubenix, pkgs, ... }:
with lib; with lib;
let let
cfg = config.kubernetes.api.resources.deployments.app; cfg = config.kubernetes.api.resources.deployments.app;
in { in
{
imports = with kubenix.modules; [ test k8s legacy ]; imports = with kubenix.modules; [ test k8s legacy ];
test = { test = {
@ -15,7 +15,8 @@ in {
assertion = assertion =
cfg.kind == "Deployment" && cfg.kind == "Deployment" &&
cfg.metadata.name == "app"; cfg.metadata.name == "app";
} { }
{
message = "should have correct defaults set"; message = "should have correct defaults set";
assertion = assertion =
cfg.metadata.namespace == "test" && cfg.metadata.namespace == "test" &&

View file

@ -1,16 +1,18 @@
{ options, config, lib, kubenix, pkgs, ... }: { options, config, lib, kubenix, pkgs, ... }:
with lib; with lib;
let let
findObject = { kind, name }: filter (object: findObject = { kind, name }: filter
(object:
object.kind == kind && object.metadata.name == name object.kind == kind && object.metadata.name == name
) config.kubernetes.objects; )
config.kubernetes.objects;
getObject = filter: head (findObject filter); getObject = filter: head (findObject filter);
hasObject = { kind, name }: length (findObject { inherit kind name; }) == 1; hasObject = { kind, name }: length (findObject { inherit kind name; }) == 1;
in { in
{
imports = with kubenix.modules; [ test k8s legacy ]; imports = with kubenix.modules; [ test k8s legacy ];
test = { test = {
@ -22,14 +24,14 @@ in {
hasObject { kind = "Deployment"; name = "myapp"; } && hasObject { kind = "Deployment"; name = "myapp"; } &&
hasObject { kind = "Deployment"; name = "myapp2"; } && hasObject { kind = "Deployment"; name = "myapp2"; } &&
hasObject { kind = "Deployment"; name = "myapp2-app2"; }; hasObject { kind = "Deployment"; name = "myapp2-app2"; };
} { }
{
message = "should have default labels set"; message = "should have default labels set";
assertion = assertion =
(getObject {kind = "Deployment"; name = "myapp2-app2";}) (getObject { kind = "Deployment"; name = "myapp2-app2"; }).metadata.labels.module-label or false == "value" &&
.metadata.labels.module-label or false == "value" && (getObject { kind = "Deployment"; name = "myapp2"; }).metadata.labels.module-label or false == "value";
(getObject {kind = "Deployment"; name = "myapp2";}) }
.metadata.labels.module-label or false == "value"; {
} {
message = "should passthru resources to root module"; message = "should passthru resources to root module";
assertion = assertion =
config.kubernetes.resources.deployments.myapp2-app2-app.metadata.labels.module-label or false == "value"; config.kubernetes.resources.deployments.myapp2-app2-app.metadata.labels.module-label or false == "value";

View file

@ -1,11 +1,11 @@
{ name, config, lib, kubenix, images, ... }: { name, config, lib, kubenix, images, ... }:
with lib; with lib;
let let
cfg = config.submodules.instances.test.config; cfg = config.submodules.instances.test.config;
deployment = cfg.kubernetes.api.deployments.nginx; deployment = cfg.kubernetes.api.deployments.nginx;
in { in
{
imports = [ kubenix.modules.test kubenix.module ]; imports = [ kubenix.modules.test kubenix.module ];
test = { test = {
@ -14,10 +14,12 @@ in {
assertions = [{ assertions = [{
message = "Namespace not propagated"; message = "Namespace not propagated";
assertion = deployment.metadata.namespace == "test"; assertion = deployment.metadata.namespace == "test";
} { }
{
message = "Version not propagated"; message = "Version not propagated";
assertion = cfg.kubernetes.version == config.kubernetes.version; assertion = cfg.kubernetes.version == config.kubernetes.version;
} { }
{
message = "docker image should be added to exported images"; message = "docker image should be added to exported images";
assertion = (head config.docker.export) == images.nginx; assertion = (head config.docker.export) == images.nginx;
}]; }];

View file

@ -1,7 +1,6 @@
{ name, config, lib, kubenix, ... }: { name, config, lib, kubenix, ... }:
with lib; with lib;
let let
instance1 = config.submodules.instances.instance1; instance1 = config.submodules.instances.instance1;
instance2 = config.submodules.instances.instance2; instance2 = config.submodules.instances.instance2;
@ -25,7 +24,8 @@ let
}; };
}; };
}; };
in { in
{
imports = with kubenix.modules; [ test submodules ]; imports = with kubenix.modules; [ test submodules ];
test = { test = {
@ -34,91 +34,126 @@ in {
assertions = [{ assertions = [{
message = "should apply defaults by tag1"; message = "should apply defaults by tag1";
assertion = instance1.config.submodule.args.value == "value1"; assertion = instance1.config.submodule.args.value == "value1";
} { }
{
message = "should apply defaults by tag2"; message = "should apply defaults by tag2";
assertion = instance2.config.submodule.args.value == "value2"; assertion = instance2.config.submodule.args.value == "value2";
} { }
{
message = "should apply defaults by tag2"; message = "should apply defaults by tag2";
assertion = instance3.config.submodule.args.value == "value2"; assertion = instance3.config.submodule.args.value == "value2";
} { }
{
message = "should apply defaults to all"; message = "should apply defaults to all";
assertion = assertion =
instance1.config.submodule.args.defaultValue == "value" && instance1.config.submodule.args.defaultValue == "value" &&
instance2.config.submodule.args.defaultValue == "value"; instance2.config.submodule.args.defaultValue == "value";
} { }
{
message = "instance1 and instance3 should have value of default-value"; message = "instance1 and instance3 should have value of default-value";
assertion = instance3.config.submodule.args.defaultValue == "default-value"; assertion = instance3.config.submodule.args.defaultValue == "default-value";
} { }
{
message = "should apply defaults by submodule name"; message = "should apply defaults by submodule name";
assertion = instance4.config.submodule.args.value == "value4"; assertion = instance4.config.submodule.args.value == "value4";
} { }
{
message = "should apply defaults by custom condition"; message = "should apply defaults by custom condition";
assertion = instance5.config.submodule.args.defaultValue == "my-custom-value"; assertion = instance5.config.submodule.args.defaultValue == "my-custom-value";
} { }
{
message = "should apply defaults to versioned submodule"; message = "should apply defaults to versioned submodule";
assertion = versioned-submodule.config.submodule.args.defaultValue == "versioned-submodule"; assertion = versioned-submodule.config.submodule.args.defaultValue == "versioned-submodule";
}]; }];
}; };
submodules.imports = [{ submodules.imports = [{
modules = [submodule { modules = [
submodule
{
submodule = { submodule = {
name = "submodule1"; name = "submodule1";
tags = [ "tag1" ]; tags = [ "tag1" ];
}; };
}]; }
} { ];
modules = [submodule { }
{
modules = [
submodule
{
submodule = { submodule = {
name = "submodule2"; name = "submodule2";
tags = [ "tag2" ]; tags = [ "tag2" ];
}; };
}]; }
} { ];
modules = [submodule { }
{
modules = [
submodule
{
submodule = { submodule = {
name = "submodule3"; name = "submodule3";
tags = [ "tag2" ]; tags = [ "tag2" ];
}; };
}]; }
} { ];
modules = [submodule { }
{
modules = [
submodule
{
submodule = { submodule = {
name = "submodule4"; name = "submodule4";
}; };
}]; }
} { ];
modules = [submodule { }
{
modules = [
submodule
{
submodule = { submodule = {
name = "submodule5"; name = "submodule5";
}; };
submodule.args.value = "custom-value"; submodule.args.value = "custom-value";
}]; }
} { ];
modules = [submodule { }
{
modules = [
submodule
{
submodule = { submodule = {
name = "versioned-submodule"; name = "versioned-submodule";
version = "2.0.0"; version = "2.0.0";
}; };
}]; }
];
}]; }];
submodules.defaults = [{ submodules.defaults = [{
default.submodule.args.defaultValue = mkDefault "value"; default.submodule.args.defaultValue = mkDefault "value";
} { }
{
tags = [ "tag1" ]; tags = [ "tag1" ];
default.submodule.args.value = mkDefault "value1"; default.submodule.args.value = mkDefault "value1";
} { }
{
tags = [ "tag2" ]; tags = [ "tag2" ];
default.submodule.args.value = mkDefault "value2"; default.submodule.args.value = mkDefault "value2";
} { }
{
name = "submodule4"; name = "submodule4";
default.submodule.args.value = mkDefault "value4"; default.submodule.args.value = mkDefault "value4";
} { }
{
default = { config, ... }: { default = { config, ... }: {
submodule.args.defaultValue = mkIf (config.submodule.args.value == "custom-value") "my-custom-value"; submodule.args.defaultValue = mkIf (config.submodule.args.value == "custom-value") "my-custom-value";
}; };
} { }
{
name = "versioned-submodule"; name = "versioned-submodule";
version = "2.0.0"; version = "2.0.0";
default.submodule.args.value = mkDefault "versioned"; default.submodule.args.value = mkDefault "versioned";

View file

@ -1,7 +1,6 @@
{ name, config, lib, kubenix, subm-lib, ... }: { name, config, lib, kubenix, subm-lib, ... }:
with lib; with lib;
let let
submodule = { submodule = {
imports = [ kubenix.modules.submodule ]; imports = [ kubenix.modules.submodule ];
@ -13,7 +12,8 @@ let
}; };
}; };
}; };
in { in
{
imports = with kubenix.modules; [ test submodules ]; imports = with kubenix.modules; [ test submodules ];
test = { test = {

View file

@ -1,7 +1,6 @@
{ name, config, lib, kubenix, ... }: { name, config, lib, kubenix, ... }:
with lib; with lib;
let let
submodule = { name, ... }: { submodule = { name, ... }: {
imports = [ kubenix.modules.submodule ]; imports = [ kubenix.modules.submodule ];
@ -11,7 +10,8 @@ let
passthru.global.${name} = "true"; passthru.global.${name} = "true";
}; };
}; };
in { in
{
imports = with kubenix.modules; [ test submodules ]; imports = with kubenix.modules; [ test submodules ];
options = { options = {
@ -29,10 +29,12 @@ in {
assertions = [{ assertions = [{
message = "should passthru values if passthru enabled"; message = "should passthru values if passthru enabled";
assertion = hasAttr "inst1" config.global && config.global.inst1 == "true"; assertion = hasAttr "inst1" config.global && config.global.inst1 == "true";
} { }
{
message = "should not passthru values if passthru not enabled"; message = "should not passthru values if passthru not enabled";
assertion = !(hasAttr "inst2" config.global); assertion = !(hasAttr "inst2" config.global);
} { }
{
message = "should passthru by default"; message = "should passthru by default";
assertion = hasAttr "inst3" config.global && config.global.inst3 == "true"; assertion = hasAttr "inst3" config.global && config.global.inst3 == "true";
}]; }];

View file

@ -1,11 +1,11 @@
{ name, config, lib, kubenix, ... }: { name, config, lib, kubenix, ... }:
with lib; with lib;
let let
cfg = config.submodules.instances.instance; cfg = config.submodules.instances.instance;
args = cfg.config.submodule.args; args = cfg.config.submodule.args;
in { in
{
imports = with kubenix.modules; [ test submodules ]; imports = with kubenix.modules; [ test submodules ];
test = { test = {
@ -14,19 +14,24 @@ in {
assertions = [{ assertions = [{
message = "Submodule name is set"; message = "Submodule name is set";
assertion = cfg.name == "instance"; assertion = cfg.name == "instance";
} { }
{
message = "Submodule version is set"; message = "Submodule version is set";
assertion = cfg.version == null; assertion = cfg.version == null;
} { }
{
message = "Submodule config has submodule definition"; message = "Submodule config has submodule definition";
assertion = cfg.config.submodule.name == "submodule"; assertion = cfg.config.submodule.name == "submodule";
} { }
{
message = "Should have argument set"; message = "Should have argument set";
assertion = args.value == "test"; assertion = args.value == "test";
} { }
{
message = "Should have submodule name set"; message = "Should have submodule name set";
assertion = args.name == "instance"; assertion = args.name == "instance";
} { }
{
message = "should have tag set"; message = "should have tag set";
assertion = elem "tag" (cfg.config.submodule.tags); assertion = elem "tag" (cfg.config.submodule.tags);
}]; }];

View file

@ -1,7 +1,6 @@
{ name, config, lib, kubenix, ... }: { name, config, lib, kubenix, ... }:
with lib; with lib;
let let
inst-exact = config.submodules.instances.inst-exact.config; inst-exact = config.submodules.instances.inst-exact.config;
inst-regex = config.submodules.instances.inst-regex.config; inst-regex = config.submodules.instances.inst-regex.config;
@ -17,7 +16,8 @@ let
config.submodule.name = "subm"; config.submodule.name = "subm";
}; };
in { in
{
imports = with kubenix.modules; [ test submodules ]; imports = with kubenix.modules; [ test submodules ];
test = { test = {
@ -26,10 +26,12 @@ in {
assertions = [{ assertions = [{
message = "should select exact version"; message = "should select exact version";
assertion = inst-exact.version == "1.1.0"; assertion = inst-exact.version == "1.1.0";
} { }
{
message = "should select regex version"; message = "should select regex version";
assertion = inst-regex.version == "1.2.1"; assertion = inst-regex.version == "1.2.1";
} { }
{
message = "should select latest version"; message = "should select latest version";
assertion = inst-latest.version == "1.2.1"; assertion = inst-latest.version == "1.2.1";
}]; }];
@ -39,22 +41,29 @@ in {
modules = [{ modules = [{
config.submodule.version = "1.0.0"; config.submodule.version = "1.0.0";
config.version = "1.0.0"; config.version = "1.0.0";
} submodule]; }
} { submodule];
}
{
modules = [{ modules = [{
config.submodule.version = "1.1.0"; config.submodule.version = "1.1.0";
config.version = "1.1.0"; config.version = "1.1.0";
} submodule]; }
} { submodule];
}
{
modules = [{ modules = [{
config.submodule.version = "1.2.0"; config.submodule.version = "1.2.0";
config.version = "1.2.0"; config.version = "1.2.0";
} submodule]; }
} { submodule];
}
{
modules = [{ modules = [{
config.submodule.version = "1.2.1"; config.submodule.version = "1.2.1";
config.version = "1.2.1"; config.version = "1.2.1";
} submodule]; }
submodule];
}]; }];
submodules.instances.inst-exact = { submodules.instances.inst-exact = {