mirror of
https://github.com/TECHNOFAB11/kubenix.git
synced 2025-12-13 00:20:07 +01:00
ref: move generators into jobs & improvements
- format generated files with nixpkgs-fmt Also unrelatedly embellish flake.nix
This commit is contained in:
parent
348d922b18
commit
bf231d19fa
7 changed files with 156 additions and 126 deletions
4
jobs/default.nix
Normal file
4
jobs/default.nix
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{ pkgs }:
|
||||
{
|
||||
generators = pkgs.callPackage ./generators { };
|
||||
}
|
||||
54
jobs/generators/default.nix
Normal file
54
jobs/generators/default.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{ pkgs, lib }:
|
||||
let
|
||||
|
||||
generateIstio = import ./istio {
|
||||
inherit
|
||||
pkgs
|
||||
lib
|
||||
;
|
||||
};
|
||||
|
||||
generateK8S = name: spec: import ./k8s {
|
||||
inherit
|
||||
name
|
||||
pkgs
|
||||
lib
|
||||
spec
|
||||
;
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
|
||||
istio = pkgs.linkFarm "istio-generated" [{
|
||||
name = "latest.nix";
|
||||
path = generateIstio;
|
||||
}];
|
||||
|
||||
k8s = pkgs.linkFarm "k8s-generated" [
|
||||
{
|
||||
name = "v1.19.nix";
|
||||
path = generateK8S "v1.19" (builtins.fetchurl {
|
||||
url = "https://github.com/kubernetes/kubernetes/raw/v1.19.10/api/openapi-spec/swagger.json";
|
||||
sha256 = "sha256-ZXxonUAUxRK6rhTgK62ytTdDKCuOoWPwxJmktiKgcJc=";
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
name = "v1.20.nix";
|
||||
path = generateK8S "v1.20" (builtins.fetchurl {
|
||||
url = "https://github.com/kubernetes/kubernetes/raw/v1.20.6/api/openapi-spec/swagger.json";
|
||||
sha256 = "sha256-xzVOarQDSomHMimpt8H6MfpiQrLl9am2fDvk/GfLkDw=";
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
name = "v1.21.nix";
|
||||
path = generateK8S "v1.21" (builtins.fetchurl {
|
||||
url = "https://github.com/kubernetes/kubernetes/raw/v1.21.0/api/openapi-spec/swagger.json";
|
||||
sha256 = "sha256-EoqYTbtaTlzs7vneoNtXUmdnjTM/U+1gYwCiEy0lOcw=";
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
}
|
||||
369
jobs/generators/istio/default.nix
Normal file
369
jobs/generators/istio/default.nix
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
{ pkgs ? import <nixpkgs> { }, lib ? pkgs.lib, spec ? ./istio-schema.json }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
gen = rec {
|
||||
mkMerge = values: ''mkMerge [${concatMapStrings
|
||||
(value: "
|
||||
${value}
|
||||
")
|
||||
values}]'';
|
||||
|
||||
toNixString = value:
|
||||
if isAttrs value || isList value
|
||||
then builtins.toJSON value
|
||||
else if isString value
|
||||
then ''"${value}"''
|
||||
else if value == null
|
||||
then "null"
|
||||
else builtins.toString value;
|
||||
|
||||
removeEmptyLines = str: concatStringsSep "\n" (filter (l: (builtins.match "( |)+" l) == null) (splitString "\n" str));
|
||||
|
||||
mkOption =
|
||||
{ description ? null
|
||||
, type ? null
|
||||
, default ? null
|
||||
, apply ? null
|
||||
}: removeEmptyLines ''mkOption {
|
||||
${optionalString (description != null) "description = ${builtins.toJSON description};"}
|
||||
${optionalString (type != null) ''type = ${type};''}
|
||||
${optionalString (default != null) ''default = ${toNixString default};''}
|
||||
${optionalString (apply != null) ''apply = ${apply};''}
|
||||
}'';
|
||||
|
||||
mkOverride = priority: value: "mkOverride ${toString priority} ${toNixString value}";
|
||||
|
||||
types = {
|
||||
unspecified = "types.unspecified";
|
||||
str = "types.str";
|
||||
int = "types.int";
|
||||
bool = "types.bool";
|
||||
attrs = "types.attrs";
|
||||
nullOr = val: "(types.nullOr ${val})";
|
||||
attrsOf = val: "(types.attrsOf ${val})";
|
||||
listOf = val: "(types.listOf ${val})";
|
||||
coercedTo = coercedType: coerceFunc: finalType:
|
||||
"(types.coercedTo ${coercedType} ${coerceFunc} ${finalType})";
|
||||
either = val1: val2: "(types.either ${val1} ${val2})";
|
||||
loaOf = type: "(types.loaOf ${type})";
|
||||
};
|
||||
|
||||
hasTypeMapping = def:
|
||||
hasAttr "type" def &&
|
||||
elem def.type [ "string" "integer" "boolean" ];
|
||||
|
||||
mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")'';
|
||||
|
||||
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 == "number" 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";
|
||||
|
||||
submoduleOf = definitions: ref: ''(submoduleOf "${ref}")'';
|
||||
|
||||
submoduleForDefinition = ref: name: kind: group: version:
|
||||
''(submoduleForDefinition "${ref}" "${name}" "${kind}" "${group}" "${version}")'';
|
||||
|
||||
coerceAttrsOfSubmodulesToListByKey = ref: mergeKey:
|
||||
''(coerceAttrsOfSubmodulesToListByKey "${ref}" "${mergeKey}")'';
|
||||
|
||||
attrsToList = "values: if values != null then mapAttrsToList (n: v: v) values else values";
|
||||
|
||||
refDefinition = attr: head (tail (tail (splitString "/" attr."$ref")));
|
||||
};
|
||||
|
||||
fixJSON = content: replaceStrings [ "\\u" ] [ "u" ] content;
|
||||
|
||||
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
|
||||
|
||||
genDefinitions = swagger: with gen; (mapAttrs
|
||||
(name: definition:
|
||||
# if $ref is in definition it means it's an alias of other definition
|
||||
if hasAttr "$ref" definition
|
||||
then definitions."${refDefinition definition}"
|
||||
|
||||
else if !(hasAttr "properties" definition)
|
||||
then {
|
||||
type = mapType 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 swagger.definitions.${refDefinition property} then {
|
||||
type = requiredOrNot (mapType swagger.definitions.${refDefinition property});
|
||||
}
|
||||
else {
|
||||
type = requiredOrNot (submoduleOf definitions (refDefinition property));
|
||||
}
|
||||
|
||||
else if !(hasAttr "type" property) then {
|
||||
type = types.unspecified;
|
||||
}
|
||||
|
||||
# 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 {
|
||||
type = requiredOrNot (types.listOf (mapType swagger.definitions.${refDefinition 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";
|
||||
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 {
|
||||
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 swagger.definitions.${refDefinition property.additionalProperties}
|
||||
) then {
|
||||
type = requiredOrNot (types.attrsOf (mapType swagger.definitions.${refDefinition property.additionalProperties}));
|
||||
}
|
||||
|
||||
else if hasAttr "$ref" property.additionalProperties
|
||||
then {
|
||||
type = requiredOrNot types.attrs;
|
||||
}
|
||||
|
||||
# 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 ({
|
||||
description = property.description or "";
|
||||
} // optionProperties)
|
||||
)
|
||||
definition.properties;
|
||||
config =
|
||||
let
|
||||
optionalProps = filterAttrs
|
||||
(propName: property:
|
||||
!(elem propName (definition.required or [ ]))
|
||||
)
|
||||
definition.properties;
|
||||
in
|
||||
mapAttrs (name: property: mkOverride 1002 null) optionalProps;
|
||||
}
|
||||
)
|
||||
swagger.definitions);
|
||||
|
||||
genResources = swagger: (mapAttrsToList
|
||||
(name: property: rec {
|
||||
splittedType = splitString "." (removePrefix "me.snowdrop.istio.api." property.javaType);
|
||||
group = (concatStringsSep "." (take ((length splittedType) - 2) splittedType)) + ".istio.io";
|
||||
kind = removeSuffix "Spec" (last splittedType);
|
||||
version = last (take ((length splittedType) - 1) splittedType);
|
||||
ref = removePrefix "#/definitions/" property."$ref";
|
||||
})
|
||||
(filterAttrs
|
||||
(name: property:
|
||||
(hasPrefix "me.snowdrop.istio.api" property.javaType) &&
|
||||
hasSuffix "Spec" property.javaType
|
||||
)
|
||||
swagger.properties)) ++ (mapAttrsToList
|
||||
(name: property: rec {
|
||||
splittedType = splitString "." (removePrefix "me.snowdrop.istio.mixer." property.javaType);
|
||||
group = "config.istio.io";
|
||||
version = "v1alpha2";
|
||||
kind = head (tail splittedType);
|
||||
ref = removePrefix "#/definitions/" property."$ref";
|
||||
})
|
||||
(filterAttrs
|
||||
(name: property:
|
||||
(hasPrefix "me.snowdrop.istio.mixer" property.javaType) &&
|
||||
hasSuffix "Spec" property.javaType
|
||||
)
|
||||
swagger.properties));
|
||||
|
||||
swagger = fetchSpecs spec;
|
||||
|
||||
definitions = genDefinitions swagger;
|
||||
|
||||
generated = ''
|
||||
# This file was generated with kubenix k8s generator, do not edit
|
||||
{lib, config, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
types = lib.types // rec {
|
||||
str = mkOptionType {
|
||||
name = "str";
|
||||
description = "string";
|
||||
check = isString;
|
||||
merge = mergeEqualOption;
|
||||
};
|
||||
|
||||
# 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; };
|
||||
};
|
||||
};
|
||||
|
||||
mkOptionDefault = mkOverride 1001;
|
||||
|
||||
extraOptions = {
|
||||
kubenix = {};
|
||||
};
|
||||
|
||||
mergeValuesByKey = mergeKey: values:
|
||||
listToAttrs (map
|
||||
(value: nameValuePair (
|
||||
if isAttrs value.''${mergeKey}
|
||||
then toString value.''${mergeKey}.content
|
||||
else (toString value.''${mergeKey})
|
||||
) value)
|
||||
values);
|
||||
|
||||
submoduleOf = ref: types.submodule ({name, ...}: {
|
||||
options = definitions."''${ref}".options;
|
||||
config = definitions."''${ref}".config;
|
||||
});
|
||||
|
||||
submoduleWithMergeOf = ref: mergeKey: types.submodule ({name, ...}: let
|
||||
convertName = name:
|
||||
if definitions."''${ref}".options.''${mergeKey}.type == types.int
|
||||
then toInt name
|
||||
else name;
|
||||
in {
|
||||
options = definitions."''${ref}".options;
|
||||
config = definitions."''${ref}".config // {
|
||||
''${mergeKey} = mkOverride 1002 (convertName name);
|
||||
};
|
||||
});
|
||||
|
||||
submoduleForDefinition = ref: resource: kind: group: version:
|
||||
types.submodule ({name, ...}: {
|
||||
options = definitions."''${ref}".options // extraOptions;
|
||||
config = mkMerge ([
|
||||
definitions."''${ref}".config
|
||||
{
|
||||
kind = mkOptionDefault kind;
|
||||
apiVersion = mkOptionDefault version;
|
||||
|
||||
# metdata.name cannot use option default, due deep config
|
||||
metadata.name = mkOptionDefault name;
|
||||
}
|
||||
] ++ (config.defaults.''${resource} or [])
|
||||
++ (config.defaults.all or []));
|
||||
});
|
||||
|
||||
coerceAttrsOfSubmodulesToListByKey = ref: mergeKey: (types.coercedTo
|
||||
(types.listOf (submoduleOf ref))
|
||||
(mergeValuesByKey mergeKey)
|
||||
(types.attrsOf (submoduleWithMergeOf ref mergeKey))
|
||||
);
|
||||
|
||||
definitions = {
|
||||
${concatStrings (mapAttrsToList (name: value: ''
|
||||
"${name}" = {${optionalString (hasAttr "options" value) "
|
||||
options = {${concatStrings (mapAttrsToList (name: value: ''
|
||||
"${name}" = ${value};
|
||||
'') value.options)}};
|
||||
"}
|
||||
|
||||
${optionalString (hasAttr "config" value) ''
|
||||
config = {${concatStrings (mapAttrsToList (name: value: ''
|
||||
"${name}" = ${value};
|
||||
'') value.config)}};
|
||||
''}
|
||||
};
|
||||
'') definitions)}
|
||||
} // (import ./overrides.nix {inheirt definitions lib;}));
|
||||
in {
|
||||
kubernetes.customResources = [
|
||||
${concatMapStrings
|
||||
(resource: ''{
|
||||
group = "${resource.group}";
|
||||
version = "${resource.version}";
|
||||
kind = "${resource.kind}";
|
||||
description = "";
|
||||
module = definitions."${resource.ref}";
|
||||
}'')
|
||||
(genResources swagger)}
|
||||
];
|
||||
}
|
||||
'';
|
||||
in
|
||||
pkgs.runCommand "istio-gen.nix"
|
||||
{
|
||||
buildInputs = [ pkgs.nixpkgs-fmt ];
|
||||
} ''
|
||||
cat << 'GENERATED' > ./raw
|
||||
"${generated}"
|
||||
GENERATED
|
||||
|
||||
nixpkgs-fmt ./raw
|
||||
cp ./raw $out
|
||||
''
|
||||
4314
jobs/generators/istio/istio-schema.json
Normal file
4314
jobs/generators/istio/istio-schema.json
Normal file
File diff suppressed because it is too large
Load diff
466
jobs/generators/k8s/default.nix
Normal file
466
jobs/generators/k8s/default.nix
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
{ name
|
||||
, pkgs
|
||||
, lib
|
||||
, spec
|
||||
}:
|
||||
|
||||
with lib;
|
||||
let
|
||||
gen = rec {
|
||||
mkMerge = values: ''mkMerge [${concatMapStrings
|
||||
(value: "
|
||||
${value}
|
||||
")
|
||||
values}]'';
|
||||
|
||||
toNixString = value:
|
||||
if isAttrs value || isList value
|
||||
then builtins.toJSON value
|
||||
else if isString value
|
||||
then ''"${value}"''
|
||||
else if value == null
|
||||
then "null"
|
||||
else builtins.toString value;
|
||||
|
||||
removeEmptyLines = str: concatStringsSep "\n" (filter (l: (builtins.match "( |)+" l) == null) (splitString "\n" str));
|
||||
|
||||
mkOption =
|
||||
{ description ? null
|
||||
, type ? null
|
||||
, default ? null
|
||||
, apply ? null
|
||||
}: removeEmptyLines ''mkOption {
|
||||
${optionalString (description != null) "description = ${builtins.toJSON description};"}
|
||||
${optionalString (type != null) ''type = ${type};''}
|
||||
${optionalString (default != null) ''default = ${toNixString default};''}
|
||||
${optionalString (apply != null) ''apply = ${apply};''}
|
||||
}'';
|
||||
|
||||
mkOverride = priority: value: "mkOverride ${toString priority} ${toNixString value}";
|
||||
|
||||
types = {
|
||||
unspecified = "types.unspecified";
|
||||
str = "types.str";
|
||||
int = "types.int";
|
||||
bool = "types.bool";
|
||||
attrs = "types.attrs";
|
||||
nullOr = val: "(types.nullOr ${val})";
|
||||
attrsOf = val: "(types.attrsOf ${val})";
|
||||
listOf = val: "(types.listOf ${val})";
|
||||
coercedTo = coercedType: coerceFunc: finalType:
|
||||
"(types.coercedTo ${coercedType} ${coerceFunc} ${finalType})";
|
||||
either = val1: val2: "(types.either ${val1} ${val2})";
|
||||
loaOf = type: "(types.loaOf ${type})";
|
||||
};
|
||||
|
||||
hasTypeMapping = def:
|
||||
hasAttr "type" def &&
|
||||
elem def.type [ "string" "integer" "boolean" ];
|
||||
|
||||
mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")'';
|
||||
|
||||
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 == "number" 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";
|
||||
|
||||
submoduleOf = definitions: ref: ''(submoduleOf "${ref}")'';
|
||||
|
||||
submoduleForDefinition = ref: name: kind: group: version:
|
||||
''(submoduleForDefinition "${ref}" "${name}" "${kind}" "${group}" "${version}")'';
|
||||
|
||||
coerceAttrsOfSubmodulesToListByKey = ref: mergeKey:
|
||||
''(coerceAttrsOfSubmodulesToListByKey "${ref}" "${mergeKey}")'';
|
||||
|
||||
attrsToList = "values: if values != null then mapAttrsToList (n: v: v) values else values";
|
||||
|
||||
refDefinition = attr: head (tail (tail (splitString "/" attr."$ref")));
|
||||
};
|
||||
|
||||
refType = attr: head (tail (tail (splitString "/" attr."$ref")));
|
||||
|
||||
compareVersions = ver1: ver2:
|
||||
let
|
||||
getVersion = v: substring 1 10 v;
|
||||
splitVersion = v: builtins.splitVersion (getVersion v);
|
||||
isAlpha = v: elem "alpha" (splitVersion v);
|
||||
patchVersion = v:
|
||||
if isAlpha v then ""
|
||||
else if length (splitVersion v) == 1 then "${getVersion v}prod"
|
||||
else getVersion v;
|
||||
|
||||
v1 = patchVersion ver1;
|
||||
v2 = patchVersion ver2;
|
||||
in
|
||||
builtins.compareVersions v1 v2;
|
||||
|
||||
fixJSON = content: replaceStrings [ "\\u" ] [ "u" ] content;
|
||||
|
||||
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
|
||||
|
||||
genDefinitions = swagger: with gen; mapAttrs
|
||||
(name: definition:
|
||||
# if $ref is in definition it means it's an alias of other definition
|
||||
if hasAttr "$ref" definition
|
||||
then definitions."${refDefinition 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 swagger.definitions.${refDefinition property} then {
|
||||
type = requiredOrNot (mapType swagger.definitions.${refDefinition property});
|
||||
}
|
||||
else {
|
||||
type = requiredOrNot (submoduleOf definitions (refDefinition 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 {
|
||||
type = requiredOrNot (types.listOf (mapType swagger.definitions.${refDefinition 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";
|
||||
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 {
|
||||
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 swagger.definitions.${refDefinition property.additionalProperties}
|
||||
) then {
|
||||
type = requiredOrNot (types.attrsOf (mapType swagger.definitions.${refDefinition property.additionalProperties}));
|
||||
}
|
||||
|
||||
else if hasAttr "$ref" property.additionalProperties
|
||||
then {
|
||||
type = requiredOrNot types.attrs;
|
||||
}
|
||||
|
||||
# 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 ({
|
||||
description = property.description or "";
|
||||
} // optionProperties)
|
||||
)
|
||||
definition.properties;
|
||||
config =
|
||||
let
|
||||
optionalProps = filterAttrs
|
||||
(propName: property:
|
||||
!(elem propName (definition.required or [ ]))
|
||||
)
|
||||
definition.properties;
|
||||
in
|
||||
mapAttrs (name: property: mkOverride 1002 null) optionalProps;
|
||||
}
|
||||
)
|
||||
swagger.definitions;
|
||||
|
||||
mapCharPairs = f: s1: s2: concatStrings (imap0
|
||||
(i: c1:
|
||||
f i c1 (if i >= stringLength s2 then "" else elemAt (stringToCharacters s2) i)
|
||||
)
|
||||
(stringToCharacters s1));
|
||||
|
||||
getAttrName = resource: kind:
|
||||
mapCharPairs
|
||||
(i: c1: c2:
|
||||
if hasPrefix "API" kind && i == 0 then "A"
|
||||
else if i == 0 then c1
|
||||
else if c2 == "" || (toLower c2) != c1 then c1
|
||||
else c2
|
||||
)
|
||||
resource
|
||||
kind;
|
||||
|
||||
genResourceTypes = swagger: mapAttrs'
|
||||
(name: path:
|
||||
let
|
||||
ref = refType (head path.post.parameters).schema;
|
||||
group' = path.post."x-kubernetes-group-version-kind".group;
|
||||
version' = path.post."x-kubernetes-group-version-kind".version;
|
||||
kind' = path.post."x-kubernetes-group-version-kind".kind;
|
||||
name' = last (splitString "/" name);
|
||||
attrName = getAttrName name' kind';
|
||||
in
|
||||
nameValuePair ref {
|
||||
inherit ref attrName;
|
||||
|
||||
name = name';
|
||||
group = if group' == "" then "core" else group';
|
||||
version = version';
|
||||
kind = kind';
|
||||
description = swagger.definitions.${ref}.description;
|
||||
defintion = refDefinition (head path.post.parameters).schema;
|
||||
})
|
||||
(filterAttrs
|
||||
(name: path:
|
||||
hasAttr "post" path &&
|
||||
path.post."x-kubernetes-action" == "post"
|
||||
)
|
||||
swagger.paths);
|
||||
|
||||
swagger = fetchSpecs spec;
|
||||
definitions = genDefinitions swagger;
|
||||
resourceTypes = genResourceTypes swagger;
|
||||
|
||||
resourceTypesByKind = zipAttrs (mapAttrsToList
|
||||
(name: resourceType: {
|
||||
${resourceType.kind} = resourceType;
|
||||
})
|
||||
resourceTypes);
|
||||
|
||||
resourcesTypesByKindSortByVersion = mapAttrs
|
||||
(kind: resourceTypes:
|
||||
reverseList (sort
|
||||
(r1: r2:
|
||||
compareVersions r1.version r2.version > 0
|
||||
)
|
||||
resourceTypes)
|
||||
)
|
||||
resourceTypesByKind;
|
||||
|
||||
latestResourceTypesByKind =
|
||||
mapAttrs (kind: resources: last resources) resourcesTypesByKindSortByVersion;
|
||||
|
||||
genResourceOptions = resource: with gen; let
|
||||
submoduleForDefinition' = definition:
|
||||
let
|
||||
in
|
||||
submoduleForDefinition
|
||||
definition.ref
|
||||
definition.name
|
||||
definition.kind
|
||||
definition.group
|
||||
definition.version;
|
||||
in
|
||||
mkOption {
|
||||
description = resource.description;
|
||||
type = types.attrsOf (submoduleForDefinition' resource);
|
||||
default = { };
|
||||
};
|
||||
|
||||
generated = ''
|
||||
# This file was generated with kubenix k8s generator, do not edit
|
||||
{ lib, options, config, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
getDefaults = resource: group: version: kind:
|
||||
catAttrs "default" (filter (default:
|
||||
(default.resource == null || default.resource == resource) &&
|
||||
(default.group == null || default.group == group) &&
|
||||
(default.version == null || default.version == version) &&
|
||||
(default.kind == null || default.kind == kind)
|
||||
) config.defaults);
|
||||
|
||||
types = lib.types // rec {
|
||||
str = mkOptionType {
|
||||
name = "str";
|
||||
description = "string";
|
||||
check = isString;
|
||||
merge = mergeEqualOption;
|
||||
};
|
||||
|
||||
# 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; };
|
||||
};
|
||||
};
|
||||
|
||||
mkOptionDefault = mkOverride 1001;
|
||||
|
||||
mergeValuesByKey = mergeKey: values:
|
||||
listToAttrs (map
|
||||
(value: nameValuePair (
|
||||
if isAttrs value.''${mergeKey}
|
||||
then toString value.''${mergeKey}.content
|
||||
else (toString value.''${mergeKey})
|
||||
) value)
|
||||
values);
|
||||
|
||||
submoduleOf = ref: types.submodule ({name, ...}: {
|
||||
options = definitions."''${ref}".options or {};
|
||||
config = definitions."''${ref}".config or {};
|
||||
});
|
||||
|
||||
submoduleWithMergeOf = ref: mergeKey: types.submodule ({name, ...}: let
|
||||
convertName = name:
|
||||
if definitions."''${ref}".options.''${mergeKey}.type == types.int
|
||||
then toInt name
|
||||
else name;
|
||||
in {
|
||||
options = definitions."''${ref}".options;
|
||||
config = definitions."''${ref}".config // {
|
||||
''${mergeKey} = mkOverride 1002 (convertName name);
|
||||
};
|
||||
});
|
||||
|
||||
submoduleForDefinition = ref: resource: kind: group: version: let
|
||||
apiVersion = if group == "core" then version else "''${group}/''${version}";
|
||||
in types.submodule ({name, ...}: {
|
||||
imports = getDefaults resource group version kind;
|
||||
options = definitions."''${ref}".options;
|
||||
config = mkMerge [
|
||||
definitions."''${ref}".config
|
||||
{
|
||||
kind = mkOptionDefault kind;
|
||||
apiVersion = mkOptionDefault apiVersion;
|
||||
|
||||
# metdata.name cannot use option default, due deep config
|
||||
metadata.name = mkOptionDefault name;
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
coerceAttrsOfSubmodulesToListByKey = ref: mergeKey: (types.coercedTo
|
||||
(types.listOf (submoduleOf ref))
|
||||
(mergeValuesByKey mergeKey)
|
||||
(types.attrsOf (submoduleWithMergeOf ref mergeKey))
|
||||
);
|
||||
|
||||
definitions = {
|
||||
${concatStrings (mapAttrsToList (name: value: ''
|
||||
"${name}" = {
|
||||
${optionalString (hasAttr "options" value) "
|
||||
options = {${concatStrings (mapAttrsToList (name: value: ''
|
||||
"${name}" = ${value};
|
||||
'') value.options)}};
|
||||
"}
|
||||
|
||||
${optionalString (hasAttr "config" value) ''
|
||||
config = {${concatStrings (mapAttrsToList (name: value: ''
|
||||
"${name}" = ${value};
|
||||
'') value.config)}};
|
||||
''}
|
||||
};
|
||||
'') definitions)}
|
||||
};
|
||||
in {
|
||||
# all resource versions
|
||||
options = {
|
||||
resources = {
|
||||
${concatStrings (mapAttrsToList (_: rt: ''
|
||||
"${rt.group}"."${rt.version}"."${rt.kind}" = ${genResourceOptions rt};
|
||||
'') resourceTypes)}
|
||||
} // {
|
||||
${concatStrings (mapAttrsToList (_: rt: ''
|
||||
"${rt.attrName}" = ${genResourceOptions rt};
|
||||
'') latestResourceTypesByKind)}
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# expose resource definitions
|
||||
inherit definitions;
|
||||
|
||||
# register resource types
|
||||
types = [${concatStrings (mapAttrsToList (_: rt: ''{
|
||||
name = "${rt.name}";
|
||||
group = "${rt.group}";
|
||||
version = "${rt.version}";
|
||||
kind = "${rt.kind}";
|
||||
attrName = "${rt.attrName}";
|
||||
}'') resourceTypes)}];
|
||||
|
||||
resources = {
|
||||
${concatStrings (mapAttrsToList (_: rt: ''
|
||||
"${rt.group}"."${rt.version}"."${rt.kind}" =
|
||||
mkAliasDefinitions options.resources."${rt.attrName}";
|
||||
'') latestResourceTypesByKind)}
|
||||
};
|
||||
};
|
||||
}
|
||||
'';
|
||||
in
|
||||
pkgs.runCommand "k8s-${name}-gen.nix"
|
||||
{
|
||||
buildInputs = [ pkgs.nixpkgs-fmt ];
|
||||
} ''
|
||||
cat << 'GENERATED' > ./raw
|
||||
"${generated}"
|
||||
GENERATED
|
||||
|
||||
nixpkgs-fmt ./raw
|
||||
cp ./raw $out
|
||||
''
|
||||
Loading…
Add table
Add a link
Reference in a new issue