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

@ -1,14 +1,13 @@
( (
import import
( (
let let
lock = builtins.fromJSON (builtins.readFile ./flake.lock); lock = builtins.fromJSON (builtins.readFile ./flake.lock);
in in
fetchTarball { fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
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,78 +20,75 @@
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) {
} default = substFunction opt.default;
// lib.optionalAttrs (opt ? default) { } // lib.optionalAttrs (opt ? type) {
default = substFunction opt.default; type = substFunction opt.type;
} } // lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != [ ]) {
// lib.optionalAttrs (opt ? type) { relatedPackages = genRelatedPackages opt.relatedPackages;
type = substFunction opt.type; }
}
// lib.optionalAttrs
(opt ? relatedPackages && opt.relatedPackages != [])
{
relatedPackages = genRelatedPackages opt.relatedPackages;
}
); );
genRelatedPackages = packages: let genRelatedPackages = packages:
unpack = p: let
if lib.isString p unpack = p:
then {name = p;} if lib.isString p
else if lib.isList p then { name = p; }
then {path = p;} else if lib.isList p
else p; then { path = p; }
describe = args: let else p;
title = args.title or null; describe = args:
name = args.name or (lib.concatStringsSep "." args.path); let
path = args.path or [args.name]; title = args.title or null;
package = name = args.name or (lib.concatStringsSep "." args.path);
args.package path = args.path or [ args.name ];
or (lib.attrByPath path package =
(throw args.package
"Invalid package attribute path '${toString path}'") or (lib.attrByPath path
pkgs); (throw
in "Invalid package attribute path '${toString path}'")
"<listitem>" pkgs);
+ "<para><literal>${lib.optionalString (title != null) in
"<listitem>"
+ "<para><literal>${lib.optionalString (title != null)
"${title} aka "}pkgs.${name} (${package.meta.name})</literal>" "${title} aka "}pkgs.${name} (${package.meta.name})</literal>"
+ lib.optionalString (!package.meta.available) + lib.optionalString (!package.meta.available)
" <emphasis>[UNAVAILABLE]</emphasis>" " <emphasis>[UNAVAILABLE]</emphasis>"
+ ": ${package.meta.description or "???"}.</para>" + ": ${package.meta.description or "???"}.</para>"
+ lib.optionalString (args ? comment) + lib.optionalString (args ? comment)
"\n<para>${args.comment}</para>" "\n<para>${args.comment}</para>"
+ lib.optionalString (package.meta ? longDescription) + lib.optionalString (package.meta ? longDescription)
"\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:
ise = lib.hasPrefix "enable"; let
isp = lib.hasPrefix "package"; ise = lib.hasPrefix "enable";
cmp = isp = lib.hasPrefix "package";
lib.splitByAndCompare ise lib.compare cmp =
(lib.splitByAndCompare isp lib.compare lib.compare); lib.splitByAndCompare ise lib.compare
in (lib.splitByAndCompare isp lib.compare lib.compare);
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

193
flake.nix
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 in
lib' = lib.extend (lib: _self: import ./lib/upstreamables.nix {inherit lib pkgs;});
attrs' = builtins.removeAttrs attrs ["module"];
in
lib'.evalModules (lib.recursiveUpdate lib'.evalModules (lib.recursiveUpdate
{ {
modules = modules = modules ++ [{
modules config._module.args = {
++ [ inherit pkgs;
{ name = "default";
config._module.args = { };
inherit pkgs; }];
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,103 +74,93 @@
''; '';
}; };
formatter = formatter = (inputs.treefmt.lib.evalModule pkgs {
(inputs.treefmt.lib.evalModule pkgs { projectRootFile = "flake.nix";
projectRootFile = "flake.nix"; programs = {
programs = { nixpkgs-fmt.enable = true;
alejandra.enable = true; black.enable = true;
black.enable = true; prettier.enable = true;
prettier.enable = true; shfmt.enable = true;
shfmt.enable = true; };
}; settings.global.excludes = [
settings.global.excludes = [ "docs/themes/*"
"docs/themes/*" "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
nix build '.#docs' -o ./docs/data/options.json nix build '.#docs' -o ./docs/data/options.json
# remove all old module pages # remove all old module pages
rm ./docs/content/modules/[!_]?*.md || true rm ./docs/content/modules/[!_]?*.md || true
# create a page for each module in hugo # create a page for each module in hugo
for mod in ${builtins.toString (builtins.attrNames self.nixosModules.kubenix)}; do for mod in ${builtins.toString (builtins.attrNames self.nixosModules.kubenix)}; do
[[ $mod == "base" ]] && mod=kubenix [[ $mod == "base" ]] && mod=kubenix
[[ $mod == "k8s" ]] && mod=kubernetes [[ $mod == "k8s" ]] && mod=kubernetes
[[ $mod == "submodule"* ]] && continue [[ $mod == "submodule"* ]] && continue
echo "&nbsp; {{< options >}}" > ./docs/content/modules/$mod.md echo "&nbsp; {{< options >}}" > ./docs/content/modules/$mod.md
done done
# 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
rm -rf $dir rm -rf $dir
mkdir $dir mkdir $dir
nix build '.#generate-k8s' nix build '.#generate-k8s'
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}); evalModules = self.evalModules.${system};
evalModules = self.evalModules.${system}; };
}; docs = import ./docs {
docs = import ./docs { inherit pkgs;
inherit pkgs; options = (self.evalModules.${system} {
options = modules = builtins.attrValues (builtins.removeAttrs
(self.evalModules.${system} { # the submodules module currently doesn't evaluate:
modules = builtins.attrValues (builtins.removeAttrs # error: No module found name/latest
# the submodules module currently doesn't evaluate: # not sure how important that documentation is a this time
# error: No module found name/latest self.nixosModules.kubenix [ "submodule" "submodules" ]);
# not sure how important that documentation is a this time }).options;
self.nixosModules.kubenix ["submodule" "submodules"]); };
}) }
.options; // pkgs.lib.attrsets.mapAttrs' (name: value: pkgs.lib.attrsets.nameValuePair "generate-${name}" value)
}; (builtins.removeAttrs (pkgs.callPackage ./pkgs/generators { }) [ "override" "overrideDerivation" ]);
}
// pkgs.lib.attrsets.mapAttrs' (name: value: pkgs.lib.attrsets.nameValuePair "generate-${name}" value)
(builtins.removeAttrs (pkgs.callPackage ./pkgs/generators {}) ["override" "overrideDerivation"]);
checks = let checks =
wasSuccess = suite: let
if suite.success wasSuccess = suite:
then pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-succeeded" {} "echo success > $out" if suite.success
else pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-failed" {} "exit 1"; then pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-succeeded" { } "echo success > $out"
examples = import ./docs/content/examples; else pkgs.runCommandNoCC "testing-suite-config-assertions-for-${suite.name}-failed" { } "exit 1";
mkK8STests = attrs: examples = import ./docs/content/examples;
(import ./tests {inherit evalModules;}) mkK8STests = attrs:
({registry = "docker.io/gatehub";} // attrs); (import ./tests { inherit evalModules; })
in ({ registry = "docker.io/gatehub"; } // attrs);
in
{ {
# TODO: access "success" derivation with nice testing utils for nice output # TODO: access "success" derivation with nice testing utils for nice output
testing = wasSuccess examples.testing.config.testing; testing = wasSuccess examples.testing.config.testing;
@ -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,46 +1,42 @@
{ { 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 }:
valuesJsonFile = builtins.toFile "${name}-values.json" (builtins.toJSON values); let
# The `helm template` and YAML -> JSON steps are separate `runCommand` derivations for easier debuggability valuesJsonFile = builtins.toFile "${name}-values.json" (builtins.toJSON values);
resourcesYaml = runCommand "${name}.yaml" {nativeBuildInputs = [kubernetes-helm];} '' # The `helm template` and YAML -> JSON steps are separate `runCommand` derivations for easier debuggability
helm template "${name}" \ resourcesYaml = runCommand "${name}.yaml" { nativeBuildInputs = [ kubernetes-helm ]; } ''
${optionalString (kubeVersion != null) "--kube-version ${kubeVersion}"} \ helm template "${name}" \
${optionalString (namespace != null) "--namespace ${namespace}"} \ ${optionalString (kubeVersion != null) "--kube-version ${kubeVersion}"} \
${optionalString (values != {}) "-f ${valuesJsonFile}"} \ ${optionalString (namespace != null) "--namespace ${namespace}"} \
${optionalString includeCRDs "--include-crds"} \ ${optionalString (values != {}) "-f ${valuesJsonFile}"} \
${optionalString noHooks "--no-hooks"} \ ${optionalString includeCRDs "--include-crds"} \
${chart} >$out ${optionalString noHooks "--no-hooks"} \
''; ${chart} >$out
in '';
runCommand "${name}.json" {} '' in
# Remove null values runCommand "${name}.json" { } ''
${yq}/bin/yq -Scs 'walk( # Remove null values
if type == "object" then ${yq}/bin/yq -Scs 'walk(
with_entries(select(.value != null)) if type == "object" then
elif type == "array" then with_entries(select(.value != null))
map(select(. != null)) elif type == "array" then
else map(select(. != null))
. 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 echo "fetching helm chart"
then "" helm fetch -d ./chart \
else "helm repo add repository ${repo}" ${ if untar then "--untar" else ""} \
} ${ if version == null then "" else "--version ${version}" } \
echo "fetching helm chart" ${ if devel then "--devel" else "" } \
helm fetch -d ./chart \ ${ if verify then "--verify" else "" } \
${ ${ if chartUrl == null then (
if untar if repo == null
then "--untar" then chart
else "" else "repository/${chart}"
} \ ) else chartUrl}
${ cp -r chart/*/ $out
if version == null '';
then "" outputHashMode = "recursive";
else "--version ${version}" outputHashAlgo = "sha256";
} \ outputHash = sha256;
${ nativeBuildInputs = [ kubernetes-helm cacert ];
if devel }
then "--devel"
else ""
} \
${
if verify
then "--verify"
else ""
} \
${
if chartUrl == null
then
(
if repo == null
then chart
else "repository/${chart}"
)
else chartUrl
}
cp -r chart/*/ $out
'';
outputHashMode = "recursive";
outputHashAlgo = "sha256";
outputHash = sha256;
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,42 +1,32 @@
{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 ) (types.submodule {
) (types.submodule { options = {
options = { name = mkOption ({
name = mkOption ({ 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") {
} default = default.name;
// (optionalAttrs (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 = if default == null then null else { };
default =
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,27 +44,22 @@ 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 ? {}, hash = builtins.hashString "sha1" (builtins.toJSON items);
}: let
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; })
}) items;
items; 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,30 +1,27 @@
{ { 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"
) )
documents); documents);
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:
splitted = splitString "/" apiVersion; let
in { splitted = splitString "/" apiVersion;
group = in
if length splitted == 1 {
then "core" group =
else head splitted; if length splitted == 1
version = last splitted; then "core"
}; else head splitted;
in { version = last splitted;
imports = [./k8s.nix]; };
in
{
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,42 +102,40 @@ 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
] ]
++ release.overrides); ++ release.overrides);
}) })
release.objects release.objects
) )
cfg.releases)); cfg.releases));
}; };

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";
} }

File diff suppressed because it is too large Load diff

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,12 +8,11 @@ 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) && (default.kind == null || default.kind == kind)
&& (default.kind == null || default.kind == kind)
) )
cfg.api.defaults); cfg.api.defaults);
@ -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,20 +124,21 @@ with lib; let
else -1) else -1)
lst)); lst));
compareVersions = ver1: ver2: let compareVersions = ver1: ver2:
getVersion = substring 1 10; let
splittedVer1 = builtins.splitVersion (getVersion ver1); getVersion = substring 1 10;
splittedVer2 = builtins.splitVersion (getVersion ver2); splittedVer1 = builtins.splitVersion (getVersion ver1);
splittedVer2 = builtins.splitVersion (getVersion ver2);
v1 = v1 =
if length splittedVer1 == 1 if length splittedVer1 == 1
then "${getVersion ver1}prod" then "${getVersion ver1}prod"
else getVersion ver1; else getVersion ver1;
v2 = v2 =
if length splittedVer2 == 1 if length splittedVer2 == 1
then "${getVersion ver2}prod" then "${getVersion ver2}prod"
else getVersion ver2; else getVersion ver2;
in in
builtins.compareVersions v1 v2; builtins.compareVersions v1 v2;
customResourceTypesByAttrName = zipAttrs (mapAttrsToList customResourceTypesByAttrName = zipAttrs (mapAttrsToList
@ -155,23 +147,20 @@ with lib; let
}) })
cfg.customTypes); cfg.customTypes);
customResourceTypesByAttrNameSortByVersion = customResourceTypesByAttrNameSortByVersion = mapAttrs
mapAttrs (_: resourceTypes:
( reverseList (sort
_: resourceTypes: (
reverseList (sort r1: r2:
( compareVersions r1.version r2.version > 0
r1: r2: )
compareVersions r1.version r2.version > 0 resourceTypes)
)
resourceTypes)
) )
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,73 +192,74 @@ 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} =
mkAliasDefinitions options.resources.${ct.attrName}; mkAliasDefinitions options.resources.${ct.attrName};
}) })
latestCustomResourceTypes); latestCustomResourceTypes);
coerceListOfSubmodulesToAttrs = submodule: keyFn: let coerceListOfSubmodulesToAttrs = submodule: keyFn:
mergeValuesByFn = keyFn: values: let
listToAttrs (map mergeValuesByFn = keyFn: values:
( listToAttrs (map
value: (value:
nameValuePair (toString (keyFn value)) value nameValuePair (toString (keyFn value)) value
) )
values); values);
# Either value of type `finalType` or `coercedType`, the latter is # Either value of type `finalType` or `coercedType`, the latter is
# converted to `finalType` using `coerceFunc`. # converted to `finalType` using `coerceFunc`.
coercedTo = coercedType: coerceFunc: finalType: coercedTo = coercedType: coerceFunc: finalType:
mkOptionType rec { mkOptionType rec {
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:
coerceVal = val: let
if finalType.check val coerceVal = val:
then val if finalType.check val
else let coerced = coerceFunc val; in assert finalType.check coerced; coerced; then val
in else let coerced = coerceFunc val; in assert finalType.check coerced; coerced;
finalType.merge loc (map (def: def // {value = coerceVal def.value;}) defs); in
inherit (finalType) getSubOptions; finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
inherit (finalType) getSubModules; inherit (finalType) getSubOptions;
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); inherit (finalType) getSubModules;
typeMerge = _t1: _t2: null; substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
functor = (defaultFunctor name) // {wrapped = finalType;}; typeMerge = _t1: _t2: null;
}; 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,16 +386,15 @@ 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
then true then true
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,40 +447,37 @@ 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 = mkIf (config.kubernetes.namespace != null)
metadata.namespace = (mkDefault config.kubernetes.namespace);
mkIf (config.kubernetes.namespace != null)
(mkDefault config.kubernetes.namespace);
# set project name to all resources # set project name to all resources
metadata.annotations = { metadata.annotations = {
"kubenix/project-name" = config.kubenix.project; "kubenix/project-name" = config.kubenix.project;
"kubenix/k8s-version" = cfg.version; "kubenix/k8s-version" = cfg.version;
}; };
}; };
} }];
]; }
} ]
] ++
++ # import of yaml files
# import of yaml files (map
(map (i:
(i: let let
# load yaml file # load yaml file
object = importYAML i; object = importYAML i;
groupVersion = splitString "/" object.apiVersion; groupVersion = splitString "/" object.apiVersion;
@ -504,15 +488,15 @@ 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}
) )
cfg.api.types); cfg.api.types);

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,161 +11,139 @@ 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) && (
&& ( (length submoduleDefault.tags == 0)
(length submoduleDefault.tags == 0) || (length (intersectLists submoduleDefault.tags tags)) > 0
|| (length (intersectLists submoduleDefault.tags tags)) > 0 )
) && (
&& ( (length submoduleDefault.features == 0)
(length submoduleDefault.features == 0) || (length (intersectLists submoduleDefault.features features)) > 0
|| (length (intersectLists submoduleDefault.features features)) > 0 )
)
) )
config.submodules.defaults); config.submodules.defaults);
specialArgs = 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
) )
matchingSubmodules; matchingSubmodules;
matchingModule = matchingModule =
if length versionSortedSubmodules == 0 if length versionSortedSubmodules == 0
then then
throw "No module found ${name}/${ throw "No module found ${name}/${
if version == null if version == null
then "latest" then "latest"
else version else version
}" }"
else head versionSortedSubmodules; else head versionSortedSubmodules;
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:
opts' = toList opts; let
inherit (lib.modules) evalModules; opts' = toList opts;
in inherit (lib.modules) evalModules;
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:
coerce = def: let
if isFunction def coerce = def:
then def if isFunction def
else {config = def;}; then def
modules = else { config = def; };
opts' modules = opts' ++ map
++ map (def: { (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: modules = opts';
(evalModules inherit prefix specialArgs;
{ # This is a work-around due to the fact that some sub-modules,
modules = opts'; # such as the one included in an attribute set, expects a "args"
inherit prefix specialArgs; # attribute to be given to the sub-module. As the option
# This is a work-around due to the fact that some sub-modules, # evaluation does not have any specific attribute name, we
# such as the one included in an attribute set, expects a "args" # provide a default one for the documentation.
# attribute to be given to the sub-module. As the option #
# evaluation does not have any specific attribute name, we # This is mandatory as some option declaration might use the
# provide a default one for the documentation. # "name" attribute given as argument of the submodule and use it
# # as the default of option declarations.
# This is mandatory as some option declaration might use the #
# "name" attribute given as argument of the submodule and use it # Using lookalike unicode single angle quotation marks because
# as the default of option declarations. # of the docbook transformation the options receive. In all uses
# # &gt; and &lt; wouldn't be encoded correctly so the encoded values
# Using lookalike unicode single angle quotation marks because # would be used, and use of `<` and `>` would break the XML document.
# of the docbook transformation the options receive. In all uses # It shouldn't cause an issue since this is cosmetic for the manual.
# &gt; and &lt; wouldn't be encoded correctly so the encoded values args.name = "name";
# would be used, and use of `<` and `>` would break the XML document. }).options;
# It shouldn't cause an issue since this is cosmetic for the manual.
args.name = "name";
})
.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
// { # each submodule with its location.
# Merging of submodules is done as part of mergeOptionDecls, as we have to annotate payload = [ ];
# each submodule with its location. binOp = _lhs: _rhs: [ ];
payload = []; };
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 {
@ -226,197 +197,184 @@ in {
description = "List of submodule imports"; description = "List of submodule imports";
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, evaledSubmodule' = evalModules {
... inherit specialArgs;
}: let modules = config.modules
evaledSubmodule' = evalModules { ++ [ ./base.nix ]
inherit specialArgs; ++ [{
modules =
config.modules
++ [./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 = { {
module = mkOption { options = {
description = "Module defining submodule"; module = mkOption {
type = types.unspecified; description = "Module defining submodule";
}; type = types.unspecified;
};
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 {
description = "List of features exposed by submodule"; description = "List of features exposed by submodule";
type = types.listOf types.str; type = types.listOf types.str;
}; };
definition = mkOption { definition = mkOption {
description = "Submodule definition"; description = "Submodule definition";
type = types.attrs; type = types.attrs;
}; };
exportAs = mkOption { exportAs = mkOption {
description = "Name under which to register exports"; description = "Name under which to register exports";
type = types.nullOr types.str; type = types.nullOr types.str;
default = null; default = null;
}; };
}; };
config = { config = {
definition = { definition = {
inherit (evaledSubmodule.config.submodule) name description version tags exports; inherit (evaledSubmodule.config.submodule) name description version tags exports;
}; };
inherit (evaledSubmodule.config._m) features; inherit (evaledSubmodule.config._m) features;
}; };
}) })
) )
); );
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, # submodule associated with
options, submodule = findSubmodule {
... name = config.submodule;
}: let inherit (config) version;
# submodule associated with
submodule = findSubmodule {
name = config.submodule;
inherit (config) version;
};
# definition of a submodule
submoduleDefinition = submodule.definition;
# submodule defaults
defaults = getDefaults {
inherit (submoduleDefinition) name;
inherit (submoduleDefinition) version;
inherit (submoduleDefinition) tags;
inherit (submodule) features;
};
in {
options = {
name = mkOption {
description = "Submodule instance name";
type = types.str;
default = name;
}; };
submodule = mkOption { # definition of a submodule
description = "Name of the submodule to use"; submoduleDefinition = submodule.definition;
type = types.str;
default = name;
};
version = mkOption { # submodule defaults
description = '' defaults = getDefaults {
Version of submodule to use, if version starts with "~" it is inherit (submoduleDefinition) name;
threated as regex pattern for example "~1.0.*" inherit (submoduleDefinition) version;
''; inherit (submoduleDefinition) tags;
type = types.nullOr types.str; inherit (submodule) features;
default = null;
}; };
in
{
options = {
name = mkOption {
description = "Submodule instance name";
type = types.str;
default = name;
};
passthru.enable = mkOption { submodule = mkOption {
description = "Whether to passthru submodule resources"; description = "Name of the submodule to use";
type = types.bool; type = types.str;
default = true; default = name;
}; };
config = mkOption { version = mkOption {
description = "Submodule instance ${config.name} for ${submoduleDefinition.name}:${submoduleDefinition.version} config"; description = ''
type = Version of submodule to use, if version starts with "~" it is
submoduleWithSpecialArgs threated as regex pattern for example "~1.0.*"
({...}: { '';
imports = submodule.modules ++ defaults ++ [./base.nix]; type = types.nullOr types.str;
_module.args.pkgs = pkgs; default = null;
_module.args.name = config.name; };
_module.args.submodule = config;
submodule.args = mkAliasDefinitions options.args;
})
specialArgs;
default = {};
};
args = mkOption { passthru.enable = mkOption {
description = "Submodule arguments (alias of config.submodule.args)"; description = "Whether to passthru submodule resources";
type = types.bool;
default = true;
};
config = mkOption {
description = "Submodule instance ${config.name} for ${submoduleDefinition.name}:${submoduleDefinition.version} config";
type = submoduleWithSpecialArgs
({ ... }: {
imports = submodule.modules ++ defaults ++ [ ./base.nix ];
_module.args.pkgs = pkgs;
_module.args.name = config.name;
_module.args.submodule = config;
submodule.args = mkAliasDefinitions options.args;
})
specialArgs;
default = { };
};
args = mkOption {
description = "Submodule arguments (alias of config.submodule.args)";
};
}; };
}; }));
}));
}; };
default = {}; default = { };
}; };
config = mkMerge ([ config = mkMerge ([
{ {
# register exported functions as args # register exported functions as args
_module.args = mkMerge (map _module.args = mkMerge (map
(submodule: { (submodule: {
${submodule.exportAs} = submodule.definition.exports; ${submodule.exportAs} = submodule.definition.exports;
})
(filter (submodule: submodule.exportAs != null) cfg.imports));
_m.features = [ "submodules" ];
submodules.specialArgs.kubenix = kubenix;
# passthru kubenix.project to submodules
submodules.defaults = mkMerge [
[{
default = {
kubenix.project = parentConfig.kubenix.project;
};
}]
(map
(propagate: {
inherit (propagate) features;
default = propagate.module;
}) })
(filter (submodule: submodule.exportAs != null) cfg.imports)); config._m.propagate)
];
}
_m.features = ["submodules"]; (mkIf cfg.propagate.enable {
# if propagate is enabled and submodule has submodules included propagage defaults and imports
submodules.specialArgs.kubenix = kubenix; submodules.defaults = [{
features = [ "submodules" ];
# passthru kubenix.project to submodules default = {
submodules.defaults = mkMerge [ submodules = {
[ inherit (cfg) defaults;
{ inherit (cfg) imports;
default = { };
kubenix.project = parentConfig.kubenix.project; };
}; }];
} })
] ]
++ passthruConfig);
(map
(propagate: {
inherit (propagate) features;
default = propagate.module;
})
config._m.propagate)
];
}
(mkIf cfg.propagate.enable {
# if propagate is enabled and submodule has submodules included propagage defaults and imports
submodules.defaults = [
{
features = ["submodules"];
default = {
submodules = {
inherit (cfg) defaults;
inherit (cfg) imports;
};
};
}
];
})
]
++ 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
[ kubetest
pytest kubernetes
kubetest ] ++ cfg.extraPackages);
kubernetes
]
++ cfg.extraPackages);
toTestScript = t: toTestScript = t:
if isString t.script if isString t.script
@ -28,26 +20,29 @@ with lib; let
'' ''
else t.script; else t.script;
tests = let tests =
# make sure tests are prefixed so that alphanumerical let
# sorting reproduces them in the same order as they # make sure tests are prefixed so that alphanumerical
# have been declared in the list. # sorting reproduces them in the same order as they
seive = t: t.script != null && t.enabled; # have been declared in the list.
allEligibleTests = filter seive testing.tests; seive = t: t.script != null && t.enabled;
listLengthPadding = builtins.length ( allEligibleTests = filter seive testing.tests;
lib.stringToCharacters ( listLengthPadding = builtins.length (
builtins.toString ( lib.stringToCharacters (
builtins.length allEligibleTests builtins.toString (
builtins.length allEligibleTests
)
) )
) );
); op = i: t: {
op = i: t: { path = toTestScript t;
path = toTestScript t; name =
name = let 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" (
lib.imap0 op allEligibleTests lib.imap0 op allEligibleTests
); );
@ -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,13 +35,11 @@ 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:
( (intersectLists d.features testFeatures)
d: == d.features
(intersectLists d.features testFeatures) || (length d.features) == 0
== d.features
|| (length d.features) == 0
) )
testing.common; testing.common;
@ -59,17 +47,19 @@ with lib; let
modulesWithCommonOptions = modules ++ (map (d: d.options) commonOpts); modulesWithCommonOptions = modules ++ (map (d: d.options) commonOpts);
# evaled test # evaled test
evaled = let evaled =
evaled' = kubenix.evalModules { let
modules = modulesWithCommonOptions; evaled' = kubenix.evalModules {
}; modules = modulesWithCommonOptions;
in };
in
if testing.doThrowError if testing.doThrowError
then evaled' then evaled'
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,26 +24,22 @@ 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"; addIf = true;
addIf = true; ipMasq = true;
ipMasq = true; isGateway = true;
isGateway = true; ipam = {
ipam = { 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,16 +63,18 @@ 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) system; inherit pkgs;
}; inherit (pkgs) system;
};
mkKubernetesSingleNodeTest { mkKubernetesSingleNodeTest {
inherit extraConfiguration; inherit extraConfiguration;
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
@ -48,10 +47,10 @@ stdenv.mkDerivation rec {
''; '';
WHAT = lib.concatStringsSep " " ([ WHAT = lib.concatStringsSep " " ([
"cmd/kubeadm" "cmd/kubeadm"
"cmd/kubectl" "cmd/kubectl"
] ]
++ components); ++ components);
postBuild = '' postBuild = ''
./hack/update-generated-docs.sh ./hack/update-generated-docs.sh
@ -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,45 +1,36 @@
{ { 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:
short = builtins.concatStringsSep "." (lib.lists.sublist 0 2 (builtins.splitVersion version)); let
in { short = builtins.concatStringsSep "." (lib.lists.sublist 0 2 (builtins.splitVersion version));
name = "v${short}.nix"; in
path = generateK8S "v${short}" (builtins.fetchurl { {
url = "https://github.com/kubernetes/kubernetes/raw/v${version}/api/openapi-spec/swagger.json"; name = "v${short}.nix";
sha256 = sha; path = generateK8S "v${short}" (builtins.fetchurl {
}); url = "https://github.com/kubernetes/kubernetes/raw/v${version}/api/openapi-spec/swagger.json";
} sha256 = sha;
) });
(import ../../versions.nix).full }
)
(import ../../versions.nix).full
) )
); );
} }

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,157 +78,152 @@ 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:
( # if $ref is in definition it means it's an alias of other definition
_name: definition: if hasAttr "$ref" definition
# if $ref is in definition it means it's an alias of other definition then definitions."${refDefinition definition}"
if hasAttr "$ref" definition else if !(hasAttr "properties" definition)
then definitions."${refDefinition definition}" then {
else if !(hasAttr "properties" definition) type = mapType definition;
then { }
type = mapType definition; else {
} options = mapAttrs
else { (propName: property:
options = let
mapAttrs isRequired = elem propName (definition.required or [ ]);
( requiredOrNot = type:
propName: property: let if isRequired
isRequired = elem propName (definition.required or []); then type
requiredOrNot = type: else types.nullOr type;
if isRequired optionProperties =
then type # if $ref is in property it references other definition,
else types.nullOr type; # but if other definition does not have properties, then just take it's type
optionProperties = if hasAttr "$ref" property
# if $ref is in property it references other definition, then
# but if other definition does not have properties, then just take it's type if hasTypeMapping swagger.definitions.${refDefinition property}
if hasAttr "$ref" property then {
then type = requiredOrNot (mapType swagger.definitions.${refDefinition property});
if hasTypeMapping swagger.definitions.${refDefinition property} }
then { else {
type = requiredOrNot (mapType swagger.definitions.${refDefinition property}); type = requiredOrNot (submoduleOf definitions (refDefinition property));
} }
else { else if !(hasAttr "type" property)
type = requiredOrNot (submoduleOf definitions (refDefinition property)); then {
} type = types.unspecified;
else if !(hasAttr "type" property) }
# if property has an array type
else if property.type == "array"
then
# if reference is in items it can reference other type of another
# definition
if hasAttr "$ref" property.items
then
# if it is a reference to simple type
if hasTypeMapping swagger.definitions.${refDefinition property.items}
then { then {
type = types.unspecified; type = requiredOrNot (types.listOf (mapType swagger.definitions.${refDefinition property.items}.type));
} }
# if property has an array type # if a reference is to complex type
else if property.type == "array" else
then # if x-kubernetes-patch-merge-key is set then make it an
# if reference is in items it can reference other type of another # attribute set of submodules
# definition if hasAttr "x-kubernetes-patch-merge-key" property
if hasAttr "$ref" property.items
then then
# if it is a reference to simple type let
if hasTypeMapping swagger.definitions.${refDefinition property.items} mergeKey = property."x-kubernetes-patch-merge-key";
then { in
type = requiredOrNot (types.listOf (mapType swagger.definitions.${refDefinition property.items}.type)); {
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey);
apply = attrsToList;
} }
# if a reference is to complex type # in other case it's a simple list
else
# if x-kubernetes-patch-merge-key is set then make it an
# attribute set of submodules
if hasAttr "x-kubernetes-patch-merge-key" property
then let
mergeKey = property."x-kubernetes-patch-merge-key";
in {
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey);
apply = attrsToList;
}
# in other case it's a simple list
else {
type = requiredOrNot (types.listOf (submoduleOf definitions (refDefinition property.items)));
}
# in other case it only references a simple type
else { else {
type = requiredOrNot (types.listOf (mapType property.items)); type = requiredOrNot (types.listOf (submoduleOf definitions (refDefinition property.items)));
} }
else if property.type == "object" && hasAttr "additionalProperties" property # in other case it only references a simple type
then else {
# if it is a reference to simple type type = requiredOrNot (types.listOf (mapType property.items));
if }
( else if property.type == "object" && hasAttr "additionalProperties" property
hasAttr "$ref" property.additionalProperties then
&& hasTypeMapping swagger.definitions.${refDefinition property.additionalProperties} # if it is a reference to simple type
) if
then { (
type = requiredOrNot (types.attrsOf (mapType swagger.definitions.${refDefinition property.additionalProperties})); hasAttr "$ref" property.additionalProperties
} && hasTypeMapping swagger.definitions.${refDefinition property.additionalProperties}
else if hasAttr "$ref" property.additionalProperties )
then { then {
type = requiredOrNot types.attrs; type = requiredOrNot (types.attrsOf (mapType swagger.definitions.${refDefinition property.additionalProperties}));
} }
# if is an array else if hasAttr "$ref" property.additionalProperties
else if property.additionalProperties.type == "array" then {
then { type = requiredOrNot types.attrs;
type = requiredOrNot (types.loaOf (mapType property.additionalProperties.items)); }
} # if is an array
else { else if property.additionalProperties.type == "array"
type = requiredOrNot (types.attrsOf (mapType property.additionalProperties)); then {
} type = requiredOrNot (types.loaOf (mapType property.additionalProperties.items));
# just a simple property }
else { else {
type = requiredOrNot (mapType property); type = requiredOrNot (types.attrsOf (mapType property.additionalProperties));
}; }
in # just a simple property
mkOption ({ else {
description = property.description or ""; type = requiredOrNot (mapType property);
} };
// optionProperties) in
mkOption ({
description = property.description or "";
}
// optionProperties)
)
definition.properties;
config =
let
optionalProps = filterAttrs
(propName: _property:
!(elem propName (definition.required or [ ]))
) )
definition.properties; definition.properties;
config = let in
optionalProps = mapAttrs (_name: _property: mkOverride 1002 null) optionalProps;
filterAttrs }
( )
propName: _property: swagger.definitions);
!(elem propName (definition.required or []))
)
definition.properties;
in
mapAttrs (_name: _property: mkOverride 1002 null) optionalProps;
}
)
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"; kind = removeSuffix "Spec" (last splittedType);
kind = removeSuffix "Spec" (last splittedType); version = last (take ((length splittedType) - 1) splittedType);
version = last (take ((length splittedType) - 1) splittedType); ref = removePrefix "#/definitions/" property."$ref";
ref = removePrefix "#/definitions/" property."$ref"; })
}) (filterAttrs
(filterAttrs (_name: property:
( (hasPrefix "me.snowdrop.istio.api" property.javaType)
_name: property: && hasSuffix "Spec" property.javaType
(hasPrefix "me.snowdrop.istio.api" property.javaType) )
&& hasSuffix "Spec" property.javaType swagger.properties))
) ++ (mapAttrsToList
swagger.properties)) (_name: property: rec {
++ (mapAttrsToList splittedType = splitString "." (removePrefix "me.snowdrop.istio.mixer." property.javaType);
(_name: property: rec { group = "config.istio.io";
splittedType = splitString "." (removePrefix "me.snowdrop.istio.mixer." property.javaType); version = "v1alpha2";
group = "config.istio.io"; kind = head (tail splittedType);
version = "v1alpha2"; ref = removePrefix "#/definitions/" property."$ref";
kind = head (tail splittedType); })
ref = removePrefix "#/definitions/" property."$ref"; (filterAttrs
}) (_name: property:
(filterAttrs (hasPrefix "me.snowdrop.istio.mixer" property.javaType)
( && hasSuffix "Spec" property.javaType
_name: property: )
(hasPrefix "me.snowdrop.istio.mixer" property.javaType) swagger.properties));
&& hasSuffix "Spec" property.javaType
)
swagger.properties));
swagger = fetchSpecs spec; swagger = fetchSpecs spec;
@ -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,43 +75,42 @@ 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:
getVersion = substring 1 10; let
splitVersion = v: builtins.splitVersion (getVersion v); getVersion = substring 1 10;
isAlpha = v: elem "alpha" (splitVersion v); splitVersion = v: builtins.splitVersion (getVersion v);
patchVersion = v: isAlpha = v: elem "alpha" (splitVersion v);
if isAlpha v patchVersion = v:
then "" if isAlpha v
else if length (splitVersion v) == 1 then ""
then "${getVersion v}prod" else if length (splitVersion v) == 1
else getVersion v; then "${getVersion v}prod"
else getVersion v;
v1 = patchVersion ver1; v1 = patchVersion ver1;
v2 = patchVersion ver2; v2 = patchVersion ver2;
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 # if $ref is in definition it means it's an alias of other definition
( if hasAttr "$ref" definition
_name: definition: then definitions."${refDefinition definition}"
# if $ref is in definition it means it's an alias of other definition else if !(hasAttr "properties" definition)
if hasAttr "$ref" definition then { }
then definitions."${refDefinition definition}" # in other case it's an actual definition
else if !(hasAttr "properties" definition) else {
then {} options =
# in other case it's an actual definition mapAttrs
else { (
options = propName: property:
mapAttrs let
( isRequired = elem propName (definition.required or [ ]);
propName: property: let
isRequired = elem propName (definition.required or []);
requiredOrNot = type: requiredOrNot = type:
if isRequired if isRequired
then type then type
@ -146,43 +133,47 @@ with lib; let
# if property has an array type # if property has an array type
else if property.type == "array" else if property.type == "array"
then then
# if reference is in items it can reference other type of another # if reference is in items it can reference other type of another
# definition # definition
if hasAttr "$ref" property.items if hasAttr "$ref" property.items
then then
# if it is a reference to simple type # if it is a reference to simple type
if hasTypeMapping swagger.definitions.${refDefinition property.items} if hasTypeMapping swagger.definitions.${refDefinition property.items}
then { then {
type = requiredOrNot (types.listOf (mapType swagger.definitions.${refDefinition property.items}.type)); type = requiredOrNot (types.listOf (mapType swagger.definitions.${refDefinition property.items}.type));
} }
# if a reference is to complex type # if a reference is to complex type
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
mergeKey = property."x-kubernetes-patch-merge-key"; let
in { mergeKey = property."x-kubernetes-patch-merge-key";
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey []); in
apply = attrsToList; {
} type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey [ ]);
apply = attrsToList;
}
# in other case it's a simple list # in other case it's a simple list
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 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
mergeKey = "name"; let
in { mergeKey = "name";
type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey ( in
if hasAttr "x-kubernetes-list-map-keys" property {
then property."x-kubernetes-list-map-keys" type = requiredOrNot (coerceAttrsOfSubmodulesToListByKey (refDefinition property.items) mergeKey (
else [] if hasAttr "x-kubernetes-list-map-keys" property
)); then property."x-kubernetes-list-map-keys"
apply = attrsToList; else [ ]
} ));
apply = attrsToList;
}
else { else {
type = type =
if (refDefinition property.items) == _name if (refDefinition property.items) == _name
@ -195,7 +186,7 @@ with lib; let
} }
else if property.type == "object" && hasAttr "additionalProperties" property else if property.type == "object" && hasAttr "additionalProperties" property
then then
# if it is a reference to simple type # if it is a reference to simple type
if if
( (
hasAttr "$ref" property.additionalProperties hasAttr "$ref" property.additionalProperties
@ -221,63 +212,60 @@ with lib; let
type = requiredOrNot (mapType property); type = requiredOrNot (mapType property);
}; };
in in
mkOption ({ mkOption ({
description = property.description or ""; description = property.description or "";
} }
// 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
mapAttrs (_name: _property: mkOverride 1002 null) optionalProps; mapAttrs (_name: _property: mkOverride 1002 null) optionalProps;
} }
) )
swagger.definitions; swagger.definitions;
mapCharPairs = f: s1: s2: mapCharPairs = f: s1: s2: concatStrings (imap0
concatStrings (imap0 (i: c1:
( f i c1 (
i: c1: if i >= stringLength s2
f i c1 ( then ""
if i >= stringLength s2 else elemAt (stringToCharacters s2) i
then ""
else elemAt (stringToCharacters s2) i
)
) )
(stringToCharacters s1)); )
(stringToCharacters s1));
getAttrName = resource: kind: getAttrName = resource: kind: mapCharPairs
mapCharPairs (i: c1: c2:
( if hasPrefix "API" kind && i == 0
i: c1: c2: then "A"
if hasPrefix "API" kind && i == 0 else if i == 0
then "A" then c1
else if i == 0 else if c2 == "" || (toLower c2) != c1
then c1 then c1
else if c2 == "" || (toLower c2) != c1 else c2
then c1
else c2
) )
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;
kind' = path.post."x-kubernetes-group-version-kind".kind; kind' = path.post."x-kubernetes-group-version-kind".kind;
name' = last (splitString "/" name); name' = last (splitString "/" name);
attrName = getAttrName name' kind'; attrName = getAttrName name' kind';
in in
nameValuePair ref { nameValuePair ref {
inherit ref attrName; inherit ref attrName;
@ -309,37 +297,34 @@ with lib; let
}) })
resourceTypes); resourceTypes);
resourcesTypesByKindSortByVersion = resourcesTypesByKindSortByVersion = mapAttrs
mapAttrs (_kind: resourceTypes:
( reverseList (sort
_kind: resourceTypes: (
reverseList (sort r1: r2:
( compareVersions r1.version r2.version > 0
r1: r2: )
compareVersions r1.version r2.version > 0 resourceTypes)
)
resourceTypes)
) )
resourceTypesByKind; resourceTypesByKind;
latestResourceTypesByKind = latestResourceTypesByKind = mapAttrs (_kind: last) resourcesTypesByKindSortByVersion;
mapAttrs (_kind: last) resourcesTypesByKindSortByVersion;
genResourceOptions = resource: genResourceOptions = resource:
with gen; let with gen; let
submoduleForDefinition' = definition: submoduleForDefinition' = definition:
submoduleForDefinition submoduleForDefinition
definition.ref definition.ref
definition.name definition.name
definition.kind definition.kind
definition.group definition.group
definition.version; definition.version;
in in
mkOption { mkOption {
inherit (resource) description; inherit (resource) description;
type = types.attrsOf (submoduleForDefinition' resource); type = types.attrsOf (submoduleForDefinition' resource);
default = {}; default = { };
}; };
generated = '' generated = ''
# This file was generated with kubenix k8s generator, do not edit # This file was generated with kubenix k8s generator, do not edit
@ -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,99 +1,95 @@
{ { 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 "";
# kubectl does some parsing which removes the -I flag so # kubectl does some parsing which removes the -I flag so
# as workaround, we write to a script and call that # as workaround, we write to a script and call that
# https://github.com/kubernetes/kubernetes/pull/108199#issuecomment-1058405404 # https://github.com/kubernetes/kubernetes/pull/108199#issuecomment-1058405404
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
# write the manifests for use with `nix build` # write the manifests for use with `nix build`
ln -s $result $out/manifest.json ln -s $result $out/manifest.json
# create a script for `nix run` # create a script for `nix run`
cat <<EOF> $out/bin/kubenix cat <<EOF> $out/bin/kubenix
set -uo pipefail set -uo pipefail
export KUBECONFIG=$kubeconfig export KUBECONFIG=$kubeconfig
export KUBECTL_EXTERNAL_DIFF=$diff export KUBECTL_EXTERNAL_DIFF=$diff
function _help() { function _help() {
echo " echo "
kubenix - Kubernetes management with Nix kubenix - Kubernetes management with Nix
commands: commands:
"" - run diff, prompt for confirmation, then apply "" - run diff, prompt for confirmation, then apply
apply - create resources in target cluster apply - create resources in target cluster
diff - show a diff between configured and live resources diff - show a diff between configured and live resources
render - print resource manifests to stdout render - print resource manifests to stdout
options: options:
-h --help - show this menu -h --help - show this menu
" "
} }
function _kubectl() { function _kubectl() {
${vals}/bin/vals eval -fail-on-missing-key-in-map < $result | ${kubectl}/bin/kubectl \$@ ${vals}/bin/vals eval -fail-on-missing-key-in-map < $result | ${kubectl}/bin/kubectl \$@
} }
# if no args given, add empty string # if no args given, add empty string
[ \$# -eq 0 ] && set -- "" [ \$# -eq 0 ] && set -- ""
# parse arguments # parse arguments
while test \$# -gt 0; do while test \$# -gt 0; do
case "\$1" in case "\$1" in
-h|--help) -h|--help)
_help _help
exit 0;; exit 0;;
"") "")
_kubectl diff -f - --prune _kubectl diff -f - --prune
if [[ "\$?" -eq 1 ]]; then if [[ "\$?" -eq 1 ]]; then
read -p 'apply? [y/N]: ' response read -p 'apply? [y/N]: ' response
[[ \$response == "y" ]] && _kubectl apply -f - --prune --all [[ \$response == "y" ]] && _kubectl apply -f - --prune --all
fi fi
shift;; shift;;
render) render)
${vals}/bin/vals eval < $result ${vals}/bin/vals eval < $result
shift;; shift;;
apply|diff) apply|diff)
_kubectl \$@ -f - --prune _kubectl \$@ -f - --prune
shift;; shift;;
*) *)
_kubectl \$@ _kubectl \$@
shift;; shift;;
esac esac
done done
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,44 +1,40 @@
{ { 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 =
nginxPort = "80"; let
nginxConf = pkgs.writeText "nginx.conf" '' nginxPort = "80";
user nginx nginx; nginxConf = pkgs.writeText "nginx.conf" ''
daemon off; user nginx nginx;
error_log /dev/stdout info; daemon off;
pid /dev/null; error_log /dev/stdout info;
events {} pid /dev/null;
http { events {}
access_log /dev/stdout; http {
server { access_log /dev/stdout;
listen ${nginxPort}; server {
index index.html; listen ${nginxPort};
location / { index index.html;
root ${nginxWebRoot}; location / {
root ${nginxWebRoot};
}
} }
} }
} '';
''; nginxWebRoot = pkgs.writeTextDir "index.html" ''
nginxWebRoot = pkgs.writeTextDir "index.html" '' <html><body><h1>Hello from NGINX</h1></body></html>
<html><body><h1>Hello from NGINX</h1></body></html> '';
''; in
in
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,41 +19,33 @@ 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 = {
name = "test-submodule"; name = "test-submodule";
passthru = { passthru = {
kubernetes.objects = config.kubernetes.objects; kubernetes.objects = config.kubernetes.objects;
docker.images = config.docker.images; docker.images = config.docker.images;
};
}; };
kubernetes.resources.pods.nginx = {
metadata.name = name;
spec.containers.nginx.image = config.docker.images.nginx.path;
};
docker.images.nginx.image = images.nginx;
}; };
};
}
];
kubernetes.api.defaults = [ kubernetes.resources.pods.nginx = {
{ metadata.name = name;
propagate = true; spec.containers.nginx.image = config.docker.images.nginx.path;
default.metadata.labels.my-label = "my-value"; };
}
]; docker.images.nginx.image = images.nginx;
};
};
}];
kubernetes.api.defaults = [{
propagate = true;
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);
} }