feat(legacy): refactor legacy kubenix support

This commit is contained in:
Jaka Hudoklin 2019-10-06 21:39:10 +02:00
parent a8dcc69d54
commit 9f05cd56b1
No known key found for this signature in database
GPG key ID: D1F18234B07BD6E2
8 changed files with 139 additions and 98 deletions

View file

@ -40,4 +40,49 @@ rec {
i = acc.i + 1; i = acc.i + 1;
value = acc.value + (toInt char) * (exp 8 acc.i); value = acc.value + (toInt char) * (exp 8 acc.i);
}) {i = 0; value = 0;} (stringToCharacters value)).value; }) {i = 0; value = 0;} (stringToCharacters value)).value;
submoduleWithSpecialArgs = opts: specialArgs:
let
opts' = toList opts;
inherit (lib.modules) evalModules;
in
mkOptionType rec {
name = "submodule";
check = x: isAttrs x || isFunction x;
merge = loc: defs:
let
coerce = def: if isFunction def then def else { config = def; };
modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs;
in (evalModules {
inherit modules specialArgs;
prefix = loc;
}).config;
getSubOptions = prefix: (evalModules
{ modules = opts'; inherit prefix specialArgs;
# This is a work-around due to the fact that some sub-modules,
# such as the one included in an attribute set, expects a "args"
# attribute to be given to the sub-module. As the option
# evaluation does not have any specific attribute name, we
# provide a default one for the documentation.
#
# This is mandatory as some option declaration might use the
# "name" attribute given as argument of the submodule and use it
# as the default of option declarations.
#
# Using lookalike unicode single angle quotation marks because
# of the docbook transformation the options receive. In all uses
# > and < wouldn't be encoded correctly so the encoded values
# would be used, and use of `<` and `>` would break the XML document.
# It shouldn't cause an issue since this is cosmetic for the manual.
args.name = "name";
}).options;
getSubModules = opts';
substSubModules = m: submoduleWithSpecialArgs m specialArgs;
functor = (defaultFunctor name) // {
# Merging of submodules is done as part of mergeOptionDecls, as we have to annotate
# each submodule with its location.
payload = [];
binOp = lhs: rhs: [];
};
};
} }

View file

@ -1,50 +0,0 @@
{ lib }:
with lib;
rec {
submoduleWithSpecialArgs = opts: specialArgs:
let
opts' = toList opts;
inherit (lib.modules) evalModules;
in
mkOptionType rec {
name = "submodule";
check = x: isAttrs x || isFunction x;
merge = loc: defs:
let
coerce = def: if isFunction def then def else { config = def; };
modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs;
in (evalModules {
inherit modules specialArgs;
prefix = loc;
}).config;
getSubOptions = prefix: (evalModules
{ modules = opts'; inherit prefix specialArgs;
# This is a work-around due to the fact that some sub-modules,
# such as the one included in an attribute set, expects a "args"
# attribute to be given to the sub-module. As the option
# evaluation does not have any specific attribute name, we
# provide a default one for the documentation.
#
# This is mandatory as some option declaration might use the
# "name" attribute given as argument of the submodule and use it
# as the default of option declarations.
#
# Using lookalike unicode single angle quotation marks because
# of the docbook transformation the options receive. In all uses
# &gt; and &lt; wouldn't be encoded correctly so the encoded values
# would be used, and use of `<` and `>` would break the XML document.
# It shouldn't cause an issue since this is cosmetic for the manual.
args.name = "name";
}).options;
getSubModules = opts';
substSubModules = m: submoduleWithSpecialArgs m specialArgs;
functor = (defaultFunctor name) // {
# Merging of submodules is done as part of mergeOptionDecls, as we have to annotate
# each submodule with its location.
payload = [];
binOp = lhs: rhs: [];
};
};
}

View file

@ -8,7 +8,5 @@
testing = ./testing.nix; testing = ./testing.nix;
test = ./test.nix; test = ./test.nix;
module = ./module.nix; module = ./module.nix;
v1 = { legacy = ./legacy.nix;
modules = ./v1/modules.nix;
};
} }

View file

@ -1,7 +1,8 @@
{ config, pkgs, lib, ... }: # support for legacy kubenix
{ options, config, pkgs, lib, kubenix, ... }:
with lib; with lib;
with import ../../lib/modules.nix { inherit lib; };
let let
parentModule = module; parentModule = module;
@ -30,8 +31,8 @@ let
_module.args.name = module.name; _module.args.name = module.name;
_module.args.module = module; _module.args.module = module;
} }
../k8s.nix ./k8s.nix
./modules.nix ./legacy.nix
(injectModuleAttrs moduleDefinition.module {_file = file;}) (injectModuleAttrs moduleDefinition.module {_file = file;})
{ {
config.kubernetes.api.defaults = [{ config.kubernetes.api.defaults = [{
@ -42,6 +43,7 @@ let
++ (optionals (hasAttr moduleDefinition.name config.kubernetes.defaultModuleConfiguration) ++ (optionals (hasAttr moduleDefinition.name config.kubernetes.defaultModuleConfiguration)
config.kubernetes.defaultModuleConfiguration.${moduleDefinition.name}); config.kubernetes.defaultModuleConfiguration.${moduleDefinition.name});
# prefix kubernetes objects with ${serviceName}, this magic was removed in new kubenix
prefixResources = resources: serviceName: map (resource: resource // { prefixResources = resources: serviceName: map (resource: resource // {
metadata = resource.metadata // { metadata = resource.metadata // {
name = "${serviceName}-${resource.metadata.name}"; name = "${serviceName}-${resource.metadata.name}";
@ -60,10 +62,10 @@ let
else throw ''requested kubernetes moduleDefinition with name "${name}" does not exist''; else throw ''requested kubernetes moduleDefinition with name "${name}" does not exist'';
in { in {
imports = [ ../k8s.nix ]; imports = [ ./k8s.nix ];
options.kubernetes.moduleDefinitions = mkOption { options.kubernetes.moduleDefinitions = mkOption {
description = "Attribute set of module definitions"; description = "Legacy kubenix attribute set of module definitions";
default = {}; default = {};
type = types.attrsOf (types.submodule ({name, ...}: { type = types.attrsOf (types.submodule ({name, ...}: {
options = { options = {
@ -93,7 +95,7 @@ in {
}; };
options.kubernetes.defaultModuleConfiguration = mkOption { options.kubernetes.defaultModuleConfiguration = mkOption {
description = "Module default options"; description = "Legacy kubenix module default options";
type = types.submodule { type = types.submodule {
options = defaultModuleConfigurationOptions // { options = defaultModuleConfigurationOptions // {
all = mkOption { all = mkOption {
@ -107,7 +109,7 @@ in {
}; };
options.kubernetes.modules = mkOption { options.kubernetes.modules = mkOption {
description = "Attribute set of modules"; description = "Legacy kubenix attribute set of modules";
default = {}; default = {};
type = types.attrsOf (types.submodule ({config, name, ...}: { type = types.attrsOf (types.submodule ({config, name, ...}: {
options = { options = {
@ -134,8 +136,10 @@ in {
configuration = mkOption { configuration = mkOption {
description = "Module configuration"; description = "Module configuration";
type = types.submodule { type = submoduleWithSpecialArgs {
imports = mkModuleOptions (getModuleDefinition config.module) config; imports = mkModuleOptions (getModuleDefinition config.module) config;
} {
inherit kubenix;
}; };
default = {}; default = {};
}; };
@ -149,7 +153,24 @@ in {
})); }));
}; };
options.kubernetes.defaults = mkOption {
type = types.attrsOf (types.coercedTo types.unspecified (value: [value]) (types.listOf types.unspecified));
description = "Legacy kubenix kubernetes defaults.";
default = {};
};
# for back compatibility with kubernetes.customResources
options.kubernetes.customResources = options.kubernetes.resources;
config = { config = {
kubernetes.api.defaults = mapAttrsToList (attrName: default: let
type = head (filter (type: type.attrName == attrName) config.kubernetes.api.types);
in {
default = { imports = default; };
} // (if (attrName == "all") then {} else {
resource = type.name;
})) config.kubernetes.defaults;
kubernetes.objects = mkMerge ( kubernetes.objects = mkMerge (
mapAttrsToList (name: module: let mapAttrsToList (name: module: let
moduleDefinition = getModuleDefinition module.module; moduleDefinition = getModuleDefinition module.module;
@ -160,6 +181,9 @@ in {
) config.kubernetes.modules ) config.kubernetes.modules
); );
# custom resources are now included in normal resources, so just make an alias
kubernetes.customResources = mkAliasDefinitions options.kubernetes.resources;
kubernetes.defaultModuleConfiguration.all = { kubernetes.defaultModuleConfiguration.all = {
_file = head options.kubernetes.defaultModuleConfiguration.files; _file = head options.kubernetes.defaultModuleConfiguration.files;
config.kubernetes.version = mkDefault config.kubernetes.version; config.kubernetes.version = mkDefault config.kubernetes.version;

View file

@ -1,7 +1,6 @@
{ config, kubenix, pkgs, lib, ... }: { config, options, kubenix, pkgs, lib, ... }:
with lib; with lib;
with import ../lib/modules.nix { inherit lib; };
let let
cfg = config.submodules; cfg = config.submodules;

View file

@ -30,7 +30,7 @@ let
./k8s/order.nix ./k8s/order.nix
./k8s/submodule.nix ./k8s/submodule.nix
./k8s/imports.nix ./k8s/imports.nix
./k8s/v1/modules.nix ./k8s/legacy.nix
./helm/simple.nix ./helm/simple.nix
./istio/bookinfo.nix ./istio/bookinfo.nix
./submodules/simple.nix ./submodules/simple.nix

58
tests/k8s/legacy.nix Normal file
View file

@ -0,0 +1,58 @@
{ config, lib, kubenix, pkgs, k8sVersion, ... }:
with lib;
{
imports = with kubenix.modules; [ test k8s legacy ];
test = {
name = "k8s-legacy";
description = "Simple test tesing kubenix legacy support";
assertions = [];
};
kubernetes.version = k8sVersion;
kubernetes.moduleDefinitions.app.module = { config, k8s, ... }: {
options = {
replicas = mkOption {
description = "Number of replicas to run";
type = types.int;
default = 2;
};
};
config.kubernetes.defaults = {
all = [{
metadata.labels.default = "value";
}];
deployments = [{
metadata.labels.default2 = "value2";
} {
metadata.labels.default3 = "value3";
}];
};
config.kubernetes.resources.deployments.app = {
spec = {
replicas = config.replicas;
selector = {
matchLabels.app = "app";
};
template.spec = {
containers.app = {
image = "hello-world";
};
};
};
};
};
kubernetes.modules.myapp = {
module = "app";
namespace = "test";
configuration.replicas = 3;
};
}

View file

@ -1,33 +0,0 @@
{ config, lib, kubenix, pkgs, k8sVersion, ... }: {
imports = with kubenix.modules; [ test k8s v1.modules ];
test = {
name = "k8s-v1-modules";
description = "Simple test tesing CRD";
assertions = [];
};
kubernetes.version = k8sVersion;
kubernetes.moduleDefinitions.app.module = { config, k8s, ... }: {
kubernetes.resources.deployments.app = {
spec = {
replicas = 2;
selector = {
matchLabels.app = "app";
};
template.spec = {
containers.app = {
image = "hello-world";
};
};
};
};
};
kubernetes.modules.myapp = {
module = "app";
namespace = "test";
};
}