switch formatting to nixpkgs-fmt

This commit is contained in:
Bryton Hall 2023-07-07 22:01:34 -04:00
parent 3598716c73
commit 2712e89716
65 changed files with 4839 additions and 5222 deletions

View file

@ -1,5 +1,6 @@
{ {
"recommendations": [ "recommendations": [
"jnoortheen.nix-ide",
"mkhl.direnv" "mkhl.direnv"
] ]
} }

View file

@ -1,6 +1,6 @@
{ {
"git.detectSubmodules": false, "git.detectSubmodules": false,
"nix.formatterPath": "alejandra", "nix.enableLanguageServer": true,
"search.exclude": { "search.exclude": {
"docs/themes": true "docs/themes": true
} }

View file

@ -9,6 +9,5 @@
sha256 = lock.nodes.flake-compat.locked.narHash; sha256 = lock.nodes.flake-compat.locked.narHash;
} }
) )
{src = ./.;} { src = ./.; }
) ).defaultNix
.defaultNix

View file

@ -1,4 +1,4 @@
{ {
deployment = import ./deployment {}; deployment = import ./deployment { };
testing = import ./testing {}; testing = import ./testing { };
} }

View file

@ -1,8 +1,8 @@
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} { kubenix.evalModules.${builtins.currentSystem} {
module = {kubenix, ...}: { module = { kubenix, ... }: {
# instead of defining everything inline, let's import it # instead of defining everything inline, let's import it
imports = [./module.nix]; imports = [ ./module.nix ];
# annotate the generated resources with a project name # annotate the generated resources with a project name
kubenix.project = "example"; kubenix.project = "example";

View file

@ -1,5 +1,5 @@
{kubenix, ...}: { { kubenix, ... }: {
imports = [kubenix.modules.k8s]; imports = [ kubenix.modules.k8s ];
kubernetes.resources = { kubernetes.resources = {
deployments.nginx.spec = { deployments.nginx.spec = {
@ -50,12 +50,10 @@
services.nginx.spec = { services.nginx.spec = {
selector.app = "nginx"; selector.app = "nginx";
ports = [ ports = [{
{
name = "http"; name = "http";
port = 80; port = 80;
} }];
];
}; };
}; };
} }

View file

@ -1,7 +1,7 @@
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} { kubenix.evalModules.${builtins.currentSystem} {
module = {kubenix, ...}: { module = { kubenix, ... }: {
imports = [kubenix.modules.helm]; imports = [ kubenix.modules.helm ];
kubernetes.helm.releases.example = { kubernetes.helm.releases.example = {
chart = kubenix.lib.helm.fetch { chart = kubenix.lib.helm.fetch {
repo = "https://charts.bitnami.com/bitnami"; repo = "https://charts.bitnami.com/bitnami";

View file

@ -1,15 +1,10 @@
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} { kubenix.evalModules.${builtins.currentSystem} {
module = { module = { kubenix, config, pkgs, ... }: {
kubenix, imports = with kubenix.modules; [ k8s docker ];
config,
pkgs,
...
}: {
imports = with kubenix.modules; [k8s docker];
docker = { docker = {
registry.url = "docker.somewhere.io"; registry.url = "docker.somewhere.io";
images.example.image = pkgs.callPackage ./image.nix {}; images.example.image = pkgs.callPackage ./image.nix { };
}; };
kubernetes.resources.pods.example.spec.containers = { kubernetes.resources.pods.example.spec.containers = {
custom.image = config.docker.images.example.path; custom.image = config.docker.images.example.path;

View file

@ -1,10 +1,7 @@
{ { dockerTools, nginx }:
dockerTools,
nginx,
}:
dockerTools.buildLayeredImage { dockerTools.buildLayeredImage {
name = "nginx"; name = "nginx";
contents = [nginx]; contents = [ nginx ];
extraCommands = '' extraCommands = ''
mkdir -p etc mkdir -p etc
chmod u+w etc chmod u+w etc
@ -12,9 +9,9 @@ dockerTools.buildLayeredImage {
echo "nginx:x:1000:nginx" > etc/group echo "nginx:x:1000:nginx" > etc/group
''; '';
config = { config = {
Cmd = ["nginx" "-c" "/etc/nginx/nginx.conf"]; Cmd = [ "nginx" "-c" "/etc/nginx/nginx.conf" ];
ExposedPorts = { ExposedPorts = {
"80/tcp" = {}; "80/tcp" = { };
}; };
}; };
} }

View file

@ -1,7 +1,7 @@
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} { kubenix.evalModules.${builtins.currentSystem} {
module = {kubenix, ...}: { module = { kubenix, ... }: {
imports = [./module.nix]; imports = [ ./module.nix ];
kubenix.project = "multi-namespace-example"; kubenix.project = "multi-namespace-example";
kubernetes.version = "1.24"; kubernetes.version = "1.24";

View file

@ -1,11 +1,5 @@
{ { config, lib, pkgs, kubenix, ... }: {
config, imports = with kubenix.modules; [ submodules k8s ];
lib,
pkgs,
kubenix,
...
}: {
imports = with kubenix.modules; [submodules k8s];
# Import submodule. # Import submodule.
submodules.imports = [ submodules.imports = [
@ -26,12 +20,10 @@
# Now we can set the args options defined in the submodule. # Now we can set the args options defined in the submodule.
args.kubernetes.resources = { args.kubernetes.resources = {
services.nginx.spec = { services.nginx.spec = {
ports = [ ports = [{
{
name = "http"; name = "http";
port = 80; port = 80;
} }];
];
selector.app = "nginx"; selector.app = "nginx";
}; };
}; };
@ -41,12 +33,10 @@
submodule = "namespaced"; submodule = "namespaced";
args.kubernetes.resources = { args.kubernetes.resources = {
services.nginx.spec = { services.nginx.spec = {
ports = [ ports = [{
{
name = "https"; name = "https";
port = 443; port = 443;
} }];
];
selector.app = "nginx"; selector.app = "nginx";
}; };
}; };

View file

@ -1,13 +1,12 @@
{ { config
config, , kubenix
kubenix, , lib
lib, , # Name of submodule instance.
# Name of submodule instance. name
name, , # This is a shorthand for config.submodule.args and contains
# This is a shorthand for config.submodule.args and contains
# final values of the args options. # final values of the args options.
args, args
... , ...
}: { }: {
imports = with kubenix.modules; [ imports = with kubenix.modules; [
# This needs to be imported in order to define a submodule. # This needs to be imported in order to define a submodule.
@ -25,7 +24,7 @@
# to set kubernetes options from the k8s module which are already # to set kubernetes options from the k8s module which are already
# precisely typed. # precisely typed.
type = lib.types.attrs; type = lib.types.attrs;
default = {}; default = { };
}; };
}; };
@ -54,9 +53,9 @@
kubernetes = lib.mkMerge [ kubernetes = lib.mkMerge [
# Use instance name as namespace # Use instance name as namespace
{namespace = name;} { namespace = name; }
# Create namespace object # Create namespace object
{resources.namespaces.${name} = {};} { resources.namespaces.${name} = { }; }
# All resources defined here will use the above namespace # All resources defined here will use the above namespace
args.kubernetes args.kubernetes
]; ];

View file

@ -1,11 +1,11 @@
# let's creata a function whose only input is the kubenix package # let's creata a function whose only input is the kubenix package
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
# evalModules is our main entrypoint # evalModules is our main entrypoint
kubenix.evalModules.${builtins.currentSystem} { kubenix.evalModules.${builtins.currentSystem} {
# to it, we pass a module that accepts a (different) kubenix object # to it, we pass a module that accepts a (different) kubenix object
module = {kubenix, ...}: { module = { kubenix, ... }: {
# in order to define options, we need to import their definitions # in order to define options, we need to import their definitions
imports = [kubenix.modules.k8s]; imports = [ kubenix.modules.k8s ];
# now we have full access to define Kubernetes resources # now we have full access to define Kubernetes resources
kubernetes.resources.pods = { kubernetes.resources.pods = {
# "example" is the name of our pod # "example" is the name of our pod

View file

@ -1,7 +1,7 @@
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} { kubenix.evalModules.${builtins.currentSystem} {
module = {kubenix, ...}: { module = { kubenix, ... }: {
imports = [kubenix.modules.k8s]; imports = [ kubenix.modules.k8s ];
kubernetes.resources.secrets.example.stringData = { kubernetes.resources.secrets.example.stringData = {
password = "ref+file:///path/to/secret"; password = "ref+file:///path/to/secret";
}; };

View file

@ -1,17 +1,15 @@
{kubenix ? import ../../../..}: { kubenix ? import ../../../.. }:
kubenix.evalModules.x86_64-linux { kubenix.evalModules.x86_64-linux {
module = {kubenix, ...}: { module = { kubenix, ... }: {
imports = [kubenix.modules.testing]; imports = [ kubenix.modules.testing ];
testing = { testing = {
tests = [./test.nix]; tests = [ ./test.nix ];
common = [ common = [{
{ features = [ "k8s" ];
features = ["k8s"];
options = { options = {
kubernetes.version = "1.24"; kubernetes.version = "1.24";
}; };
} }];
];
}; };
}; };
} }

View file

@ -1,9 +1,5 @@
{ { kubenix, test, ... }: {
kubenix, imports = [ kubenix.modules.test ];
test,
...
}: {
imports = [kubenix.modules.test];
test = { test = {
name = "example"; name = "example";

View file

@ -1,14 +1,10 @@
# adapted from: https://discourse.nixos.org/t/franken-script-to-generate-nixos-options-docs-with-custom-modules/1674/4 # adapted from: https://discourse.nixos.org/t/franken-script-to-generate-nixos-options-docs-with-custom-modules/1674/4
{ { pkgs, options }:
pkgs, let
options, extraSources = [ ];
}: let
extraSources = [];
lib = pkgs.lib; lib = pkgs.lib;
optionsListVisible = optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
lib.filter (opt: opt.visible && !opt.internal)
(lib.optionAttrSetToDocList options);
# Replace functions by the string <function> # Replace functions by the string <function>
substFunction = x: substFunction = x:
@ -24,45 +20,40 @@
isPath = x: (builtins.typeOf x) == "path"; isPath = x: (builtins.typeOf x) == "path";
optionsListDesc = lib.flip map optionsListVisible ( optionsListDesc = lib.flip map optionsListVisible (opt:
opt: opt // {
opt description =
// { let
description = let
attempt = builtins.tryEval opt.description; attempt = builtins.tryEval opt.description;
in in
if attempt.success if attempt.success
then attempt.value then attempt.value
else "N/A"; else "N/A";
declarations = map stripAnyPrefixes opt.declarations; declarations = map stripAnyPrefixes opt.declarations;
} } // lib.optionalAttrs (opt ? example) {
// lib.optionalAttrs (opt ? example) {
example = substFunction opt.example; example = substFunction opt.example;
} } // lib.optionalAttrs (opt ? default) {
// lib.optionalAttrs (opt ? default) {
default = substFunction opt.default; default = substFunction opt.default;
} } // lib.optionalAttrs (opt ? type) {
// lib.optionalAttrs (opt ? type) {
type = substFunction opt.type; type = substFunction opt.type;
} } // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != [ ]) {
// lib.optionalAttrs
(opt ? relatedPackages && opt.relatedPackages != [])
{
relatedPackages = genRelatedPackages opt.relatedPackages; relatedPackages = genRelatedPackages opt.relatedPackages;
} }
); );
genRelatedPackages = packages: let genRelatedPackages = packages:
let
unpack = p: unpack = p:
if lib.isString p if lib.isString p
then {name = p;} then { name = p; }
else if lib.isList p else if lib.isList p
then {path = p;} then { path = p; }
else p; else p;
describe = args: let describe = args:
let
title = args.title or null; title = args.title or null;
name = args.name or (lib.concatStringsSep "." args.path); name = args.name or (lib.concatStringsSep "." args.path);
path = args.path or [args.name]; path = args.path or [ args.name ];
package = package =
args.package args.package
or (lib.attrByPath path or (lib.attrByPath path
@ -82,11 +73,13 @@
"\n<programlisting>${package.meta.longDescription}" "\n<programlisting>${package.meta.longDescription}"
+ "</programlisting>" + "</programlisting>"
+ "</listitem>"; + "</listitem>";
in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: in
"<itemizedlist>${lib.concatStringsSep "\n" (map (p:
describe (unpack p)) describe (unpack p))
packages)}</itemizedlist>"; packages)}</itemizedlist>";
optionLess = a: b: let optionLess = a: b:
let
ise = lib.hasPrefix "enable"; ise = lib.hasPrefix "enable";
isp = lib.hasPrefix "package"; isp = lib.hasPrefix "package";
cmp = cmp =
@ -95,7 +88,7 @@
in in
lib.compareLists cmp a.loc b.loc < 0; lib.compareLists cmp a.loc b.loc < 0;
prefixesToStrip = map (p: "${toString p}/") ([../../..] ++ extraSources); prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources);
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip; stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip;
############################################################################### ###############################################################################
@ -119,4 +112,4 @@
}) })
optionsList))); optionsList)));
in in
pkgs.writeText "options.json" optionsJSON pkgs.writeText "options.json" optionsJSON

View file

@ -11,57 +11,50 @@
}; };
}; };
outputs = inputs @ {self, ...}: outputs = inputs @ { self, ... }:
(inputs.flake-utils.lib.eachDefaultSystem ( (inputs.flake-utils.lib.eachDefaultSystem (system:
system: let let
pkgs = import inputs.nixpkgs { pkgs = import inputs.nixpkgs {
inherit system; inherit system;
overlays = [self.overlays.default]; overlays = [ self.overlays.default ];
config.allowUnsupportedSystem = true; config.allowUnsupportedSystem = true;
}; };
inherit (pkgs) lib; inherit (pkgs) lib;
kubenix = { kubenix = {
lib = import ./lib {inherit lib pkgs;}; lib = import ./lib { inherit lib pkgs; };
evalModules = self.evalModules.${system}; evalModules = self.evalModules.${system};
modules = self.nixosModules.kubenix; modules = self.nixosModules.kubenix;
}; };
# 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 = attrs @ { evalModules = attrs @ { module ? null, modules ? [ module ], ... }:
module ? null, let
modules ? [module], lib' = lib.extend (lib: _self: import ./lib/upstreamables.nix { inherit lib pkgs; });
... attrs' = builtins.removeAttrs attrs [ "module" ];
}: let
lib' = lib.extend (lib: _self: import ./lib/upstreamables.nix {inherit lib pkgs;});
attrs' = builtins.removeAttrs attrs ["module"];
in in
lib'.evalModules (lib.recursiveUpdate lib'.evalModules (lib.recursiveUpdate
{ {
modules = modules = modules ++ [{
modules
++ [
{
config._module.args = { config._module.args = {
inherit pkgs; inherit pkgs;
name = "default"; name = "default";
}; };
} }];
];
specialArgs = { specialArgs = {
inherit kubenix; inherit kubenix;
inherit pkgs; inherit pkgs;
}; };
} }
attrs'); attrs');
in { in
{
inherit evalModules pkgs; inherit evalModules pkgs;
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
# extra tools
dive dive
fd fd
k9s k9s
@ -81,11 +74,10 @@
''; '';
}; };
formatter = formatter = (inputs.treefmt.lib.evalModule pkgs {
(inputs.treefmt.lib.evalModule pkgs {
projectRootFile = "flake.nix"; projectRootFile = "flake.nix";
programs = { programs = {
alejandra.enable = true; nixpkgs-fmt.enable = true;
black.enable = true; black.enable = true;
prettier.enable = true; prettier.enable = true;
shfmt.enable = true; shfmt.enable = true;
@ -95,16 +87,12 @@
"docs/layouts/*" "docs/layouts/*"
"modules/generated/*" "modules/generated/*"
]; ];
}) }).config.build.wrapper;
.config
.build
.wrapper;
apps = { apps = {
docs = { docs = {
type = "app"; type = "app";
program = program = (pkgs.writeShellScript "gen-docs" ''
(pkgs.writeShellScript "gen-docs" ''
set -eo pipefail set -eo pipefail
# generate json object of module options # generate json object of module options
@ -123,14 +111,12 @@
# build the site # build the site
cd docs && ${pkgs.hugo}/bin/hugo "$@" cd docs && ${pkgs.hugo}/bin/hugo "$@"
'') '').outPath;
.outPath;
}; };
generate = { generate = {
type = "app"; type = "app";
program = program = (pkgs.writeShellScript "gen-modules" ''
(pkgs.writeShellScript "gen-modules" ''
set -eo pipefail set -eo pipefail
dir=./modules/generated dir=./modules/generated
@ -140,13 +126,11 @@
cp ./result/* $dir/ cp ./result/* $dir/
rm result rm result
'') '').outPath;
.outPath;
}; };
}; };
packages = packages = {
{
inherit (pkgs) kubernetes kubectl; inherit (pkgs) kubernetes kubectl;
default = pkgs.callPackage ./pkgs/kubenix.nix { default = pkgs.callPackage ./pkgs/kubenix.nix {
inherit (self.packages.${system}); inherit (self.packages.${system});
@ -154,29 +138,28 @@
}; };
docs = import ./docs { docs = import ./docs {
inherit pkgs; inherit pkgs;
options = options = (self.evalModules.${system} {
(self.evalModules.${system} {
modules = builtins.attrValues (builtins.removeAttrs modules = builtins.attrValues (builtins.removeAttrs
# the submodules module currently doesn't evaluate: # the submodules module currently doesn't evaluate:
# error: No module found name/latest # error: No module found name/latest
# not sure how important that documentation is a this time # not sure how important that documentation is a this time
self.nixosModules.kubenix ["submodule" "submodules"]); self.nixosModules.kubenix [ "submodule" "submodules" ]);
}) }).options;
.options;
}; };
} }
// pkgs.lib.attrsets.mapAttrs' (name: value: pkgs.lib.attrsets.nameValuePair "generate-${name}" value) // pkgs.lib.attrsets.mapAttrs' (name: value: pkgs.lib.attrsets.nameValuePair "generate-${name}" value)
(builtins.removeAttrs (pkgs.callPackage ./pkgs/generators {}) ["override" "overrideDerivation"]); (builtins.removeAttrs (pkgs.callPackage ./pkgs/generators { }) [ "override" "overrideDerivation" ]);
checks = let checks =
let
wasSuccess = suite: wasSuccess = suite:
if suite.success if suite.success
then pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-succeeded" {} "echo success > $out" then pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-succeeded" { } "echo success > $out"
else pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-failed" {} "exit 1"; else pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-failed" { } "exit 1";
examples = import ./docs/content/examples; examples = import ./docs/content/examples;
mkK8STests = attrs: mkK8STests = attrs:
(import ./tests {inherit evalModules;}) (import ./tests { inherit evalModules; })
({registry = "docker.io/gatehub";} // attrs); ({ registry = "docker.io/gatehub"; } // attrs);
in in
{ {
# TODO: access "success" derivation with nice testing utils for nice output # TODO: access "success" derivation with nice testing utils for nice output
@ -185,7 +168,7 @@
// builtins.listToAttrs (builtins.map // builtins.listToAttrs (builtins.map
(v: { (v: {
name = "test-k8s-${builtins.replaceStrings ["."] ["_"] v}"; name = "test-k8s-${builtins.replaceStrings ["."] ["_"] v}";
value = wasSuccess (mkK8STests {k8sVersion = v;}); value = wasSuccess (mkK8STests { k8sVersion = v; });
}) })
(import ./versions.nix).versions); (import ./versions.nix).versions);
} }

View file

@ -1,8 +1,5 @@
{ { lib, pkgs }: {
lib, k8s = import ./k8s { inherit lib; };
pkgs, docker = import ./docker { inherit lib pkgs; };
}: { helm = import ./helm { inherit pkgs; };
k8s = import ./k8s {inherit lib;};
docker = import ./docker {inherit lib pkgs;};
helm = import ./helm {inherit pkgs;};
} }

View file

@ -1,13 +1,6 @@
{ { lib, pkgs }:
lib,
pkgs,
}:
with lib; { with lib; {
copyDockerImages = { copyDockerImages = { images, dest, args ? "" }:
images,
dest,
args ? "",
}:
pkgs.writeScript "copy-docker-images.sh" (concatMapStrings pkgs.writeScript "copy-docker-images.sh" (concatMapStrings
(image: '' (image: ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}

View file

@ -1,29 +1,25 @@
{ { runCommand, lib, kubernetes-helm, yq }:
runCommand,
lib,
kubernetes-helm,
yq,
}:
with lib; with lib;
{ {
# chart to template # chart to template
chart, chart
# release name # release name
name, , name
# namespace to install release into # namespace to install release into
namespace ? null, , namespace ? null
# values to pass to chart # values to pass to chart
values ? {}, , values ? { }
# kubernetes version to template chart for # kubernetes version to template chart for
kubeVersion ? null, , kubeVersion ? null
# whether to include CRD # whether to include CRD
includeCRDs ? false, , includeCRDs ? false
# whether to include hooks # whether to include hooks
noHooks ? false, , noHooks ? false
}: let }:
let
valuesJsonFile = builtins.toFile "${name}-values.json" (builtins.toJSON values); valuesJsonFile = builtins.toFile "${name}-values.json" (builtins.toJSON values);
# The `helm template` and YAML -> JSON steps are separate `runCommand` derivations for easier debuggability # The `helm template` and YAML -> JSON steps are separate `runCommand` derivations for easier debuggability
resourcesYaml = runCommand "${name}.yaml" {nativeBuildInputs = [kubernetes-helm];} '' resourcesYaml = runCommand "${name}.yaml" { nativeBuildInputs = [ kubernetes-helm ]; } ''
helm template "${name}" \ helm template "${name}" \
${optionalString (kubeVersion != null) "--kube-version ${kubeVersion}"} \ ${optionalString (kubeVersion != null) "--kube-version ${kubeVersion}"} \
${optionalString (namespace != null) "--namespace ${namespace}"} \ ${optionalString (namespace != null) "--namespace ${namespace}"} \
@ -32,8 +28,8 @@ with lib;
${optionalString noHooks "--no-hooks"} \ ${optionalString noHooks "--no-hooks"} \
${chart} >$out ${chart} >$out
''; '';
in in
runCommand "${name}.json" {} '' runCommand "${name}.json" { } ''
# Remove null values # Remove null values
${yq}/bin/yq -Scs 'walk( ${yq}/bin/yq -Scs 'walk(
if type == "object" then if type == "object" then
@ -43,4 +39,4 @@ with lib;
else else
. .
end)' ${resourcesYaml} >$out end)' ${resourcesYaml} >$out
'' ''

View file

@ -1,4 +1,4 @@
{pkgs}: { { pkgs }: {
chart2json = pkgs.callPackage ./chart2json.nix {}; chart2json = pkgs.callPackage ./chart2json.nix { };
fetch = pkgs.callPackage ./fetchhelm.nix {}; fetch = pkgs.callPackage ./fetchhelm.nix { };
} }

View file

@ -1,80 +1,48 @@
{ { stdenvNoCC, lib, kubernetes-helm, cacert }:
stdenvNoCC, let
lib, cleanName = lib.replaceStrings [ "/" ] [ "-" ];
kubernetes-helm,
cacert,
}: let
cleanName = lib.replaceStrings ["/"] ["-"];
in in
{ {
# name of the chart # name of the chart
chart, chart
# chart url to fetch from custom location , # chart url to fetch from custom location
chartUrl ? null, chartUrl ? null
# version of the chart , # version of the chart
version ? null, version ? null
# chart hash , # chart hash
sha256, sha256
# whether to extract chart , # whether to extract chart
untar ? true, untar ? true
# use custom charts repo , # use custom charts repo
repo ? null, repo ? null
# pass --verify to helm chart , # pass --verify to helm chart
verify ? false, verify ? false
# pass --devel to helm chart , # pass --devel to helm chart
devel ? false, devel ? false
}: ,
stdenvNoCC.mkDerivation { }:
name = "${cleanName chart}-${ stdenvNoCC.mkDerivation {
if version == null name = "${cleanName chart}-${ if version == null then "dev" else version }";
then "dev"
else version
}";
buildCommand = '' buildCommand = ''
export HOME="$PWD" export HOME="$PWD"
echo "adding helm repo" echo "adding helm repo"
${ ${ if repo == null then "" else "helm repo add repository ${repo}" }
if repo == null
then ""
else "helm repo add repository ${repo}"
}
echo "fetching helm chart" echo "fetching helm chart"
helm fetch -d ./chart \ helm fetch -d ./chart \
${ ${ if untar then "--untar" else ""} \
if untar ${ if version == null then "" else "--version ${version}" } \
then "--untar" ${ if devel then "--devel" else "" } \
else "" ${ if verify then "--verify" else "" } \
} \ ${ if chartUrl == null then (
${
if version == null
then ""
else "--version ${version}"
} \
${
if devel
then "--devel"
else ""
} \
${
if verify
then "--verify"
else ""
} \
${
if chartUrl == null
then
(
if repo == null if repo == null
then chart then chart
else "repository/${chart}" else "repository/${chart}"
) ) else chartUrl}
else chartUrl
}
cp -r chart/*/ $out cp -r chart/*/ $out
''; '';
outputHashMode = "recursive"; outputHashMode = "recursive";
outputHashAlgo = "sha256"; outputHashAlgo = "sha256";
outputHash = sha256; outputHash = sha256;
nativeBuildInputs = [kubernetes-helm cacert]; nativeBuildInputs = [ kubernetes-helm cacert ];
} }

View file

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

View file

@ -1,15 +1,10 @@
{lib}: { lib }:
with lib; rec { with lib; rec {
# TODO: refactor with mkOptionType # TODO: refactor with mkOptionType
mkSecretOption = { mkSecretOption = { description ? "", default ? { }, allowNull ? true }:
description ? "",
default ? {},
allowNull ? true,
}:
mkOption { mkOption {
inherit description; inherit description;
type = type = (
(
if allowNull if allowNull
then types.nullOr then types.nullOr
else id else id
@ -19,24 +14,19 @@ with lib; rec {
description = "Name of the secret where secret is stored"; description = "Name of the secret where secret is stored";
type = types.str; type = types.str;
default = default.name; default = default.name;
} } // (optionalAttrs (default ? "name") {
// (optionalAttrs (default ? "name") {
default = default.name; default = default.name;
})); }));
key = mkOption ({ key = mkOption ({
description = "Name of the key where secret is stored"; description = "Name of the key where secret is stored";
type = types.str; type = types.str;
} } // (optionalAttrs (default ? "key") {
// (optionalAttrs (default ? "key") {
default = default.key; default = default.key;
})); }));
}; };
}); });
default = default = if default == null then null else { };
if default == null
then null
else {};
}; };
secretToEnv = value: { secretToEnv = value: {
@ -46,10 +36,7 @@ with lib; rec {
}; };
# Creates kubernetes list from a list of kubernetes objects # Creates kubernetes list from a list of kubernetes objects
mkList = { mkList = { items, labels ? { } }: {
items,
labels ? {},
}: {
kind = "List"; kind = "List";
apiVersion = "v1"; apiVersion = "v1";
@ -57,14 +44,11 @@ with lib; rec {
}; };
# Creates hashed kubernetes list from a list of kubernetes objects # Creates hashed kubernetes list from a list of kubernetes objects
mkHashedList = { mkHashedList = { items, labels ? { } }:
items, let
labels ? {},
}: let
hash = builtins.hashString "sha1" (builtins.toJSON items); hash = builtins.hashString "sha1" (builtins.toJSON items);
labeledItems = labeledItems = map
map
(item: (item:
recursiveUpdate item { recursiveUpdate item {
metadata.labels."kubenix/hash" = hash; metadata.labels."kubenix/hash" = hash;
@ -73,11 +57,9 @@ with lib; rec {
in in
mkList { mkList {
items = labeledItems; items = labeledItems;
labels = labels = {
{
"kubenix/hash" = hash; "kubenix/hash" = hash;
} } // labels;
// labels;
}; };
inherit (lib) toBase64; inherit (lib) toBase64;

View file

@ -1,21 +1,18 @@
{ { lib, pkgs }:
lib,
pkgs,
}:
with lib; let with lib; let
self = { self = {
importYAML = path: importYAML = path:
importJSON (pkgs.runCommand "yaml-to-json" {} '' importJSON (pkgs.runCommand "yaml-to-json" { } ''
${pkgs.yq}/bin/yq -c . ${path} > $out ${pkgs.yq}/bin/yq -c . ${path} > $out
''); '');
toYAML = config: toYAML = config:
builtins.readFile (pkgs.runCommand "to-yaml" {} '' builtins.readFile (pkgs.runCommand "to-yaml" { } ''
${pkgs.yq}/bin/yq -y . ${pkgs.writeText "to-json" (builtins.toJSON config)} > $out ${pkgs.yq}/bin/yq -y . ${pkgs.writeText "to-json" (builtins.toJSON config)} > $out
''); '');
toMultiDocumentYaml = name: documents: toMultiDocumentYaml = name: documents:
pkgs.runCommand name {} pkgs.runCommand name { }
(concatMapStringsSep "\necho --- >> $out\n" (concatMapStringsSep "\necho --- >> $out\n"
( (
d: "${pkgs.yq}/bin/yq -y . ${pkgs.writeText "to-json" (builtins.toJSON config)} >> $out" d: "${pkgs.yq}/bin/yq -y . ${pkgs.writeText "to-json" (builtins.toJSON config)} >> $out"
@ -24,7 +21,7 @@ with lib; let
toBase64 = value: toBase64 = value:
builtins.readFile builtins.readFile
(pkgs.runCommand "value-to-b64" {} "echo -n '${value}' | ${pkgs.coreutils}/bin/base64 -w0 > $out"); (pkgs.runCommand "value-to-b64" { } "echo -n '${value}' | ${pkgs.coreutils}/bin/base64 -w0 > $out");
exp = base: exp: foldr (_value: acc: acc * base) 1 (range 1 exp); exp = base: exp: foldr (_value: acc: acc * base) 1 (range 1 exp);
@ -38,8 +35,7 @@ with lib; let
i = 0; i = 0;
value = 0; value = 0;
} }
(stringToCharacters value)) (stringToCharacters value)).value;
.value;
}; };
in in
self self

View file

@ -1,4 +1,4 @@
{lib, ...}: { lib, ... }:
with lib; { with lib; {
options = { options = {
kubenix.project = mkOption { kubenix.project = mkOption {
@ -10,7 +10,7 @@ with lib; {
_m.features = mkOption { _m.features = mkOption {
description = "List of features exposed by module"; description = "List of features exposed by module";
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
}; };
_m.propagate = mkOption { _m.propagate = mkOption {
@ -20,17 +20,17 @@ with lib; {
features = mkOption { features = mkOption {
description = "List of features that submodule has to have to propagate module"; description = "List of features that submodule has to have to propagate module";
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
}; };
module = mkOption { module = mkOption {
description = "Module to propagate"; description = "Module to propagate";
type = types.unspecified; type = types.unspecified;
default = {}; default = { };
}; };
}; };
})); }));
default = []; default = [ ];
}; };
}; };
} }

View file

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

View file

@ -1,12 +1,6 @@
# helm defines kubenix module with options for using helm charts # helm defines kubenix module with options for using helm charts
# with kubenix # with kubenix
{ { config, lib, pkgs, helm, ... }:
config,
lib,
pkgs,
helm,
...
}:
with lib; let with lib; let
cfg = config.kubernetes.helm; cfg = config.kubernetes.helm;
@ -16,29 +10,28 @@ with lib; let
name = "recursive-attrs"; name = "recursive-attrs";
description = "recursive attribute set"; description = "recursive attribute set";
check = isAttrs; check = isAttrs;
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 = group =
if length splitted == 1 if length splitted == 1
then "core" then "core"
else head splitted; else head splitted;
version = last splitted; version = last splitted;
}; };
in { in
imports = [./k8s.nix]; {
imports = [ ./k8s.nix ];
options.kubernetes.helm = { options.kubernetes.helm = {
releases = mkOption { releases = mkOption {
description = "Attribute set of helm releases"; description = "Attribute set of helm releases";
type = types.attrsOf (types.submodule ({ type = types.attrsOf (types.submodule ({ config, name, ... }: {
config,
name,
...
}: {
options = { options = {
name = mkOption { name = mkOption {
description = "Helm release name"; description = "Helm release name";
@ -60,7 +53,7 @@ in {
values = mkOption { values = mkOption {
description = "Values to pass to chart"; description = "Values to pass to chart";
type = recursiveAttrs; type = recursiveAttrs;
default = {}; default = { };
}; };
kubeVersion = mkOption { kubeVersion = mkOption {
@ -72,7 +65,7 @@ in {
overrides = mkOption { overrides = mkOption {
description = "Overrides to apply to all chart resources"; description = "Overrides to apply to all chart resources";
type = types.listOf types.unspecified; type = types.listOf types.unspecified;
default = []; default = [ ];
}; };
overrideNamespace = mkOption { overrideNamespace = mkOption {
@ -109,36 +102,34 @@ in {
objects = mkOption { objects = mkOption {
description = "Generated kubernetes objects"; description = "Generated kubernetes objects";
type = types.listOf types.attrs; type = types.listOf types.attrs;
default = []; default = [ ];
}; };
}; };
config.overrides = mkIf (config.overrideNamespace && config.namespace != null) [ config.overrides = mkIf (config.overrideNamespace && config.namespace != null) [{
{
metadata.namespace = config.namespace; metadata.namespace = config.namespace;
} }];
];
config.objects = importJSON (helm.chart2json { config.objects = importJSON (helm.chart2json {
inherit (config) chart name namespace values kubeVersion includeCRDs noHooks; inherit (config) chart name namespace values kubeVersion includeCRDs noHooks;
}); });
})); }));
default = {}; default = { };
}; };
}; };
config = { config = {
# 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 kubernetes.api.resources = mkMerge (flatten (mapAttrsToList
( (_: release: map
_: release: (object:
map let
(object: let
apiVersion = parseApiVersion object.apiVersion; apiVersion = parseApiVersion object.apiVersion;
inherit (object.metadata) name; inherit (object.metadata) name;
in { in
{
"${apiVersion.group}"."${apiVersion.version}".${object.kind}."${name}" = mkMerge ([ "${apiVersion.group}"."${apiVersion.version}".${object.kind}."${name}" = mkMerge ([
object object
] ]

View file

@ -1,19 +1,13 @@
{ { lib, definitions }:
lib,
definitions,
}:
with lib; { with lib; {
"istio_networking_v1alpha3_StringMatch" = "istio_networking_v1alpha3_StringMatch" = recursiveUpdate
recursiveUpdate (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"
) )
definitions."istio_networking_v1alpha3_StringMatch_Regex"; definitions."istio_networking_v1alpha3_StringMatch_Regex";
"istio_networking_v1alpha3_PortSelector" = "istio_networking_v1alpha3_PortSelector" = recursiveUpdate
recursiveUpdate
definitions."istio_networking_v1alpha3_PortSelector_Name" definitions."istio_networking_v1alpha3_PortSelector_Name"
definitions."istio_networking_v1alpha3_PortSelector_Number"; definitions."istio_networking_v1alpha3_PortSelector_Number";
} }

View file

@ -1,13 +1,7 @@
# This file was generated with kubenix k8s generator, do not edit # This file was generated with kubenix k8s generator, do not edit
{ { lib, config, ... }:
lib,
config,
...
}:
with lib; let with lib; let
types = types = lib.types // rec {
lib.types
// rec {
str = mkOptionType { str = mkOptionType {
name = "str"; name = "str";
description = "string"; description = "string";
@ -22,28 +16,30 @@ with lib; let
name = "coercedTo"; name = "coercedTo";
description = "${finalType.description} or ${coercedType.description}"; description = "${finalType.description} or ${coercedType.description}";
check = x: finalType.check x || coercedType.check x; check = x: finalType.check x || coercedType.check x;
merge = loc: defs: let merge = loc: defs:
let
coerceVal = val: coerceVal = val:
if finalType.check val if finalType.check val
then val then val
else let else
let
coerced = coerceFunc val; coerced = coerceFunc val;
in in
assert finalType.check coerced; coerced; assert finalType.check coerced; coerced;
in in
finalType.merge loc (map (def: def // {value = coerceVal def.value;}) defs); 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; };
}; };
}; };
mkOptionDefault = mkOverride 1001; mkOptionDefault = mkOverride 1001;
extraOptions = { extraOptions = {
kubenix = {}; kubenix = { };
}; };
mergeValuesByKey = mergeKey: values: mergeValuesByKey = mergeKey: values:
@ -59,18 +55,20 @@ with lib; let
values); values);
submoduleOf = ref: submoduleOf = ref:
types.submodule ({name, ...}: { types.submodule ({ name, ... }: {
options = definitions."${ref}".options; options = definitions."${ref}".options;
config = definitions."${ref}".config; config = definitions."${ref}".config;
}); });
submoduleWithMergeOf = ref: mergeKey: submoduleWithMergeOf = ref: mergeKey:
types.submodule ({name, ...}: let 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 = config =
definitions."${ref}".config definitions."${ref}".config
@ -80,7 +78,7 @@ with lib; let
}); });
submoduleForDefinition = ref: resource: kind: group: version: submoduleForDefinition = ref: resource: kind: group: version:
types.submodule ({name, ...}: { types.submodule ({ name, ... }: {
options = definitions."${ref}".options // extraOptions; options = definitions."${ref}".options // extraOptions;
config = mkMerge ([ config = mkMerge ([
definitions."${ref}".config definitions."${ref}".config
@ -92,8 +90,8 @@ with lib; let
metadata.name = mkOptionDefault name; metadata.name = mkOptionDefault name;
} }
] ]
++ (config.defaults.${resource} or []) ++ (config.defaults.${resource} or [ ])
++ (config.defaults.all or [])); ++ (config.defaults.all or [ ]));
}); });
coerceAttrsOfSubmodulesToListByKey = ref: mergeKey: ( coerceAttrsOfSubmodulesToListByKey = ref: mergeKey: (
@ -103,8 +101,7 @@ with lib; let
(types.attrsOf (submoduleWithMergeOf ref mergeKey)) (types.attrsOf (submoduleWithMergeOf ref mergeKey))
); );
definitions = definitions = {
{
"google_rpc_Status" = { "google_rpc_Status" = {
options = { options = {
"code" = mkOption { "code" = mkOption {
@ -2859,7 +2856,7 @@ with lib; let
}; };
}; };
"istio_mixer_v1_ReportResponse" = {}; "istio_mixer_v1_ReportResponse" = { };
"istio_mixer_v1_RouteDirective" = { "istio_mixer_v1_RouteDirective" = {
options = { options = {
@ -4710,8 +4707,9 @@ with lib; 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";

View file

@ -1,12 +1,5 @@
# K8S module defines kubernetes definitions for kubenix # K8S module defines kubernetes definitions for kubenix
{ { options, config, lib, pkgs, k8s, ... }:
options,
config,
lib,
pkgs,
k8s,
...
}:
with lib; let with lib; let
versions = (import ../versions.nix).versions; versions = (import ../versions.nix).versions;
cfg = config.kubernetes; cfg = config.kubernetes;
@ -15,8 +8,7 @@ with lib; let
getDefaults = resource: group: version: kind: getDefaults = resource: group: version: kind:
catAttrs "default" (filter catAttrs "default" (filter
( (default:
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)
@ -31,7 +23,7 @@ with lib; let
then map moduleToAttrs value then map moduleToAttrs value
else value; else value;
apiOptions = {config, ...}: { apiOptions = { config, ... }: {
options = { options = {
definitions = mkOption { definitions = mkOption {
description = "Attribute set of kubernetes definitions"; description = "Attribute set of kubernetes definitions";
@ -74,18 +66,17 @@ with lib; let
default = mkOption { default = mkOption {
description = "Default to apply"; description = "Default to apply";
type = types.unspecified; type = types.unspecified;
default = {}; default = { };
}; };
}; };
})); }));
default = []; default = [ ];
apply = unique; apply = unique;
}; };
types = mkOption { types = mkOption {
description = "List of registered kubernetes types"; description = "List of registered kubernetes types";
type = type = coerceListOfSubmodulesToAttrs
coerceListOfSubmodulesToAttrs
{ {
options = { options = {
group = mkOption { group = mkOption {
@ -115,7 +106,7 @@ with lib; let
}; };
} }
gvkKeyFn; gvkKeyFn;
default = {}; default = { };
}; };
}; };
@ -133,7 +124,8 @@ with lib; let
else -1) else -1)
lst)); lst));
compareVersions = ver1: ver2: let compareVersions = ver1: ver2:
let
getVersion = substring 1 10; getVersion = substring 1 10;
splittedVer1 = builtins.splitVersion (getVersion ver1); splittedVer1 = builtins.splitVersion (getVersion ver1);
splittedVer2 = builtins.splitVersion (getVersion ver2); splittedVer2 = builtins.splitVersion (getVersion ver2);
@ -155,10 +147,8 @@ with lib; let
}) })
cfg.customTypes); cfg.customTypes);
customResourceTypesByAttrNameSortByVersion = customResourceTypesByAttrNameSortByVersion = mapAttrs
mapAttrs (_: resourceTypes:
(
_: resourceTypes:
reverseList (sort reverseList (sort
( (
r1: r2: r1: r2:
@ -168,10 +158,9 @@ with lib; let
) )
customResourceTypesByAttrName; customResourceTypesByAttrName;
latestCustomResourceTypes = latestCustomResourceTypes = mapAttrsToList (_: last) customResourceTypesByAttrNameSortByVersion;
mapAttrsToList (_: last) customResourceTypesByAttrNameSortByVersion;
customResourceModuleForType = config: ct: {name, ...}: { customResourceModuleForType = config: ct: { name, ... }: {
imports = getDefaults ct.name ct.group ct.version ct.kind; imports = getDefaults ct.name ct.group ct.version ct.kind;
options = { options = {
apiVersion = mkOption { apiVersion = mkOption {
@ -192,7 +181,7 @@ with lib; let
spec = mkOption { spec = mkOption {
description = "Module spec"; description = "Module spec";
type = types.either types.attrs (types.submodule ct.module); type = types.either types.attrs (types.submodule ct.module);
default = {}; default = { };
}; };
}; };
@ -203,30 +192,29 @@ with lib; let
}; };
}; };
customResourceOptions = customResourceOptions = (mapAttrsToList
(mapAttrsToList (_: ct: { config, ... }:
(_: ct: {config, ...}: let 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 {
inherit (ct) description; inherit (ct) description;
type = types.attrsOf (types.submodule module); type = types.attrsOf (types.submodule module);
default = {}; default = { };
}; };
}) })
cfg.customTypes) cfg.customTypes)
++ (map ++ (map
(ct: { (ct: { options, config, ... }:
options, let
config,
...
}: let
module = customResourceModuleForType config ct; module = customResourceModuleForType config ct;
in { in
{
options.resources.${ct.attrName} = mkOption { options.resources.${ct.attrName} = mkOption {
inherit (ct) description; inherit (ct) description;
type = types.attrsOf (types.submodule module); type = types.attrsOf (types.submodule module);
default = {}; default = { };
}; };
config.resources.${ct.group}.${ct.version}.${ct.kind} = config.resources.${ct.group}.${ct.version}.${ct.kind} =
@ -234,11 +222,11 @@ with lib; let
}) })
latestCustomResourceTypes); latestCustomResourceTypes);
coerceListOfSubmodulesToAttrs = submodule: keyFn: let coerceListOfSubmodulesToAttrs = submodule: keyFn:
let
mergeValuesByFn = keyFn: values: mergeValuesByFn = keyFn: values:
listToAttrs (map listToAttrs (map
( (value:
value:
nameValuePair (toString (keyFn value)) value nameValuePair (toString (keyFn value)) value
) )
values); values);
@ -250,26 +238,28 @@ with lib; let
name = "coercedTo"; name = "coercedTo";
description = "${finalType.description} or ${coercedType.description}"; description = "${finalType.description} or ${coercedType.description}";
check = x: finalType.check x || coercedType.check x; check = x: finalType.check x || coercedType.check x;
merge = loc: defs: let merge = loc: defs:
let
coerceVal = val: coerceVal = val:
if finalType.check val if finalType.check val
then val then val
else let coerced = coerceFunc val; in assert finalType.check coerced; coerced; else let coerced = coerceFunc val; in assert finalType.check coerced; coerced;
in in
finalType.merge loc (map (def: def // {value = coerceVal def.value;}) defs); finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
inherit (finalType) getSubOptions; inherit (finalType) getSubOptions;
inherit (finalType) getSubModules; inherit (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 in
coercedTo 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));
in { in
imports = [./base.nix]; {
imports = [ ./base.nix ];
options.kubernetes = { options.kubernetes = {
kubeconfig = mkOption { kubeconfig = mkOption {
@ -296,7 +286,7 @@ in {
customResources = mkOption { customResources = mkOption {
description = "Setup custom resources"; description = "Setup custom resources";
type = types.listOf types.attrs; type = types.listOf types.attrs;
default = []; default = [ ];
}; };
resourceOrder = mkOption { resourceOrder = mkOption {
@ -310,25 +300,24 @@ in {
api = mkOption { api = mkOption {
type = types.submodule { type = types.submodule {
imports = imports = [
[
./generated/v${cfg.version}.nix ./generated/v${cfg.version}.nix
apiOptions apiOptions
] ]
++ customResourceOptions; ++ customResourceOptions;
}; };
default = {}; default = { };
}; };
imports = mkOption { imports = mkOption {
type = types.listOf (types.either types.package types.path); type = types.listOf (types.either types.package types.path);
description = "List of resources to import"; description = "List of resources to import";
default = []; default = [ ];
}; };
resources = mkOption { resources = mkOption {
description = "Alias for `config.kubernetes.api.resources` options"; description = "Alias for `config.kubernetes.api.resources` options";
default = {}; default = { };
type = types.attrsOf types.attrs; type = types.attrsOf types.attrs;
}; };
@ -342,8 +331,7 @@ in {
group = "helm.cattle.io"; group = "helm.cattle.io";
}; };
}; };
type = type = coerceListOfSubmodulesToAttrs
coerceListOfSubmodulesToAttrs
{ {
options = { options = {
group = mkOption { group = mkOption {
@ -385,12 +373,12 @@ in {
module = mkOption { module = mkOption {
description = "Custom type module"; description = "Custom type module";
type = types.unspecified; type = types.unspecified;
default = {}; default = { };
}; };
}; };
} }
gvkKeyFn; gvkKeyFn;
default = {}; default = { };
}; };
objects = mkOption { objects = mkOption {
@ -398,8 +386,7 @@ in {
type = types.listOf types.attrs; type = types.listOf types.attrs;
apply = items: apply = items:
sort sort
( (r1: r2:
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 else if elem r1.kind cfg.resourceOrder
@ -407,7 +394,7 @@ in {
else false else false
) )
(unique items); (unique items);
default = []; default = [ ];
}; };
generated = mkOption { generated = mkOption {
@ -428,12 +415,12 @@ in {
config = { config = {
# features that module is defining # features that module is defining
_m.features = ["k8s"]; _m.features = [ "k8s" ];
# module propagation options # module propagation options
_m.propagate = [ _m.propagate = [
{ {
features = ["k8s"]; features = [ "k8s" ];
module = _: { module = _: {
# propagate kubernetes version and namespace # propagate kubernetes version and namespace
kubernetes.version = mkDefault cfg.version; kubernetes.version = mkDefault cfg.version;
@ -441,8 +428,8 @@ in {
}; };
} }
{ {
features = ["k8s" "submodule"]; features = [ "k8s" "submodule" ];
module = {config, ...}: { module = { config, ... }: {
# set module defaults # set module defaults
kubernetes.api.defaults = kubernetes.api.defaults =
(filter (default: default.propagate) cfg.api.defaults) (filter (default: default.propagate) cfg.api.defaults)
@ -460,24 +447,21 @@ in {
]; ];
# expose k8s helper methods as module argument # expose k8s helper methods as module argument
_module.args.k8s = import ../lib/k8s {inherit lib;}; _module.args.k8s = import ../lib/k8s { inherit lib; };
kubernetes.api = mkMerge ([ kubernetes.api = mkMerge ([
{ {
# register custom types # register custom types
types = types = mapAttrsToList
mapAttrsToList
(_: cr: { (_: cr: {
inherit (cr) name group version kind attrName; inherit (cr) name group version kind attrName;
}) })
cfg.customTypes; cfg.customTypes;
defaults = [ defaults = [{
{
default = { default = {
# set default kubernetes namespace to all resources # set default kubernetes namespace to all resources
metadata.namespace = metadata.namespace = mkIf (config.kubernetes.namespace != null)
mkIf (config.kubernetes.namespace != null)
(mkDefault config.kubernetes.namespace); (mkDefault config.kubernetes.namespace);
# set project name to all resources # set project name to all resources
@ -486,14 +470,14 @@ in {
"kubenix/k8s-version" = cfg.version; "kubenix/k8s-version" = cfg.version;
}; };
}; };
} }];
];
} }
] ]
++ ++
# import of yaml files # import of yaml files
(map (map
(i: let (i:
let
# load yaml file # load yaml file
object = importYAML i; object = importYAML i;
groupVersion = splitString "/" object.apiVersion; groupVersion = splitString "/" object.apiVersion;
@ -504,14 +488,14 @@ in {
then "core" then "core"
else head groupVersion; else head groupVersion;
inherit (object) kind; inherit (object) kind;
in { in
{
resources.${group}.${version}.${kind}.${name} = object; resources.${group}.${version}.${kind}.${name} = object;
}) })
cfg.imports)); cfg.imports));
kubernetes.objects = flatten (mapAttrsToList kubernetes.objects = flatten (mapAttrsToList
( (_: type:
_: type:
mapAttrsToList (_name: moduleToAttrs) mapAttrsToList (_name: moduleToAttrs)
cfg.api.resources.${type.group}.${type.version}.${type.kind} cfg.api.resources.${type.group}.${type.version}.${type.kind}
) )

View file

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

View file

@ -1,11 +1,4 @@
{ { config, options, kubenix, pkgs, lib, ... }:
config,
options,
kubenix,
pkgs,
lib,
...
}:
with lib; let with lib; let
cfg = config.submodules; cfg = config.submodules;
parentConfig = config; parentConfig = config;
@ -18,15 +11,9 @@ with lib; let
else requiredVersion == version else requiredVersion == version
else true; else true;
getDefaults = { getDefaults = { name, version, tags, features }:
name,
version,
tags,
features,
}:
catAttrs "default" (filter catAttrs "default" (filter
( (submoduleDefault:
submoduleDefault:
(submoduleDefault.name == null || submoduleDefault.name == name) (submoduleDefault.name == null || submoduleDefault.name == name)
&& (matchesVersion submoduleDefault.version version) && (matchesVersion submoduleDefault.version version)
&& ( && (
@ -40,31 +27,22 @@ with lib; let
) )
config.submodules.defaults); config.submodules.defaults);
specialArgs = specialArgs = cfg.specialArgs // {
cfg.specialArgs
// {
parentConfig = config; parentConfig = config;
}; };
findSubmodule = { findSubmodule = { name, version ? null, latest ? true }:
name, let
version ? null, matchingSubmodules = filter
latest ? true, (el:
}: let
matchingSubmodules =
filter
(
el:
el.definition.name el.definition.name
== name == name
&& (matchesVersion version el.definition.version) && (matchesVersion version el.definition.version)
) )
cfg.imports; cfg.imports;
versionSortedSubmodules = versionSortedSubmodules = sort
sort (s1: s2:
(
s1: s2:
if builtins.compareVersions s1.definition.version s2.definition.version > 0 if builtins.compareVersions s1.definition.version s2.definition.version > 0
then true then true
else false else false
@ -83,57 +61,52 @@ with lib; let
in in
matchingModule; matchingModule;
passthruConfig = passthruConfig = mapAttrsToList
mapAttrsToList
(name: _opt: { (name: _opt: {
${name} = mkMerge (mapAttrsToList ${name} = mkMerge (mapAttrsToList
( (_: inst:
_: 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 _module.args = mkMerge (mapAttrsToList
( (_: inst:
_: 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); config.submodules.instances);
}) })
(removeAttrs options ["_definedNames" "_module" "_m" "submodules"]); (removeAttrs options [ "_definedNames" "_module" "_m" "submodules" ]);
submoduleWithSpecialArgs = opts: specialArgs: let submoduleWithSpecialArgs = opts: specialArgs:
let
opts' = toList opts; opts' = toList opts;
inherit (lib.modules) evalModules; inherit (lib.modules) evalModules;
in in
mkOptionType rec { mkOptionType rec {
name = "submodule"; name = "submodule";
check = x: isAttrs x || isFunction x; check = x: isAttrs x || isFunction x;
merge = loc: defs: let merge = loc: defs:
let
coerce = def: coerce = def:
if isFunction def if isFunction def
then def then def
else {config = def;}; else { config = def; };
modules = modules = opts' ++ map
opts' (def: {
++ map (def: {
_file = def.file; _file = def.file;
imports = [(coerce def.value)]; imports = [ (coerce def.value) ];
}) })
defs; defs;
in in
(evalModules { (evalModules {
inherit modules specialArgs; inherit modules specialArgs;
prefix = loc; prefix = loc;
}) }).config;
.config; getSubOptions = prefix: (evalModules {
getSubOptions = prefix:
(evalModules
{
modules = opts'; modules = opts';
inherit prefix specialArgs; 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,
@ -152,27 +125,25 @@ with lib; let
# would be used, and use of `<` and `>` would break the XML document. # would be used, and use of `<` and `>` would break the XML document.
# It shouldn't cause an issue since this is cosmetic for the manual. # It shouldn't cause an issue since this is cosmetic for the manual.
args.name = "name"; args.name = "name";
}) }).options;
.options;
getSubModules = opts'; getSubModules = opts';
substSubModules = m: submoduleWithSpecialArgs m specialArgs; substSubModules = m: submoduleWithSpecialArgs m specialArgs;
functor = functor = (defaultFunctor name) // {
(defaultFunctor name)
// {
# Merging of submodules is done as part of mergeOptionDecls, as we have to annotate # Merging of submodules is done as part of mergeOptionDecls, as we have to annotate
# each submodule with its location. # each submodule with its location.
payload = []; payload = [ ];
binOp = _lhs: _rhs: []; binOp = _lhs: _rhs: [ ];
}; };
}; };
in { in
imports = [./base.nix]; {
imports = [ ./base.nix ];
options = { options = {
submodules.specialArgs = mkOption { submodules.specialArgs = mkOption {
description = "Special args to pass to submodules. These arguments can be used for imports"; description = "Special args to pass to submodules. These arguments can be used for imports";
type = types.attrs; type = types.attrs;
default = {}; default = { };
}; };
submodules.defaults = mkOption { submodules.defaults = mkOption {
@ -197,23 +168,23 @@ in {
tags = mkOption { tags = mkOption {
description = "List of tags to apply defaults for"; description = "List of tags to apply defaults for";
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
}; };
features = mkOption { features = mkOption {
description = "List of features that submodule has to have to apply defaults"; description = "List of features that submodule has to have to apply defaults";
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
}; };
default = mkOption { default = mkOption {
description = "Default to apply to submodule instance"; description = "Default to apply to submodule instance";
type = types.unspecified; type = types.unspecified;
default = {}; default = { };
}; };
}; };
})); }));
default = []; default = [ ];
}; };
submodules.propagate.enable = mkOption { submodules.propagate.enable = mkOption {
@ -227,30 +198,25 @@ in {
type = types.listOf ( type = types.listOf (
types.coercedTo types.coercedTo
types.path types.path
(module: {inherit module;}) (module: { inherit module; })
( (
types.submodule ({ types.submodule ({ name, config, ... }:
name, let
config,
...
}: let
evaledSubmodule' = evalModules { evaledSubmodule' = evalModules {
inherit specialArgs; inherit specialArgs;
modules = modules = config.modules
config.modules ++ [ ./base.nix ]
++ [./base.nix] ++ [{
++ [
{
_module.args.check = false; _module.args.check = false;
} }];
];
}; };
evaledSubmodule = evaledSubmodule =
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";
@ -260,7 +226,7 @@ in {
modules = mkOption { modules = mkOption {
description = "List of modules defining submodule"; description = "List of modules defining submodule";
type = types.listOf types.unspecified; type = types.listOf types.unspecified;
default = [config.module]; default = [ config.module ];
}; };
features = mkOption { features = mkOption {
@ -290,18 +256,14 @@ in {
}) })
) )
); );
default = []; default = [ ];
}; };
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 ({ type = types.attrsOf (types.submodule ({ name, config, options, ... }:
name, let
config,
options,
...
}: let
# submodule associated with # submodule associated with
submodule = findSubmodule { submodule = findSubmodule {
name = config.submodule; name = config.submodule;
@ -318,7 +280,8 @@ in {
inherit (submoduleDefinition) tags; inherit (submoduleDefinition) tags;
inherit (submodule) features; inherit (submodule) features;
}; };
in { in
{
options = { options = {
name = mkOption { name = mkOption {
description = "Submodule instance name"; description = "Submodule instance name";
@ -349,17 +312,16 @@ 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 = type = submoduleWithSpecialArgs
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 = { };
}; };
args = mkOption { args = mkOption {
@ -368,7 +330,7 @@ in {
}; };
})); }));
}; };
default = {}; default = { };
}; };
config = mkMerge ([ config = mkMerge ([
@ -380,19 +342,17 @@ in {
}) })
(filter (submodule: submodule.exportAs != null) cfg.imports)); (filter (submodule: submodule.exportAs != null) cfg.imports));
_m.features = ["submodules"]; _m.features = [ "submodules" ];
submodules.specialArgs.kubenix = kubenix; submodules.specialArgs.kubenix = kubenix;
# passthru kubenix.project to submodules # passthru kubenix.project to submodules
submodules.defaults = mkMerge [ submodules.defaults = mkMerge [
[ [{
{
default = { default = {
kubenix.project = parentConfig.kubenix.project; kubenix.project = parentConfig.kubenix.project;
}; };
} }]
]
(map (map
(propagate: { (propagate: {
@ -405,17 +365,15 @@ in {
(mkIf cfg.propagate.enable { (mkIf cfg.propagate.enable {
# if propagate is enabled and submodule has submodules included propagage defaults and imports # if propagate is enabled and submodule has submodules included propagage defaults and imports
submodules.defaults = [ submodules.defaults = [{
{ features = [ "submodules" ];
features = ["submodules"];
default = { default = {
submodules = { submodules = {
inherit (cfg) defaults; inherit (cfg) defaults;
inherit (cfg) imports; inherit (cfg) imports;
}; };
}; };
} }];
];
}) })
] ]
++ passthruConfig); ++ passthruConfig);

View file

@ -1,15 +1,9 @@
{ { config, pkgs, lib, kubenix, ... }:
config,
pkgs,
lib,
kubenix,
...
}:
with lib; let with lib; let
cfg = config.testing; cfg = config.testing;
testModule = { testModule = {
imports = [./evalTest.nix]; imports = [ ./evalTest.nix ];
# passthru testing configuration # passthru testing configuration
config._module.args = { config._module.args = {
@ -18,9 +12,9 @@ with lib; 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
@ -48,23 +42,23 @@ in {
features = mkOption { features = mkOption {
description = "List of features that test has to have to apply options"; description = "List of features that test has to have to apply options";
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
}; };
options = mkOption { options = mkOption {
description = "Options to apply to test"; description = "Options to apply to test";
type = types.unspecified; type = types.unspecified;
default = {}; default = { };
apply = default: {_file = "testing.common";} // default; apply = default: { _file = "testing.common"; } // default;
}; };
}; };
})); }));
default = []; default = [ ];
}; };
tests = mkOption { tests = mkOption {
description = "List of test cases"; description = "List of test cases";
default = []; default = [ ];
type = types.listOf (types.coercedTo types.path type = types.listOf (types.coercedTo types.path
(module: { (module: {
inherit module; inherit module;
@ -88,7 +82,7 @@ in {
args = mkOption { args = mkOption {
description = "Attribute set of extra args passed to tests"; description = "Attribute set of extra args passed to tests";
type = types.attrs; type = types.attrs;
default = {}; default = { };
}; };
success = mkOption { success = mkOption {

View file

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

View file

@ -1,23 +1,15 @@
{ { lib, config, pkgs, ... }:
lib,
config,
pkgs,
...
}:
with lib; let with lib; let
inherit (config) testing; inherit (config) testing;
cfg = testing.driver.kubetest; cfg = testing.driver.kubetest;
kubetest = import ./kubetestdrv.nix {inherit pkgs;}; kubetest = import ./kubetestdrv.nix { inherit pkgs; };
pythonEnv = pkgs.python38.withPackages (ps: pythonEnv = pkgs.python38.withPackages (ps: with ps; [
with ps;
[
pytest pytest
kubetest kubetest
kubernetes kubernetes
] ] ++ cfg.extraPackages);
++ cfg.extraPackages);
toTestScript = t: toTestScript = t:
if isString t.script if isString t.script
@ -28,7 +20,8 @@ with lib; let
'' ''
else t.script; else t.script;
tests = let tests =
let
# make sure tests are prefixed so that alphanumerical # make sure tests are prefixed so that alphanumerical
# sorting reproduces them in the same order as they # sorting reproduces them in the same order as they
# have been declared in the list. # have been declared in the list.
@ -43,9 +36,11 @@ with lib; let
); );
op = i: t: { op = i: t: {
path = toTestScript t; path = toTestScript t;
name = let name =
let
prefix = lib.fixedWidthNumber listLengthPadding i; prefix = lib.fixedWidthNumber listLengthPadding i;
in "${prefix}_${t.name}_test.py"; in
"${prefix}_${t.name}_test.py";
}; };
in in
pkgs.linkFarm "${testing.name}-tests" ( pkgs.linkFarm "${testing.name}-tests" (
@ -56,7 +51,8 @@ with lib; let
#!/usr/bin/env bash #!/usr/bin/env bash
${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;
@ -69,7 +65,7 @@ in {
extraPackages = mkOption { extraPackages = mkOption {
type = types.listOf types.package; type = types.listOf types.package;
description = "Extra packages to pass to tests"; description = "Extra packages to pass to tests";
default = []; default = [ ];
}; };
}; };

View file

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

View file

@ -1,10 +1,4 @@
{ { lib, config, testing, kubenix, ... }:
lib,
config,
testing,
kubenix,
...
}:
with lib; let with lib; let
modules = [ modules = [
# testing module # testing module
@ -29,13 +23,9 @@ with lib; let
# eval without checking # eval without checking
evaled' = kubenix.evalModules { evaled' = kubenix.evalModules {
modules = modules = modules ++ [{
modules
++ [
{
_module.args.check = false; _module.args.check = false;
} }];
];
}; };
# test configuration # test configuration
@ -45,10 +35,8 @@ with lib; let
testFeatures = evaled'.config._m.features; testFeatures = evaled'.config._m.features;
# common options that can be applied on this test # common options that can be applied on this test
commonOpts = commonOpts = filter
filter (d:
(
d:
(intersectLists d.features testFeatures) (intersectLists d.features testFeatures)
== d.features == d.features
|| (length d.features) == 0 || (length d.features) == 0
@ -59,7 +47,8 @@ with lib; let
modulesWithCommonOptions = modules ++ (map (d: d.options) commonOpts); modulesWithCommonOptions = modules ++ (map (d: d.options) commonOpts);
# evaled test # evaled test
evaled = let evaled =
let
evaled' = kubenix.evalModules { evaled' = kubenix.evalModules {
modules = modulesWithCommonOptions; modules = modulesWithCommonOptions;
}; };
@ -69,7 +58,8 @@ with lib; let
else if (builtins.tryEval evaled'.config.test.assertions).success else if (builtins.tryEval evaled'.config.test.assertions).success
then evaled' then evaled'
else null; else null;
in { in
{
options = { options = {
module = mkOption { module = mkOption {
description = "Module defining kubenix test"; description = "Module defining kubenix test";
@ -112,7 +102,7 @@ in {
description = "Test result"; description = "Test result";
type = types.unspecified; type = types.unspecified;
internal = true; internal = true;
default = []; default = [ ];
}; };
script = mkOption { script = mkOption {

View file

@ -1,9 +1,4 @@
{ { lib, config, pkgs, ... }:
lib,
config,
pkgs,
...
}:
with lib; let with lib; let
inherit (config) testing; inherit (config) testing;
@ -34,7 +29,8 @@ with lib; 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

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

View file

@ -1,8 +1,4 @@
{ { lib, config, ... }:
lib,
config,
...
}:
with lib; { with lib; {
options.test = { options.test = {
name = mkOption { name = mkOption {
@ -36,13 +32,11 @@ with lib; {
}; };
}; };
}); });
default = []; default = [ ];
example = [ example = [{
{
assertion = false; assertion = false;
message = "you can't enable this for some reason"; message = "you can't enable this for some reason";
} }];
];
description = '' description = ''
This option allows modules to express conditions that must This option allows modules to express conditions that must
hold for the evaluation of the system configuration to hold for the evaluation of the system configuration to

View file

@ -1,7 +1,6 @@
{ { stdenv
stdenv, , kubernetes
kubernetes, , installShellFiles
installShellFiles,
}: }:
stdenv.mkDerivation { stdenv.mkDerivation {
name = "kubectl-${kubernetes.version}"; name = "kubectl-${kubernetes.version}";
@ -10,9 +9,9 @@ stdenv.mkDerivation {
# split out (see homepage) # split out (see homepage)
dontUnpack = true; dontUnpack = true;
nativeBuildInputs = [installShellFiles]; nativeBuildInputs = [ installShellFiles ];
outputs = ["out" "man"]; outputs = [ "out" "man" ];
installPhase = '' installPhase = ''
install -D ${kubernetes}/bin/kubectl -t $out/bin install -D ${kubernetes}/bin/kubectl -t $out/bin

View file

@ -1,21 +1,20 @@
{ { stdenv
stdenv, , lib
lib, , fetchFromGitHub
fetchFromGitHub, , removeReferencesTo
removeReferencesTo, , which
which, , go
go, , makeWrapper
makeWrapper, , rsync
rsync, , installShellFiles
installShellFiles, , components ? [
components ? [
"cmd/kubelet" "cmd/kubelet"
"cmd/kube-apiserver" "cmd/kube-apiserver"
"cmd/kube-controller-manager" "cmd/kube-controller-manager"
"cmd/kube-proxy" "cmd/kube-proxy"
"cmd/kube-scheduler" "cmd/kube-scheduler"
"test/e2e/e2e.test" "test/e2e/e2e.test"
], ]
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "kubernetes"; pname = "kubernetes";
@ -28,11 +27,11 @@ stdenv.mkDerivation rec {
hash = "sha256-r9Clwr+87Ns4VXUW9F6cgks+LknY39ngbQgZ5UMZ0Vo="; hash = "sha256-r9Clwr+87Ns4VXUW9F6cgks+LknY39ngbQgZ5UMZ0Vo=";
}; };
nativeBuildInputs = [removeReferencesTo makeWrapper which go rsync installShellFiles]; nativeBuildInputs = [ removeReferencesTo makeWrapper which go rsync installShellFiles ];
outputs = ["out" "man" "pause"]; outputs = [ "out" "man" "pause" ];
patches = [./fixup-addonmanager-lib-path.patch]; patches = [ ./fixup-addonmanager-lib-path.patch ];
postPatch = '' postPatch = ''
# go env breaks the sandbox # go env breaks the sandbox
@ -84,7 +83,7 @@ stdenv.mkDerivation rec {
description = "Production-Grade Container Scheduling and Management"; description = "Production-Grade Container Scheduling and Management";
license = licenses.asl20; license = licenses.asl20;
homepage = "https://kubernetes.io"; homepage = "https://kubernetes.io";
maintainers = with maintainers; [johanot offline saschagrunert]; maintainers = with maintainers; [ johanot offline saschagrunert ];
platforms = platforms.unix; platforms = platforms.unix;
}; };
} }

View file

@ -1,37 +1,28 @@
{ { pkgs, lib }:
pkgs, let
lib,
}: let
generateIstio = import ./istio { generateIstio = import ./istio {
inherit inherit pkgs lib;
pkgs
lib
;
}; };
generateK8S = name: spec: generateK8S = name: spec:
import ./k8s { import ./k8s {
inherit inherit name pkgs lib spec;
name
pkgs
lib
spec
;
}; };
in { in
istio = pkgs.linkFarm "istio-generated" [ {
{ istio = pkgs.linkFarm "istio-generated" [{
name = "latest.nix"; name = "latest.nix";
path = generateIstio; path = generateIstio;
} }];
];
k8s = pkgs.linkFarm "k8s-generated" ( k8s = pkgs.linkFarm "k8s-generated" (
builtins.attrValues ( builtins.attrValues (
builtins.mapAttrs ( builtins.mapAttrs
version: sha: let (version: sha:
let
short = builtins.concatStringsSep "." (lib.lists.sublist 0 2 (builtins.splitVersion version)); short = builtins.concatStringsSep "." (lib.lists.sublist 0 2 (builtins.splitVersion version));
in { in
{
name = "v${short}.nix"; name = "v${short}.nix";
path = generateK8S "v${short}" (builtins.fetchurl { path = generateK8S "v${short}" (builtins.fetchurl {
url = "https://github.com/kubernetes/kubernetes/raw/v${version}/api/openapi-spec/swagger.json"; url = "https://github.com/kubernetes/kubernetes/raw/v${version}/api/openapi-spec/swagger.json";

View file

@ -1,7 +1,6 @@
{ { pkgs ? import <nixpkgs> { }
pkgs ? import <nixpkgs> {}, , lib ? pkgs.lib
lib ? pkgs.lib, , spec ? ./istio-schema.json
spec ? ./istio-schema.json,
}: }:
with lib; let with lib; let
gen = rec { gen = rec {
@ -20,14 +19,9 @@ with lib; let
then "null" then "null"
else builtins.toString value; else builtins.toString value;
removeEmptyLines = str: concatStringsSep "\n" (filter (l: builtins.match "[[:space:]]*" l != []) (splitString "\n" str)); removeEmptyLines = str: concatStringsSep "\n" (filter (l: builtins.match "[[:space:]]*" l != [ ]) (splitString "\n" str));
mkOption = { mkOption = { description ? null, type ? null, default ? null, apply ? null }:
description ? null,
type ? null,
default ? 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};''}
@ -53,7 +47,7 @@ with lib; let
hasTypeMapping = def: hasTypeMapping = def:
hasAttr "type" def hasAttr "type" def
&& elem def.type ["string" "integer" "boolean"]; && elem def.type [ "string" "integer" "boolean" ];
mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")''; mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")'';
@ -84,14 +78,12 @@ with lib; let
refDefinition = attr: head (tail (tail (splitString "/" attr."$ref"))); refDefinition = attr: head (tail (tail (splitString "/" attr."$ref")));
}; };
fixJSON = replaceStrings ["\\u"] ["u"]; fixJSON = replaceStrings [ "\\u" ] [ "u" ];
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path)); fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
genDefinitions = swagger: genDefinitions = swagger: with gen; (mapAttrs
with gen; (mapAttrs (_name: definition:
(
_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}"
@ -100,11 +92,10 @@ with lib; let
type = mapType definition; type = mapType definition;
} }
else { else {
options = options = mapAttrs
mapAttrs (propName: property:
( let
propName: property: let isRequired = elem propName (definition.required or [ ]);
isRequired = elem propName (definition.required or []);
requiredOrNot = type: requiredOrNot = type:
if isRequired if isRequired
then type then type
@ -142,9 +133,11 @@ with lib; 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,12 +183,11 @@ with lib; let
// optionProperties) // optionProperties)
) )
definition.properties; definition.properties;
config = let config =
optionalProps = let
filterAttrs optionalProps = filterAttrs
( (propName: _property:
propName: _property: !(elem propName (definition.required or [ ]))
!(elem propName (definition.required or []))
) )
definition.properties; definition.properties;
in in
@ -204,8 +196,7 @@ with lib; let
) )
swagger.definitions); swagger.definitions);
genResources = swagger: genResources = swagger: (mapAttrsToList
(mapAttrsToList
(_name: property: rec { (_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";
@ -214,8 +205,7 @@ with lib; let
ref = removePrefix "#/definitions/" property."$ref"; ref = removePrefix "#/definitions/" property."$ref";
}) })
(filterAttrs (filterAttrs
( (_name: property:
_name: property:
(hasPrefix "me.snowdrop.istio.api" property.javaType) (hasPrefix "me.snowdrop.istio.api" property.javaType)
&& hasSuffix "Spec" property.javaType && hasSuffix "Spec" property.javaType
) )
@ -229,8 +219,7 @@ with lib; let
ref = removePrefix "#/definitions/" property."$ref"; ref = removePrefix "#/definitions/" property."$ref";
}) })
(filterAttrs (filterAttrs
( (_name: property:
_name: property:
(hasPrefix "me.snowdrop.istio.mixer" property.javaType) (hasPrefix "me.snowdrop.istio.mixer" property.javaType)
&& hasSuffix "Spec" property.javaType && hasSuffix "Spec" property.javaType
) )
@ -367,14 +356,14 @@ with lib; let
} }
''; '';
in in
pkgs.runCommand "istio-gen.nix" pkgs.runCommand "istio-gen.nix"
{ {
buildInputs = [pkgs.nixpkgs-fmt]; buildInputs = [ pkgs.nixpkgs-fmt ];
} '' } ''
cat << 'GENERATED' > ./raw cat << 'GENERATED' > ./raw
"${generated}" "${generated}"
GENERATED GENERATED
nixpkgs-fmt ./raw nixpkgs-fmt ./raw
cp ./raw $out cp ./raw $out
'' ''

View file

@ -1,9 +1,4 @@
{ { name, pkgs, lib, spec }:
name,
pkgs,
lib,
spec,
}:
with lib; let with lib; let
gen = rec { gen = rec {
mkMerge = values: ''mkMerge [${concatMapStrings mkMerge = values: ''mkMerge [${concatMapStrings
@ -21,14 +16,9 @@ with lib; let
then "null" then "null"
else builtins.toString value; else builtins.toString value;
removeEmptyLines = str: concatStringsSep "\n" (filter (l: builtins.match "[[:space:]]*" l != []) (splitString "\n" str)); removeEmptyLines = str: concatStringsSep "\n" (filter (l: builtins.match "[[:space:]]*" l != [ ]) (splitString "\n" str));
mkOption = { mkOption = { description ? null, type ? null, default ? null, apply ? null }:
description ? null,
type ? null,
default ? 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};''}
@ -52,9 +42,7 @@ with lib; let
loaOf = type: "(types.loaOf ${type})"; loaOf = type: "(types.loaOf ${type})";
}; };
hasTypeMapping = def: hasTypeMapping = def: hasAttr "type" def && elem def.type [ "string" "integer" "boolean" ];
hasAttr "type" def
&& elem def.type ["string" "integer" "boolean"];
mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")''; mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")'';
@ -87,7 +75,8 @@ with lib; 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 = substring 1 10; getVersion = substring 1 10;
splitVersion = v: builtins.splitVersion (getVersion v); splitVersion = v: builtins.splitVersion (getVersion v);
isAlpha = v: elem "alpha" (splitVersion v); isAlpha = v: elem "alpha" (splitVersion v);
@ -103,27 +92,25 @@ with lib; let
in in
builtins.compareVersions v1 v2; builtins.compareVersions v1 v2;
fixJSON = replaceStrings ["\\u"] ["u"]; fixJSON = replaceStrings [ "\\u" ] [ "u" ];
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path)); fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
genDefinitions = swagger: genDefinitions = swagger: with gen; mapAttrs
with gen; (_name: definition:
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}"
else if !(hasAttr "properties" definition) else if !(hasAttr "properties" definition)
then {} then { }
# in other case it's an actual definition # in other case it's an actual definition
else { else {
options = options =
mapAttrs mapAttrs
( (
propName: property: let propName: property:
isRequired = elem propName (definition.required or []); let
isRequired = elem propName (definition.required or [ ]);
requiredOrNot = type: requiredOrNot = type:
if isRequired if isRequired
then type then type
@ -159,11 +146,13 @@ with lib; let
else else
# make it an attribute set of submodules if only x-kubernetes-patch-merge-key is present, or # make it an attribute set of submodules if only x-kubernetes-patch-merge-key is present, or
# x-kubernetes-patch-merge-key == x-kubernetes-list-map-keys. # x-kubernetes-patch-merge-key == x-kubernetes-list-map-keys.
if (hasAttr "x-kubernetes-patch-merge-key" property) && (!(hasAttr "x-kubernetes-list-map-keys" property) || (property."x-kubernetes-list-map-keys" == [property."x-kubernetes-patch-merge-key"])) if (hasAttr "x-kubernetes-patch-merge-key" property) && (!(hasAttr "x-kubernetes-list-map-keys" property) || (property."x-kubernetes-list-map-keys" == [ property."x-kubernetes-patch-merge-key" ]))
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;
} }
# in other case it's a simple list # in other case it's a simple list
@ -173,13 +162,15 @@ with lib; let
if if
hasAttr "properties" swagger.definitions.${refDefinition property.items} hasAttr "properties" swagger.definitions.${refDefinition property.items}
&& hasAttr "name" swagger.definitions.${refDefinition property.items}.properties && hasAttr "name" swagger.definitions.${refDefinition property.items}.properties
then let then
let
mergeKey = "name"; mergeKey = "name";
in { in
{
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey ( type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey (
if hasAttr "x-kubernetes-list-map-keys" property if hasAttr "x-kubernetes-list-map-keys" property
then property."x-kubernetes-list-map-keys" then property."x-kubernetes-list-map-keys"
else [] else [ ]
)); ));
apply = attrsToList; apply = attrsToList;
} }
@ -227,12 +218,13 @@ with lib; let
// optionProperties) // optionProperties)
) )
definition.properties; definition.properties;
config = let config =
let
optionalProps = optionalProps =
filterAttrs filterAttrs
( (
propName: _property: propName: _property:
!(elem propName (definition.required or [])) !(elem propName (definition.required or [ ]))
) )
definition.properties; definition.properties;
in in
@ -241,10 +233,8 @@ with lib; let
) )
swagger.definitions; swagger.definitions;
mapCharPairs = f: s1: s2: mapCharPairs = f: s1: s2: concatStrings (imap0
concatStrings (imap0 (i: c1:
(
i: c1:
f i c1 ( f i c1 (
if i >= stringLength s2 if i >= stringLength s2
then "" then ""
@ -253,10 +243,8 @@ with lib; let
) )
(stringToCharacters s1)); (stringToCharacters s1));
getAttrName = resource: kind: getAttrName = resource: kind: mapCharPairs
mapCharPairs (i: c1: c2:
(
i: c1: c2:
if hasPrefix "API" kind && i == 0 if hasPrefix "API" kind && i == 0
then "A" then "A"
else if i == 0 else if i == 0
@ -268,9 +256,9 @@ with lib; let
resource resource
kind; kind;
genResourceTypes = swagger: genResourceTypes = swagger: mapAttrs'
mapAttrs' (name: path:
(name: path: let 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;
@ -309,10 +297,8 @@ with lib; let
}) })
resourceTypes); resourceTypes);
resourcesTypesByKindSortByVersion = resourcesTypesByKindSortByVersion = mapAttrs
mapAttrs (_kind: resourceTypes:
(
_kind: resourceTypes:
reverseList (sort reverseList (sort
( (
r1: r2: r1: r2:
@ -322,8 +308,7 @@ with lib; let
) )
resourceTypesByKind; resourceTypesByKind;
latestResourceTypesByKind = latestResourceTypesByKind = mapAttrs (_kind: last) resourcesTypesByKindSortByVersion;
mapAttrs (_kind: last) resourcesTypesByKindSortByVersion;
genResourceOptions = resource: genResourceOptions = resource:
with gen; let with gen; let
@ -338,7 +323,7 @@ with lib; let
mkOption { mkOption {
inherit (resource) description; inherit (resource) description;
type = types.attrsOf (submoduleForDefinition' resource); type = types.attrsOf (submoduleForDefinition' resource);
default = {}; default = { };
}; };
generated = '' generated = ''
@ -530,14 +515,14 @@ with lib; let
} }
''; '';
in in
pkgs.runCommand "k8s-${name}-gen.nix" pkgs.runCommand "k8s-${name}-gen.nix"
{ {
buildInputs = [pkgs.nixpkgs-fmt]; buildInputs = [ pkgs.nixpkgs-fmt ];
} '' } ''
cat << 'GENERATED' > ./raw cat << 'GENERATED' > ./raw
${generated} ${generated}
GENERATED GENERATED
nixpkgs-fmt ./raw nixpkgs-fmt ./raw
cp ./raw $out cp ./raw $out
'' ''

View file

@ -1,23 +1,19 @@
{ { kubectl
kubectl, , vals
vals, , colordiff
colordiff, , evalModules
evalModules, , runCommand
runCommand, , writeShellScript
writeShellScript, , module ? { }
module ? {}, , specialArgs ? { }
specialArgs ? {}, }:
}: let let
kubernetes = kubernetes = (evalModules {
(evalModules {
inherit module specialArgs; inherit module specialArgs;
}) }).config.kubernetes or { };
.config
.kubernetes
or {};
in in
runCommand "kubenix" runCommand "kubenix"
{ {
kubeconfig = kubernetes.kubeconfig or ""; kubeconfig = kubernetes.kubeconfig or "";
result = kubernetes.result or ""; result = kubernetes.result or "";
@ -27,7 +23,7 @@ in
diff = writeShellScript "kubenix-diff" '' diff = writeShellScript "kubenix-diff" ''
${colordiff}/bin/colordiff --nobanner -N -u -I ' kubenix/hash: ' -I ' generation: ' $@ ${colordiff}/bin/colordiff --nobanner -N -u -I ' kubenix/hash: ' -I ' generation: ' $@
''; '';
} '' } ''
set -euo pipefail set -euo pipefail
mkdir -p $out/bin mkdir -p $out/bin
@ -96,4 +92,4 @@ in
EOF EOF
chmod +x $out/bin/kubenix chmod +x $out/bin/kubenix
'' ''

View file

@ -1,20 +1,15 @@
{ { system ? builtins.currentSystem, evalModules ? (import ../. { }).evalModules.${system} }:
system ? builtins.currentSystem, { k8sVersion ? "1.23"
evalModules ? (import ../. {}).evalModules.${system}, , registry ? throw "Registry url not defined"
}: { , doThrowError ? true
k8sVersion ? "1.23", # whether any testing error should throw an error
registry ? throw "Registry url not defined", , enabledTests ? null
doThrowError ? true, # whether any testing error should throw an error }:
enabledTests ? null, let
}: let
inherit inherit
((evalModules { ((evalModules {
module = { module = { kubenix, pkgs, ... }: {
kubenix, imports = [ kubenix.modules.testing ];
pkgs,
...
}: {
imports = [kubenix.modules.testing];
testing = { testing = {
inherit doThrowError enabledTests; inherit doThrowError enabledTests;
@ -38,21 +33,19 @@
./submodules/passthru.nix ./submodules/passthru.nix
]; ];
args = {images = pkgs.callPackage ./images.nix {};}; args = { images = pkgs.callPackage ./images.nix { }; };
docker.registryUrl = registry; docker.registryUrl = registry;
common = [ common = [{
{ features = [ "k8s" ];
features = ["k8s"];
options = { options = {
kubernetes.version = k8sVersion; kubernetes.version = k8sVersion;
}; };
} }];
];
}; };
}; };
})) }))
config config
; ;
in in
config.testing // {recurseForDerivations = true;} config.testing // { recurseForDerivations = true; }

View file

@ -1,11 +1,4 @@
{ { config, lib, pkgs, kubenix, helm, ... }:
config,
lib,
pkgs,
kubenix,
helm,
...
}:
with lib; with lib;
with kubenix.lib; with kubenix.lib;
with pkgs.dockerTools; let with pkgs.dockerTools; let
@ -35,8 +28,9 @@ with pkgs.dockerTools; 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 kubenix.modules.docker]; {
imports = [ kubenix.modules.test kubenix.modules.helm kubenix.modules.k8s kubenix.modules.docker ];
docker.images = { docker.images = {
postgresql.image = postgresql; postgresql.image = postgresql;

View file

@ -1,18 +1,14 @@
{ { pkgs, dockerTools, lib, ... }:
pkgs,
dockerTools,
lib,
...
}:
with lib; { with lib; {
curl = dockerTools.buildLayeredImage { curl = dockerTools.buildLayeredImage {
name = "curl"; name = "curl";
tag = "latest"; tag = "latest";
config.Cmd = ["${pkgs.bash}" "-c" "sleep infinity"]; config.Cmd = [ "${pkgs.bash}" "-c" "sleep infinity" ];
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;
@ -38,7 +34,7 @@ with lib; {
dockerTools.buildLayeredImage { dockerTools.buildLayeredImage {
name = "xtruder/nginx"; name = "xtruder/nginx";
tag = "latest"; tag = "latest";
contents = [pkgs.nginx]; contents = [ pkgs.nginx ];
extraCommands = '' extraCommands = ''
mkdir -p etc mkdir -p etc
chmod u+w etc chmod u+w etc
@ -50,9 +46,9 @@ with lib; {
echo "nginx:x:1000:nginx" > etc/group echo "nginx:x:1000:nginx" > etc/group
''; '';
config = { config = {
Cmd = ["nginx" "-c" nginxConf]; Cmd = [ "nginx" "-c" nginxConf ];
ExposedPorts = { ExposedPorts = {
"${nginxPort}/tcp" = {}; "${nginxPort}/tcp" = { };
}; };
}; };
}; };

View file

@ -1,5 +1,5 @@
{kubenix, ...}: { { kubenix, ... }: {
imports = with kubenix.modules; [test k8s istio]; imports = with kubenix.modules; [ test k8s istio ];
test = { test = {
name = "istio-bookinfo"; name = "istio-bookinfo";
@ -17,7 +17,7 @@
name = "http"; name = "http";
protocol = "HTTP"; protocol = "HTTP";
}; };
hosts = ["*"]; hosts = [ "*" ];
} }
]; ];
}; };
@ -25,8 +25,8 @@
VirtualService.bookinfo = { VirtualService.bookinfo = {
spec = { spec = {
hosts = ["*"]; hosts = [ "*" ];
gateways = ["bookinfo-gateway"]; gateways = [ "bookinfo-gateway" ];
http = [ http = [
{ {
match = [ match = [

View file

@ -1,24 +1,18 @@
{ { config, lib, kubenix, ... }:
config,
lib,
kubenix,
...
}:
with lib; let with lib; 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 = {
name = "k8s-crd"; name = "k8s-crd";
description = "Simple test tesing CRD"; description = "Simple test tesing CRD";
enable = builtins.compareVersions config.kubernetes.version "1.8" >= 0; enable = builtins.compareVersions config.kubernetes.version "1.8" >= 0;
assertions = [ assertions = [{
{
message = "Custom resource should have correct version set"; message = "Custom resource should have correct version set";
assertion = latestCrontab.apiVersion == "stable.example.com/v2"; assertion = latestCrontab.apiVersion == "stable.example.com/v2";
} }];
];
script = '' script = ''
@pytest.mark.applymanifest('${config.kubernetes.resultYAML}') @pytest.mark.applymanifest('${config.kubernetes.resultYAML}')
def test_testing_module(kube): def test_testing_module(kube):

View file

@ -1,14 +1,10 @@
{ { config, lib, kubenix, ... }:
config,
lib,
kubenix,
...
}:
with lib; let with lib; let
inherit (config.kubernetes.api.resources.pods) pod1; inherit (config.kubernetes.api.resources.pods) pod1;
inherit (config.kubernetes.api.resources.pods) pod2; inherit (config.kubernetes.api.resources.pods) pod2;
in { in
imports = with kubenix.modules; [test k8s]; {
imports = with kubenix.modules; [ test k8s ];
test = { test = {
name = "k8s-defaults"; name = "k8s-defaults";
@ -29,7 +25,7 @@ in {
]; ];
}; };
kubernetes.resources.pods.pod1 = {}; kubernetes.resources.pods.pod1 = { };
kubernetes.resources.pods.pod2 = { kubernetes.resources.pods.pod2 = {
metadata.labels.custom-label = "value"; metadata.labels.custom-label = "value";
@ -48,7 +44,7 @@ in {
} }
{ {
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") {
conditional-annotation = "value"; conditional-annotation = "value";
}; };

View file

@ -1,11 +1,4 @@
{ { config, lib, kubenix, images, test, ... }:
config,
lib,
kubenix,
images,
test,
...
}:
with lib; let with lib; let
cfg = config.kubernetes.api.resources.deployments.nginx; cfg = config.kubernetes.api.resources.deployments.nginx;
image = images.nginx; image = images.nginx;
@ -17,17 +10,16 @@ with lib; let
inherit (config.kubernetes) namespace; inherit (config.kubernetes) namespace;
name = "curl"; name = "curl";
}; };
spec.containers = [ spec.containers = [{
{
name = "curl"; name = "curl";
image = config.docker.images.curl.path; image = config.docker.images.curl.path;
args = ["curl" "--retry" "20" "--retry-connrefused" "http://nginx"]; args = [ "curl" "--retry" "20" "--retry-connrefused" "http://nginx" ];
} }];
];
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 = {
name = "k8s-deployment"; name = "k8s-deployment";

View file

@ -1,14 +1,10 @@
{ { config, lib, kubenix, ... }:
config,
lib,
kubenix,
...
}:
with lib; let with lib; 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 = {
name = "k8s-imports"; name = "k8s-imports";

View file

@ -1,11 +1,6 @@
{ { config, lib, kubenix, ... }:
config,
lib,
kubenix,
...
}:
with lib; { with lib; {
imports = with kubenix.modules; [test k8s]; imports = with kubenix.modules; [ test k8s ];
test = { test = {
name = "k8s-order"; name = "k8s-order";
@ -39,7 +34,7 @@ with lib; {
plural = "crontabs"; plural = "crontabs";
singular = "crontab"; singular = "crontab";
kind = "CronTab"; kind = "CronTab";
shortNames = ["ct"]; shortNames = [ "ct" ];
}; };
}; };
}; };
@ -62,7 +57,7 @@ with lib; {
} }
]; ];
kubernetes.resources.namespaces.test = {}; kubernetes.resources.namespaces.test = { };
kubernetes.resources."stable.example.com"."v1".CronTab.crontab.spec.schedule = "* * * * *"; kubernetes.resources."stable.example.com"."v1".CronTab.crontab.spec.schedule = "* * * * *";
} }

View file

@ -1,11 +1,9 @@
{ { config, kubenix, ... }:
config, let
kubenix,
...
}: 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 = {
name = "k8s-simple"; name = "k8s-simple";
@ -22,5 +20,5 @@ in {
]; ];
}; };
kubernetes.resources.pods.nginx = {}; kubernetes.resources.pods.nginx = { };
} }

View file

@ -1,13 +1,6 @@
{ { name, config, lib, kubenix, images, ... }:
name,
config,
lib,
kubenix,
images,
...
}:
with lib; { with lib; {
imports = with kubenix.modules; [test submodules k8s docker]; imports = with kubenix.modules; [ test submodules k8s docker ];
test = { test = {
name = "k8s-submodule"; name = "k8s-submodule";
@ -26,14 +19,9 @@ with lib; {
kubernetes.namespace = "test-namespace"; kubernetes.namespace = "test-namespace";
submodules.imports = [ submodules.imports = [{
{ module = { name, config, ... }: {
module = { imports = with kubenix.modules; [ submodule k8s docker ];
name,
config,
...
}: {
imports = with kubenix.modules; [submodule k8s docker];
config = { config = {
submodule = { submodule = {
@ -52,15 +40,12 @@ with lib; {
docker.images.nginx.image = images.nginx; docker.images.nginx.image = images.nginx;
}; };
}; };
} }];
];
kubernetes.api.defaults = [ kubernetes.api.defaults = [{
{
propagate = true; propagate = true;
default.metadata.labels.my-label = "my-value"; default.metadata.labels.my-label = "my-value";
} }];
];
submodules.instances.passthru = { submodules.instances.passthru = {
submodule = "test-submodule"; submodule = "test-submodule";

View file

@ -1,10 +1,4 @@
{ { name, config, lib, kubenix, ... }:
name,
config,
lib,
kubenix,
...
}:
with lib; let with lib; let
inherit (config.submodules.instances) instance1; inherit (config.submodules.instances) instance1;
inherit (config.submodules.instances) instance2; inherit (config.submodules.instances) instance2;
@ -13,8 +7,8 @@ with lib; let
inherit (config.submodules.instances) instance5; inherit (config.submodules.instances) instance5;
inherit (config.submodules.instances) versioned-submodule; inherit (config.submodules.instances) versioned-submodule;
submodule = {...}: { submodule = { ... }: {
imports = [kubenix.modules.submodule]; imports = [ kubenix.modules.submodule ];
options.submodule.args = { options.submodule.args = {
value = mkOption { value = mkOption {
@ -28,8 +22,9 @@ with lib; let
}; };
}; };
}; };
in { in
imports = with kubenix.modules; [test submodules]; {
imports = with kubenix.modules; [ test submodules ];
test = { test = {
name = "submodules-defaults"; name = "submodules-defaults";
@ -80,7 +75,7 @@ in {
{ {
submodule = { submodule = {
name = "submodule1"; name = "submodule1";
tags = ["tag1"]; tags = [ "tag1" ];
}; };
} }
]; ];
@ -91,7 +86,7 @@ in {
{ {
submodule = { submodule = {
name = "submodule2"; name = "submodule2";
tags = ["tag2"]; tags = [ "tag2" ];
}; };
} }
]; ];
@ -102,7 +97,7 @@ in {
{ {
submodule = { submodule = {
name = "submodule3"; name = "submodule3";
tags = ["tag2"]; tags = [ "tag2" ];
}; };
} }
]; ];
@ -146,11 +141,11 @@ in {
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";
} }
{ {
@ -158,7 +153,7 @@ in {
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";
}; };
} }

View file

@ -1,14 +1,7 @@
{ { name, config, lib, kubenix, subm-lib, ... }:
name,
config,
lib,
kubenix,
subm-lib,
...
}:
with lib; let with lib; let
submodule = { submodule = {
imports = [kubenix.modules.submodule]; imports = [ kubenix.modules.submodule ];
config.submodule = { config.submodule = {
name = "subm"; name = "subm";
@ -17,8 +10,9 @@ with lib; let
}; };
}; };
}; };
in { in
imports = with kubenix.modules; [test submodules]; {
imports = with kubenix.modules; [ test submodules ];
test = { test = {
name = "submodules-exports"; name = "submodules-exports";
@ -33,7 +27,7 @@ in {
submodules.imports = [ submodules.imports = [
{ {
modules = [submodule]; modules = [ submodule ];
exportAs = "subm-lib"; exportAs = "subm-lib";
} }
]; ];

View file

@ -1,27 +1,22 @@
{ { name, config, lib, kubenix, ... }:
name,
config,
lib,
kubenix,
...
}:
with lib; let with lib; let
submodule = {name, ...}: { submodule = { name, ... }: {
imports = [kubenix.modules.submodule]; imports = [ kubenix.modules.submodule ];
config.submodule = { config.submodule = {
name = "subm"; name = "subm";
passthru.global.${name} = "true"; passthru.global.${name} = "true";
}; };
}; };
in { in
imports = with kubenix.modules; [test submodules]; {
imports = with kubenix.modules; [ test submodules ];
options = { options = {
global = mkOption { global = mkOption {
description = "Global value"; description = "Global value";
type = types.attrs; type = types.attrs;
default = {}; default = { };
}; };
}; };
@ -45,11 +40,9 @@ in {
]; ];
}; };
submodules.imports = [ submodules.imports = [{
{ modules = [ submodule ];
modules = [submodule]; }];
}
];
submodules.instances.inst1 = { submodules.instances.inst1 = {
submodule = "subm"; submodule = "subm";

View file

@ -1,15 +1,10 @@
{ { name, config, lib, kubenix, ... }:
name,
config,
lib,
kubenix,
...
}:
with lib; let with lib; let
cfg = config.submodules.instances.instance; cfg = config.submodules.instances.instance;
inherit (cfg.config.submodule) args; inherit (cfg.config.submodule) args;
in { in
imports = with kubenix.modules; [test submodules]; {
imports = with kubenix.modules; [ test submodules ];
test = { test = {
name = "submodules-simple"; name = "submodules-simple";
@ -45,8 +40,8 @@ in {
submodules.propagate.enable = true; submodules.propagate.enable = true;
submodules.imports = [ submodules.imports = [
{ {
module = {submodule, ...}: { module = { submodule, ... }: {
imports = [kubenix.modules.submodule]; imports = [ kubenix.modules.submodule ];
options.submodule.args = { options.submodule.args = {
name = mkOption { name = mkOption {
@ -62,7 +57,7 @@ in {
config = { config = {
submodule.name = "submodule"; submodule.name = "submodule";
submodule.tags = ["tag"]; submodule.tags = [ "tag" ];
}; };
}; };
} }

View file

@ -1,17 +1,11 @@
{ { name, config, lib, kubenix, ... }:
name,
config,
lib,
kubenix,
...
}:
with lib; let with lib; 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;
inst-latest = config.submodules.instances.inst-latest.config; inst-latest = config.submodules.instances.inst-latest.config;
submodule = { submodule = {
imports = [kubenix.modules.submodule]; imports = [ kubenix.modules.submodule ];
options.version = mkOption { options.version = mkOption {
type = types.str; type = types.str;
@ -20,8 +14,9 @@ with lib; 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 = {
name = "submodules-versioning"; name = "submodules-versioning";

View file

@ -12,19 +12,17 @@ let
"1.26.5" = "sha256:1dyqvggyvqw3z9sml2x06v1l9kynqcs8bkfrkx8jy81gkvg7qxdi"; "1.26.5" = "sha256:1dyqvggyvqw3z9sml2x06v1l9kynqcs8bkfrkx8jy81gkvg7qxdi";
"1.27.2" = "sha256:1yqcds6lvpnvc5dsv9pnvp5qb3kc5y6cdgx827szljdlwf51wd15"; "1.27.2" = "sha256:1yqcds6lvpnvc5dsv9pnvp5qb3kc5y6cdgx827szljdlwf51wd15";
}; };
in { in
{
inherit full; inherit full;
# sorted list of major.minor version numbers # sorted list of major.minor version numbers
# NOTE: avoiding pulling in lib here (not for any good reason) # NOTE: avoiding pulling in lib here (not for any good reason)
versions = versions = with builtins; map
map (v: let (v: (
arr = builtins.splitVersion v; concatStringsSep "." [
in ( (elemAt (splitVersion v) 0)
builtins.concatStringsSep "." (elemAt (splitVersion v) 1)
[
(builtins.elemAt arr 0)
(builtins.elemAt arr 1)
] ]
)) ))
(builtins.attrNames full); (attrNames full);
} }