kubenix/jobs/generators/istio/default.nix

381 lines
14 KiB
Nix
Raw Normal View History

2022-04-02 12:40:35 -07:00
{
pkgs ? import <nixpkgs> {},
lib ? pkgs.lib,
spec ? ./istio-schema.json,
}:
with lib; let
2019-02-11 21:21:07 +01:00
gen = rec {
2021-05-13 17:27:08 -04:00
mkMerge = values: ''mkMerge [${concatMapStrings
2022-04-02 12:40:35 -07:00
(value: "
2019-02-11 21:21:07 +01:00
${value}
2021-05-13 17:27:08 -04:00
")
2022-04-02 12:40:35 -07:00
values}]'';
2019-02-11 21:21:07 +01:00
2021-05-13 17:27:08 -04:00
toNixString = value:
if isAttrs value || isList value
2019-02-11 21:21:07 +01:00
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));
2022-04-02 12:40:35 -07:00
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};''}
}'';
2019-02-11 21:21:07 +01:00
2021-05-13 17:27:08 -04:00
mkOverride = priority: value: "mkOverride ${toString priority} ${toNixString value}";
2019-02-11 21:21:07 +01:00
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})";
2022-04-02 12:40:35 -07:00
coercedTo = coercedType: coerceFunc: finalType: "(types.coercedTo ${coercedType} ${coerceFunc} ${finalType})";
2019-02-11 21:21:07 +01:00
either = val1: val2: "(types.either ${val1} ${val2})";
loaOf = type: "(types.loaOf ${type})";
};
hasTypeMapping = def:
2022-04-02 12:40:35 -07:00
hasAttr "type" def
&& elem def.type ["string" "integer" "boolean"];
2019-02-11 21:21:07 +01:00
mergeValuesByKey = mergeKey: ''(mergeValuesByKey "${mergeKey}")'';
mapType = def:
2022-04-02 12:40:35 -07:00
if def.type == "string"
then
2019-02-11 21:21:07 +01:00
if hasAttr "format" def && def.format == "int-or-string"
then types.either types.int types.str
else types.str
2022-04-02 12:40:35 -07:00
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
2019-02-11 21:21:07 +01:00
else throw "type ${def.type} not supported";
2022-04-02 13:41:07 -07:00
submoduleOf = _definitions: ref: ''(submoduleOf "${ref}")'';
2019-02-11 21:21:07 +01:00
2022-04-02 12:40:35 -07:00
submoduleForDefinition = ref: name: kind: group: version: ''(submoduleForDefinition "${ref}" "${name}" "${kind}" "${group}" "${version}")'';
2019-02-11 21:21:07 +01:00
2022-04-02 12:40:35 -07:00
coerceAttrsOfSubmodulesToListByKey = ref: mergeKey: ''(coerceAttrsOfSubmodulesToListByKey "${ref}" "${mergeKey}")'';
2019-02-11 21:21:07 +01:00
attrsToList = "values: if values != null then mapAttrsToList (n: v: v) values else values";
refDefinition = attr: head (tail (tail (splitString "/" attr."$ref")));
};
2022-04-02 12:40:35 -07:00
fixJSON = content: replaceStrings ["\\u"] ["u"] content;
2019-02-11 21:21:07 +01:00
fetchSpecs = path: builtins.fromJSON (fixJSON (builtins.readFile path));
2022-04-02 12:40:35 -07:00
genDefinitions = swagger:
with gen; (mapAttrs
(
2022-04-02 13:41:07 -07:00
_name: definition:
2022-04-02 12:40:35 -07:00
# 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)
2021-05-13 17:27:08 -04:00
then {
2022-04-02 12:40:35 -07:00
type = types.unspecified;
2021-05-13 17:27:08 -04:00
}
2022-04-02 12:40:35 -07:00
# 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
2021-05-13 17:27:08 -04:00
then
2022-04-02 12:40:35 -07:00
# 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));
2021-05-13 17:27:08 -04:00
}
2022-04-02 12:40:35 -07:00
# 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
2021-05-13 17:27:08 -04:00
else {
2022-04-02 12:40:35 -07:00
type = requiredOrNot (types.listOf (mapType property.items));
2021-05-13 17:27:08 -04:00
}
2022-04-02 12:40:35 -07:00
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)
2021-05-13 17:27:08 -04:00
)
definition.properties;
2022-04-02 12:40:35 -07:00
config = let
optionalProps =
filterAttrs
(
2022-04-02 13:41:07 -07:00
propName: _property:
2022-04-02 12:40:35 -07:00
!(elem propName (definition.required or []))
)
definition.properties;
in
2022-04-02 13:41:07 -07:00
mapAttrs (_name: _property: mkOverride 1002 null) optionalProps;
2022-04-02 12:40:35 -07:00
}
2021-05-13 17:27:08 -04:00
)
2022-04-02 12:40:35 -07:00
swagger.definitions);
genResources = swagger:
(mapAttrsToList
2022-04-02 13:41:07 -07:00
(_name: property: rec {
2022-04-02 12:40:35 -07:00
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
(
2022-04-02 13:41:07 -07:00
_name: property:
2022-04-02 12:40:35 -07:00
(hasPrefix "me.snowdrop.istio.api" property.javaType)
&& hasSuffix "Spec" property.javaType
)
swagger.properties))
++ (mapAttrsToList
2022-04-02 13:41:07 -07:00
(_name: property: rec {
2022-04-02 12:40:35 -07:00
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
(
2022-04-02 13:41:07 -07:00
_name: property:
2022-04-02 12:40:35 -07:00
(hasPrefix "me.snowdrop.istio.mixer" property.javaType)
&& hasSuffix "Spec" property.javaType
)
swagger.properties));
2019-02-11 21:21:07 +01:00
2021-05-13 17:27:08 -04:00
swagger = fetchSpecs spec;
2019-02-11 21:21:07 +01:00
2021-05-13 17:27:08 -04:00
definitions = genDefinitions swagger;
generated = ''
2022-04-02 12:40:35 -07:00
# 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;
};
2022-04-02 12:40:35 -07:00
# 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; };
};
2021-05-13 17:27:08 -04:00
};
2022-04-02 12:40:35 -07:00
mkOptionDefault = mkOverride 1001;
2022-04-02 12:40:35 -07:00
extraOptions = {
kubenix = {};
2021-05-13 17:27:08 -04:00
};
2022-04-02 12:40:35 -07:00
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;
2021-05-13 17:27:08 -04:00
});
2022-04-02 12:40:35 -07:00
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);
};
});
2022-04-02 12:40:35 -07:00
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};
2022-04-02 12:40:35 -07:00
'')
value.options)}};
"}
${optionalString (hasAttr "config" value) ''
2022-04-02 12:40:35 -07:00
config = {${concatStrings (mapAttrsToList (name: value: ''
"${name}" = ${value};
2022-04-02 12:40:35 -07:00
'')
value.config)}};
''}
};
2022-04-02 12:40:35 -07:00
'')
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
2022-04-02 12:40:35 -07:00
pkgs.runCommand "istio-gen.nix"
{
buildInputs = [pkgs.nixpkgs-fmt];
} ''
cat << 'GENERATED' > ./raw
"${generated}"
GENERATED
nixpkgs-fmt ./raw
cp ./raw $out
''