mirror of
https://github.com/TECHNOFAB11/kubenix.git
synced 2025-12-12 16:10:05 +01:00
first commit
This commit is contained in:
commit
cbf84e25a5
22 changed files with 131008 additions and 0 deletions
11
.editorconfig
Normal file
11
.editorconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* text=auto
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
result
|
||||||
6
.travis.yml
Normal file
6
.travis.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- v5
|
||||||
|
- v4
|
||||||
|
- '0.12'
|
||||||
|
- '0.10'
|
||||||
20
README.md
Normal file
20
README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# KubeNix
|
||||||
|
|
||||||
|
> Kubernetes resource builder written in nix
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
KubeNix is a kubernetes resource builder, that uses nix module system for
|
||||||
|
definition of kubernetes resources and nix build system for building complex
|
||||||
|
kubernetes resources very easyly.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Loading and override of kubernetes json and yaml files
|
||||||
|
- Support for complex merging of kubernetes resource definitions
|
||||||
|
- No more helm stupid yaml templating, nix is a way better templating language
|
||||||
|
- Support for all kubernetes versions
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [Jaka Hudoklin](https://x-truder.net)
|
||||||
89
default.nix
Normal file
89
default.nix
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
pkgs ? import <nixpkgs> {}
|
||||||
|
}:
|
||||||
|
|
||||||
|
with pkgs.lib;
|
||||||
|
with import ./lib.nix { inherit pkgs; inherit (pkgs) lib; };
|
||||||
|
|
||||||
|
let
|
||||||
|
evalKubernetesModules = configuration: evalModules {
|
||||||
|
modules = [./kubernetes.nix ./modules.nix configuration];
|
||||||
|
args = {
|
||||||
|
inherit pkgs;
|
||||||
|
name = "default";
|
||||||
|
k8s = { inherit loadJSON loadYAML toBase64; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flattenResources = resources: flatten (
|
||||||
|
mapAttrsToList (name: resourceGroup:
|
||||||
|
mapAttrsToList (name: resource: resource) resourceGroup
|
||||||
|
) resources
|
||||||
|
);
|
||||||
|
|
||||||
|
filterResources = resourceFilter: resources:
|
||||||
|
mapAttrs (groupName: resources:
|
||||||
|
(filterAttrs (name: resource:
|
||||||
|
resourceFilter groupName name resource
|
||||||
|
) resources)
|
||||||
|
) resources;
|
||||||
|
|
||||||
|
toKubernetesList = resources: {
|
||||||
|
kind = "List";
|
||||||
|
apiVersion = "v1";
|
||||||
|
items = resources;
|
||||||
|
};
|
||||||
|
|
||||||
|
removeNixOptions = resources:
|
||||||
|
map (filterAttrs (name: attr: name != "nix")) resources;
|
||||||
|
|
||||||
|
buildResources = {
|
||||||
|
configuration ? {},
|
||||||
|
resourceFilter ? groupName: name: resource: true,
|
||||||
|
withDependencies ? true
|
||||||
|
}: let
|
||||||
|
evaldConfiguration = evalKubernetesModules configuration;
|
||||||
|
|
||||||
|
allResources = moduleToAttrs (
|
||||||
|
evaldConfiguration.config.kubernetes.resources //
|
||||||
|
evaldConfiguration.config.kubernetes.customResources
|
||||||
|
);
|
||||||
|
|
||||||
|
filteredResources = filterResources resourceFilter allResources;
|
||||||
|
|
||||||
|
allDependencies = flatten (
|
||||||
|
mapAttrsToList (groupName: resources:
|
||||||
|
mapAttrsToList (name: resource: resource.nix.dependencies) resources
|
||||||
|
) filteredResources
|
||||||
|
);
|
||||||
|
|
||||||
|
resourceDependencies =
|
||||||
|
filterResources (groupName: name: resource:
|
||||||
|
elem "${groupName}/${name}" allDependencies
|
||||||
|
) allResources;
|
||||||
|
|
||||||
|
finalResources =
|
||||||
|
if withDependencies
|
||||||
|
then recursiveUpdate resourceDependencies filteredResources
|
||||||
|
else filteredResources;
|
||||||
|
|
||||||
|
resources = removeNixOptions (
|
||||||
|
# custom resource definitions have to be allways created first
|
||||||
|
(flattenResources (filterResources (groupName: name: resource:
|
||||||
|
groupName == "customResourceDefinitions"
|
||||||
|
) finalResources)) ++
|
||||||
|
|
||||||
|
# everything but custom resource definitions
|
||||||
|
(flattenResources (filterResources (groupName: name: resource:
|
||||||
|
groupName != "customResourceDefinitions"
|
||||||
|
) finalResources))
|
||||||
|
);
|
||||||
|
|
||||||
|
kubernetesList = toKubernetesList resources;
|
||||||
|
in pkgs.writeText "resources.json" (builtins.toJSON kubernetesList);
|
||||||
|
|
||||||
|
in {
|
||||||
|
inherit buildResources;
|
||||||
|
|
||||||
|
test = buildResources { configuration = ./test/default.nix; };
|
||||||
|
}
|
||||||
23
examples/simple/configuration.nix
Normal file
23
examples/simple/configuration.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
kubernetes.resources.deployments.nginx = {
|
||||||
|
metadata.labels.app = "nginx";
|
||||||
|
spec = {
|
||||||
|
replicas = 3;
|
||||||
|
selector.matchLabels.app = "nginx";
|
||||||
|
template = {
|
||||||
|
metadata.labels.app = "nginx";
|
||||||
|
spec.containers.nginx = {
|
||||||
|
name = "nginx";
|
||||||
|
image = "nginx:1.7.9";
|
||||||
|
ports."80" = {};
|
||||||
|
resources.requests.cpu = "100m";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
kubernetes.resources.services.nginx = {
|
||||||
|
spec.selector.app = "nginx";
|
||||||
|
spec.ports."80".targetPort = 80;
|
||||||
|
};
|
||||||
|
}
|
||||||
333
kubernetes.nix
Normal file
333
kubernetes.nix
Normal file
|
|
@ -0,0 +1,333 @@
|
||||||
|
{ config, lib, k8s, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
with import ./lib.nix { inherit pkgs; inherit (pkgs) lib; };
|
||||||
|
|
||||||
|
let
|
||||||
|
fixJSON = content: replaceStrings ["\\u"] ["u"] content;
|
||||||
|
|
||||||
|
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
|
||||||
|
|
||||||
|
hasTypeMapping = def:
|
||||||
|
hasAttr "type" def &&
|
||||||
|
elem def.type ["string" "integer" "boolean" "object"];
|
||||||
|
|
||||||
|
mapType = def:
|
||||||
|
if def.type == "string" then
|
||||||
|
if hasAttr "format" def && def.format == "int-or-string"
|
||||||
|
then types.either types.int types.str
|
||||||
|
else types.str
|
||||||
|
else if def.type == "integer" then types.int
|
||||||
|
else if def.type == "boolean" then types.bool
|
||||||
|
else if def.type == "object" then types.attrs
|
||||||
|
else throw "type ${def.type} not supported";
|
||||||
|
|
||||||
|
# Either value of type `finalType` or `coercedType`, the latter is
|
||||||
|
# converted to `finalType` using `coerceFunc`.
|
||||||
|
coercedTo = coercedType: coerceFunc: finalType:
|
||||||
|
mkOptionType rec {
|
||||||
|
name = "coercedTo";
|
||||||
|
description = "${finalType.description} or ${coercedType.description}";
|
||||||
|
check = x: finalType.check x || coercedType.check x;
|
||||||
|
merge = loc: defs:
|
||||||
|
let
|
||||||
|
coerceVal = val:
|
||||||
|
if finalType.check val then val
|
||||||
|
else let
|
||||||
|
coerced = coerceFunc val;
|
||||||
|
in assert finalType.check coerced; coerced;
|
||||||
|
|
||||||
|
in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
|
||||||
|
getSubOptions = finalType.getSubOptions;
|
||||||
|
getSubModules = finalType.getSubModules;
|
||||||
|
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m);
|
||||||
|
typeMerge = t1: t2: null;
|
||||||
|
functor = (defaultFunctor name) // { wrapped = finalType; };
|
||||||
|
};
|
||||||
|
|
||||||
|
submoduleOf = definition: types.submodule ({name, ...}: {
|
||||||
|
options = definition.options;
|
||||||
|
config = definition.config;
|
||||||
|
});
|
||||||
|
|
||||||
|
refType = attr: head (tail (tail (splitString "/" attr."$ref")));
|
||||||
|
|
||||||
|
extraOptions = {
|
||||||
|
nix.dependencies = mkOption {
|
||||||
|
description = "List of resources that resource depends on";
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
definitionsForKubernetesSpecs = path:
|
||||||
|
let
|
||||||
|
swagger = fetchSpecs path;
|
||||||
|
swaggerDefinitions = swagger.definitions;
|
||||||
|
|
||||||
|
definitions = mapAttrs (name: definition:
|
||||||
|
# if $ref is in definition it means it's an alias of other definition
|
||||||
|
if hasAttr "$ref" definition
|
||||||
|
then definitions."${refType definition}"
|
||||||
|
|
||||||
|
else if !(hasAttr "properties" definition)
|
||||||
|
then {}
|
||||||
|
|
||||||
|
# in other case it's an actual definition
|
||||||
|
else {
|
||||||
|
options = mapAttrs (propName: property:
|
||||||
|
let
|
||||||
|
isRequired = elem propName (definition.required or []);
|
||||||
|
requiredOrNot = type: if isRequired then type else types.nullOr type;
|
||||||
|
optionProperties =
|
||||||
|
# if $ref is in property it references other definition,
|
||||||
|
# but if other definition does not have properties, then just take it's type
|
||||||
|
if hasAttr "$ref" property then
|
||||||
|
if hasTypeMapping swaggerDefinitions.${refType property} then {
|
||||||
|
type = requiredOrNot (mapType swaggerDefinitions.${refType property});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type = requiredOrNot (submoduleOf definitions.${refType 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 swaggerDefinitions.${refType property.items}
|
||||||
|
then {
|
||||||
|
type = requiredOrNot (types.listOf (mapType swaggerDefinitions.${refType property.items}.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
# if a reference is to complex type
|
||||||
|
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";
|
||||||
|
convertName = name:
|
||||||
|
if definitions.${refType property.items}.options.${mergeKey}.type == types.int
|
||||||
|
then toInt name
|
||||||
|
else name;
|
||||||
|
in {
|
||||||
|
type = requiredOrNot (coercedTo
|
||||||
|
(types.listOf (submoduleOf definitions.${refType property.items}))
|
||||||
|
(values:
|
||||||
|
listToAttrs (map
|
||||||
|
(value: nameValuePair (
|
||||||
|
if isAttrs value.${mergeKey}
|
||||||
|
then toString value.${mergeKey}.content
|
||||||
|
else (toString value.${mergeKey})
|
||||||
|
) value)
|
||||||
|
values)
|
||||||
|
)
|
||||||
|
(types.attrsOf (types.submodule (
|
||||||
|
{name, ...}: {
|
||||||
|
options = definitions.${refType property.items}.options;
|
||||||
|
config = definitions.${refType property.items}.config // {
|
||||||
|
${mergeKey} = mkOverride 1002 (convertName name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
))
|
||||||
|
));
|
||||||
|
apply = values: if values != null then mapAttrsToList (n: v: v) values else values;
|
||||||
|
}
|
||||||
|
|
||||||
|
# in other case it's a simple list
|
||||||
|
else {
|
||||||
|
type = requiredOrNot (types.listOf (submoduleOf definitions.${refType property.items}));
|
||||||
|
}
|
||||||
|
|
||||||
|
# in other case it only references a simple type
|
||||||
|
else {
|
||||||
|
type = requiredOrNot (types.listOf (mapType property.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
else if property.type == "object" && hasAttr "additionalProperties" property
|
||||||
|
then
|
||||||
|
# if it is a reference to simple type
|
||||||
|
if (
|
||||||
|
hasAttr "$ref" property.additionalProperties &&
|
||||||
|
hasTypeMapping swaggerDefinitions.${refType property.additionalProperties}
|
||||||
|
) then {
|
||||||
|
type = requiredOrNot (types.attrsOf (mapType swaggerDefinitions.${refType property.additionalProperties}));
|
||||||
|
}
|
||||||
|
|
||||||
|
# if is an array
|
||||||
|
else if property.additionalProperties.type == "array"
|
||||||
|
then {
|
||||||
|
type = requiredOrNot (types.loaOf (mapType property.additionalProperties.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
type = requiredOrNot (types.attrsOf (mapType property.additionalProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
# just a simple property
|
||||||
|
else {
|
||||||
|
type = requiredOrNot (mapType property);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
mkOption {
|
||||||
|
inherit (definition) description;
|
||||||
|
} // optionProperties // (optionalAttrs (!isRequired) {
|
||||||
|
})
|
||||||
|
) definition.properties;
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
optionalProps = filterAttrs (propName: property:
|
||||||
|
!(elem propName (definition.required or []))
|
||||||
|
) definition.properties;
|
||||||
|
in mapAttrs (name: property: mkOverride 1002 null) optionalProps;
|
||||||
|
}
|
||||||
|
) swaggerDefinitions;
|
||||||
|
|
||||||
|
exportedDefinitions =
|
||||||
|
zipAttrs (
|
||||||
|
mapAttrsToList (name: path: let
|
||||||
|
kind = path.post."x-kubernetes-group-version-kind".kind;
|
||||||
|
|
||||||
|
lastChar = substring ((stringLength kind)-1) (stringLength kind) kind;
|
||||||
|
|
||||||
|
suffix =
|
||||||
|
if lastChar == "y" then "ies"
|
||||||
|
else if hasSuffix "ss" kind then "ses"
|
||||||
|
else if lastChar == "s" then "s"
|
||||||
|
else "${lastChar}s";
|
||||||
|
|
||||||
|
optionName = "${toLower (substring 0 1 kind)}${substring 1 ((stringLength kind)-2) kind}${suffix}";
|
||||||
|
in {
|
||||||
|
${optionName} = refType (head path.post.parameters).schema;
|
||||||
|
})
|
||||||
|
(filterAttrs (name: path:
|
||||||
|
hasAttr "post" path &&
|
||||||
|
path.post."x-kubernetes-action" == "post"
|
||||||
|
) swagger.paths)
|
||||||
|
);
|
||||||
|
|
||||||
|
kubernetesResourceOptions = mapAttrs (groupName: value:
|
||||||
|
let
|
||||||
|
values = if isList value then reverseList value else [value];
|
||||||
|
definitionName = tail values;
|
||||||
|
|
||||||
|
submoduleWithDefaultsOf = definition: swaggerDefinition: let
|
||||||
|
kind = (head swaggerDefinition."x-kubernetes-group-version-kind").kind;
|
||||||
|
group = (head swaggerDefinition."x-kubernetes-group-version-kind").group;
|
||||||
|
version = (head swaggerDefinition."x-kubernetes-group-version-kind").version;
|
||||||
|
groupVersion = if group != "" then "${group}/${version}" else version;
|
||||||
|
in types.submodule ({name, ...}: {
|
||||||
|
options = definition.options // extraOptions;
|
||||||
|
config = mkMerge [
|
||||||
|
definition.config
|
||||||
|
{
|
||||||
|
kind = mkOptionDefault kind;
|
||||||
|
apiVersion = mkOptionDefault groupVersion;
|
||||||
|
|
||||||
|
# metdata.name cannot use option default, due deep config
|
||||||
|
metadata.name = mkOptionDefault name;
|
||||||
|
}
|
||||||
|
(mkAllDefault config.kubernetes.defaults.${groupName} 1001)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
type =
|
||||||
|
if (length values) > 1
|
||||||
|
then fold (name: other:
|
||||||
|
types.either (submoduleWithDefaultsOf definitions.${name} swaggerDefinitions.${name}) other
|
||||||
|
) (submoduleWithDefaultsOf definitions.${head values} swaggerDefinitions.${head values}) (drop 1 values)
|
||||||
|
else submoduleWithDefaultsOf definitions.${head values} swaggerDefinitions.${head values};
|
||||||
|
in mkOption {
|
||||||
|
description = swaggerDefinitions.${definitionName}.description;
|
||||||
|
type = types.attrsOf type;
|
||||||
|
default = {};
|
||||||
|
}) exportedDefinitions;
|
||||||
|
|
||||||
|
customResourceOptions = mapAttrs (name: crd:
|
||||||
|
mkOption {
|
||||||
|
type = types.attrsOf (types.submodule ({name, config, ...}: {
|
||||||
|
options = {
|
||||||
|
apiVersion = mkOption {
|
||||||
|
description = "API version of custom resource";
|
||||||
|
type = types.str;
|
||||||
|
default = "${crd.spec.group}/${crd.spec.version}";
|
||||||
|
};
|
||||||
|
|
||||||
|
kind = mkOption {
|
||||||
|
description = "Custom resource kind";
|
||||||
|
type = types.str;
|
||||||
|
default = crd.spec.names.kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
metadata = mkOption {
|
||||||
|
description = "Metadata";
|
||||||
|
type = submoduleOf definitions."io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
spec = mkOption {
|
||||||
|
description = "Custom resource specification";
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
} // extraOptions;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
) config.kubernetes.resources.customResourceDefinitions;
|
||||||
|
in {
|
||||||
|
inherit swaggerDefinitions definitions exportedDefinitions kubernetesResourceOptions customResourceOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
versionDefinitions = {
|
||||||
|
"1.7" = definitionsForKubernetesSpecs ./specs/1.7/swagger.json;
|
||||||
|
"1.8" = definitionsForKubernetesSpecs ./specs/1.8/swagger.json;
|
||||||
|
};
|
||||||
|
|
||||||
|
versionOptions = {
|
||||||
|
"1.7" = (versionDefinitions."1.7").kubernetesResourceOptions // {
|
||||||
|
# kubernetes 1.7 supports crd, but does not have swagger definitions for some reason
|
||||||
|
customResourceDefinitions =
|
||||||
|
versionDefinitions."1.8".kubernetesResourceOptions.customResourceDefinitions;
|
||||||
|
};
|
||||||
|
"1.8" = (versionDefinitions."1.8").kubernetesResourceOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultOptions = mapAttrs (name: value: mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
}) versionOptions.${config.kubernetes.version};
|
||||||
|
in {
|
||||||
|
options.kubernetes.version = mkOption {
|
||||||
|
description = "Kubernetes version to deploy to";
|
||||||
|
type = types.enum (attrNames versionDefinitions);
|
||||||
|
default = "1.7";
|
||||||
|
};
|
||||||
|
|
||||||
|
options.kubernetes.resources = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = versionOptions.${config.kubernetes.version};
|
||||||
|
};
|
||||||
|
description = "Attribute set of kubernetes resources";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
options.kubernetes.defaults = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = defaultOptions;
|
||||||
|
};
|
||||||
|
description = "";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
options.kubernetes.customResources = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = versionDefinitions.${config.kubernetes.version}.customResourceOptions;
|
||||||
|
};
|
||||||
|
description = "Attribute set of custom kubernetes resources";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
32
lib.nix
Normal file
32
lib.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{lib, pkgs}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
rec {
|
||||||
|
mkAllDefault = value: priority:
|
||||||
|
if isAttrs value
|
||||||
|
then mapAttrs (n: v: mkAllDefault v priority) value
|
||||||
|
|
||||||
|
else if isList value
|
||||||
|
then map (v: mkAllDefault v priority) value
|
||||||
|
|
||||||
|
else mkOverride priority value;
|
||||||
|
|
||||||
|
moduleToAttrs = value:
|
||||||
|
if isAttrs value
|
||||||
|
then mapAttrs (n: v: moduleToAttrs v) (filterAttrs (n: v: !(hasPrefix "_" n) && v != null) value)
|
||||||
|
|
||||||
|
else if isList value
|
||||||
|
then map (v: moduleToAttrs v) value
|
||||||
|
|
||||||
|
else value;
|
||||||
|
|
||||||
|
loadJSON = path: mkAllDefault (builtins.fromJSON (builtins.readFile path)) 1000;
|
||||||
|
|
||||||
|
loadYAML = path: loadJSON (pkgs.runCommand "yaml-to-json" {
|
||||||
|
} "${pkgs.remarshal}/bin/remarshal -i ${path} -if yaml -of json > $out");
|
||||||
|
|
||||||
|
toBase64 = value:
|
||||||
|
builtins.readFile
|
||||||
|
(pkgs.runCommand "value-to-b64" {} "echo '${value}' | ${pkgs.coreutils}/bin/base64 -w0 > $out");
|
||||||
|
}
|
||||||
95
modules.nix
Normal file
95
modules.nix
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
{ config, lib, pkgs, k8s, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
with import ./lib.nix { inherit pkgs lib; };
|
||||||
|
|
||||||
|
let
|
||||||
|
globalConfig = config;
|
||||||
|
|
||||||
|
evalK8SModule = {module, name, configuration}: evalModules {
|
||||||
|
modules = [
|
||||||
|
./kubernetes.nix ./modules.nix module configuration
|
||||||
|
] ++ config.kubernetes.defaultModuleConfiguration;
|
||||||
|
args = {
|
||||||
|
inherit pkgs k8s name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
prefixResources = resources: serviceName:
|
||||||
|
mapAttrs (groupName: resources:
|
||||||
|
mapAttrs' (name: resource: nameValuePair "${serviceName}-${name}" resource) resources
|
||||||
|
) resources;
|
||||||
|
in {
|
||||||
|
options.kubernetes.defaultModuleConfiguration = mkOption {
|
||||||
|
description = "Default configuration for kubernetes modules";
|
||||||
|
type = types.listOf types.attrs;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
options.kubernetes.moduleDefinitions = mkOption {
|
||||||
|
description = "Attribute set of module definitions";
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule ({name, ...}: {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "Module definition name";
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
|
||||||
|
module = mkOption {
|
||||||
|
description = "Module definition";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
options.kubernetes.modules = mkOption {
|
||||||
|
description = "Attribute set of module definitions";
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule ({config, name, ...}: {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "Module name";
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
|
||||||
|
configuration = mkOption {
|
||||||
|
description = "Module configuration";
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
module = mkOption {
|
||||||
|
description = "Name of the module to use";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
evaledModule = mkOption {
|
||||||
|
description = "Evaluated config";
|
||||||
|
internal = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
evaledModule = (evalK8SModule {
|
||||||
|
module = globalConfig.kubernetes.moduleDefinitions.${config.module}.module;
|
||||||
|
inherit (config) name configuration;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
kubernetes.resources = mkMerge (
|
||||||
|
mapAttrsToList (name: module:
|
||||||
|
prefixResources (moduleToAttrs module.evaledModule.config.kubernetes.resources) module.name
|
||||||
|
) config.kubernetes.modules
|
||||||
|
);
|
||||||
|
|
||||||
|
kubernetes.defaultModuleConfiguration = [{
|
||||||
|
config.kubernetes.version = mkDefault config.kubernetes.version;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
}
|
||||||
56391
specs/1.7/swagger.json
Normal file
56391
specs/1.7/swagger.json
Normal file
File diff suppressed because it is too large
Load diff
73741
specs/1.8/swagger.json
Normal file
73741
specs/1.8/swagger.json
Normal file
File diff suppressed because it is too large
Load diff
8
test/configMap.json
Normal file
8
test/configMap.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"data": {
|
||||||
|
"game.properties": "enemies=aliens\nlives=3\nenemies.cheat=true\nenemies.cheat.level=noGoodRotten\nsecret.code.passphrase=UUDDLRLRBABAS\nsecret.code.allowed=true\nsecret.code.lives=30\n",
|
||||||
|
"ui.properties": "color.good=purple\ncolor.bad=yellow\nallow.textmode=true\nhow.nice.to.look=fairlyNice\n"
|
||||||
|
},
|
||||||
|
"kind": "ConfigMap"
|
||||||
|
}
|
||||||
11
test/cr.json
Normal file
11
test/cr.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"apiVersion": "stable.example.com/v1",
|
||||||
|
"kind": "CronTab",
|
||||||
|
"metadata": {
|
||||||
|
"name": "my-new-cron-object"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"cronSpec": "* * * * */5",
|
||||||
|
"image": "my-awesome-cron-image"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
test/crd.json
Normal file
20
test/crd.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"apiVersion": "apiextensions.k8s.io/v1beta1",
|
||||||
|
"kind": "CustomResourceDefinition",
|
||||||
|
"metadata": {
|
||||||
|
"name": "crontabs.stable.example.com"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"group": "stable.example.com",
|
||||||
|
"version": "v1",
|
||||||
|
"scope": "Namespaced",
|
||||||
|
"names": {
|
||||||
|
"plural": "crontabs",
|
||||||
|
"singular": "crontab",
|
||||||
|
"kind": "CronTab",
|
||||||
|
"shortNames": [
|
||||||
|
"ct"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
test/daemonset.json
Normal file
67
test/daemonset.json
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"kind": "DaemonSet",
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"k8s-app": "fluentd-logging"
|
||||||
|
},
|
||||||
|
"name": "fluentd-elasticsearch",
|
||||||
|
"namespace": "kube-system"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"selector": {
|
||||||
|
"matchLabels": {
|
||||||
|
"name": "fluentd-elasticsearch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"name": "fluentd-elasticsearch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"image": "gcr.io/google-containers/fluentd-elasticsearch:1.20",
|
||||||
|
"name": "fluentd-elasticsearch",
|
||||||
|
"resources": {
|
||||||
|
"limits": {
|
||||||
|
"memory": "200Mi"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"cpu": "100m",
|
||||||
|
"memory": "200Mi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"volumeMounts": [
|
||||||
|
{
|
||||||
|
"mountPath": "/var/log",
|
||||||
|
"name": "varlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mountPath": "/var/lib/docker/containers",
|
||||||
|
"name": "varlibdockercontainers",
|
||||||
|
"readOnly": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminationGracePeriodSeconds": 30,
|
||||||
|
"volumes": [
|
||||||
|
{
|
||||||
|
"hostPath": {
|
||||||
|
"path": "/var/log"
|
||||||
|
},
|
||||||
|
"name": "varlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hostPath": {
|
||||||
|
"path": "/var/lib/docker/containers"
|
||||||
|
},
|
||||||
|
"name": "varlibdockercontainers"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
test/default.nix
Normal file
7
test/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
kubernetes.version = "1.7";
|
||||||
|
|
||||||
|
require = [./modules.nix ./deployment.nix];
|
||||||
|
}
|
||||||
38
test/deployment.json
Normal file
38
test/deployment.json
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "nginx-deployment",
|
||||||
|
"labels": {
|
||||||
|
"app": "nginx"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"selector": {
|
||||||
|
"matchLabels": {
|
||||||
|
"app": "nginx"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"app": "nginx"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": {
|
||||||
|
"nginx": {
|
||||||
|
"name": "nginx",
|
||||||
|
"image": "nginx:1.7.9",
|
||||||
|
"ports": {
|
||||||
|
"80": {}
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"requests": {
|
||||||
|
"cpu": "100m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/deployment.nix
Normal file
24
test/deployment.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{lib, k8s, ...}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
kubernetes.resources = {
|
||||||
|
deployments.deployment = mkMerge [
|
||||||
|
(k8s.loadJSON ./deployment.json)
|
||||||
|
{
|
||||||
|
metadata.name = "abcd";
|
||||||
|
nix.dependencies = ["configMaps/configmap"];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
configMaps.configmap = k8s.loadJSON ./configMap.json;
|
||||||
|
namespaces.namespace = k8s.loadJSON ./namespace.json;
|
||||||
|
daemonSets.daemonset = k8s.loadJSON ./daemonset.json;
|
||||||
|
services.service = k8s.loadJSON ./service.json;
|
||||||
|
customResourceDefinitions.cron = k8s.loadJSON ./crd.json;
|
||||||
|
};
|
||||||
|
|
||||||
|
kubernetes.customResources.cron.my-awesome-cron-object = k8s.loadJSON ./cr.json;
|
||||||
|
};
|
||||||
|
}
|
||||||
54
test/modules.nix
Normal file
54
test/modules.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
{lib, k8s, ...}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
kubernetes.moduleDefinitions.nginx.module = {name, config, ...}: {
|
||||||
|
options = {
|
||||||
|
port = mkOption {
|
||||||
|
description = "Port for nginx to listen on";
|
||||||
|
type = types.int;
|
||||||
|
default = 80;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
kubernetes.resources.deployments.nginx = mkMerge [
|
||||||
|
(k8s.loadJSON ./deployment.json)
|
||||||
|
{
|
||||||
|
metadata.name = "${name}-nginx";
|
||||||
|
|
||||||
|
spec.template.spec.containers.nginx.ports."80" = {
|
||||||
|
containerPort = config.port;
|
||||||
|
};
|
||||||
|
|
||||||
|
spec.template.spec.containers.nginx.env.name.valueFrom.secretKeyRef = {
|
||||||
|
name = config.kubernetes.resources.configMaps.nginx.metadata.name;
|
||||||
|
key = "somekey";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
kubernetes.resources.configMaps.nginx = mkMerge [
|
||||||
|
(k8s.loadJSON ./configMap.json)
|
||||||
|
{
|
||||||
|
metadata.name = mkForce "${name}-nginx";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
kubernetes.modules.app-v1.module = "nginx";
|
||||||
|
kubernetes.modules.app-v2 = {
|
||||||
|
module = "nginx";
|
||||||
|
configuration.port = 8080;
|
||||||
|
};
|
||||||
|
|
||||||
|
kubernetes.resources.services.nginx = k8s.loadJSON ./service.json;
|
||||||
|
|
||||||
|
kubernetes.defaultModuleConfiguration = [{
|
||||||
|
kubernetes.defaults.deployments.spec.replicas = 3;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
}
|
||||||
10
test/namespace.json
Normal file
10
test/namespace.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"kind": "Namespace",
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"metadata": {
|
||||||
|
"name": "openshift-origin",
|
||||||
|
"labels": {
|
||||||
|
"name": "openshift-origin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
test/service.json
Normal file
26
test/service.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"kind": "Service",
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"metadata": {
|
||||||
|
"name": "nginx"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"selector": {
|
||||||
|
"app": "nginx"
|
||||||
|
},
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"name": "http",
|
||||||
|
"protocol": "TCP",
|
||||||
|
"port": 80,
|
||||||
|
"targetPort": 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "https",
|
||||||
|
"protocol": "TCP",
|
||||||
|
"port": 443,
|
||||||
|
"targetPort": 443
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue