Inspired by farcaller/nix-kube-generators.
A set of tools to handle Kubernetes related resources and Nix, JSON and YAML conversions.
Warning
|
this library makes use of mikefarah/yq, NOT kislyuk/yq! Keep it in mind while troubleshooting. |
Caution
|
Many functions will copy data to the Nix store, which you may not want to allow. Those which do will be appropriately pointed out. |
Some functions are convenience wrappers around others, so you can keep the code cleaner.
-
Nix standard library
-
nixpkgs standard library
Full list
-
lib.attrsets.attrNames
-
lib.attrsets.attrValues
-
lib.attrsets.recursiveUpdate
-
lib.attrsets.mapAttr
-
lib.lists.flatten
-
lib.lists.length
-
lib.strings.toLower
-
lib.strings.concatMapStrings
-
lib.strings.concatMapStringsSep
-
pkgs.yq-go
The library has 2 main focuses: one is handling YAML, JSON and Nix conversions, while the other is managing Helm Charts.
-
: self type, builtin
: builtin in Nix
Input type | YAML | JSON | JSON file | Multiple JSON files | Nix | Nix List |
---|---|---|---|---|---|---|
YAML content (1 object) |
- |
|
|
|
|
|
YAML content (multiple objects) |
- |
|
|
|
|
|
YAML file (single object) |
- |
|
|
|
|
|
YAML file (multiple objects) |
- |
|
|
|
|
|
Nix |
|
builtin |
builtin |
no |
- |
- |
JSON content |
|
- |
- |
no |
builtin |
builtin |
JSON file |
|
- |
- |
no |
builtin |
builtin |
toKubernetesList |
Turns a Nix list into the Nix equivalent of a Kubernetes resource list (kind: List). |
---|---|
|
Takes a JSON kubernetes manifests and builds a key-value pair from the manifest. Also deals with lists. - The VALUE is the content of the file itself - The KEY is the string interpolation of resource name, resource kind and resource namespace. In case of a non-namespaced resource, the kind is repeated. |
|
Sane as keyValFromJsonManifest but allows multiple paths as input. |
From YAML functions:
From JSON functions:
From Nix functions:
Others:
Where possible the following naming convention is used: ($inputFormat)To($outputFormat)(?Files?)
A wrapper around many functions takes care of adding additional flexibility while calling them.
For example you can call yamlToJsonFile in several ways depending on what kind of input you have and how clean you want to keep your code while not needing additional control. Even the source parameter can have multiple forms:
yamlToJsonFile :: Path => Derivation
yamlToJsonFile :: String => Derivation
yamlToJsonFile :: [ Path ] => [ Derivation ]
yamlToJsonFile :: [ String ] => [ Derivation ]
yamlToJsonFile :: Attrset { source, outputType ? null } => Derivation
yamlToJsonFile :: [ Attrset { source (Path), outputType ? null } ] => [ Derivation ]
yamlToJsonFile :: [ Attrset { source (String), outputType ? null } ++ Path ++ String ] => [ Derivation ]
For example these are all valid methods:
yamlToJsonFile ./tests/server-cert.yml # path
yamlToJsonFile (builtins.readFile ./tests/server-cert.yml) # string
yamlToJsonFile [ ./tests/server-cert.yml ./tests/services.yml ./another/path.nix ] # paths
yamlToJsonFile [ (builtins.readFile ./tests/server-cert.yml) (builtins.readFile ./tests/services.yml) ] # strings
yamlToJsonFile { source = ./tests/services.yml; outputType = "object"; } # Attrset
yamlToJsonFile [ { source = ./tests/server-cert.yml; } { source = builtins.readFile ./tests/services.yml; } ] # Attrsets
yamlToJsonFile [ { source = ./tests/services.yml; outputType = "object"; } ./tests/services.yml (builtins.readFile ./tests/server-cert.yml) ] # mix
Expect a list if you call a wrapped function with a list, no matter what its signature is!.
Tipically, wrapped functions do not require but support an Attrset with specific keys to modify the function behavior.
Wrapper implementation
rec {
kallPackage =
incomingArgs: f: overrides:
let
fArgs = functionArgs f;
finalArgs = intersectAttrs fArgs incomingArgs // overrides; # Merge with overrides happens last
in
f finalArgs;
# Allows to call a function in two ways:
# - f /some/path or f (builtins.readFile /some/path) or f (drv)
# - f { arg1 = "val1"; arg2 = "val2"; ... }
# while calling the downstream function with a unified interface.
resolveArgs = args: if isAttrs args then args else { source = args; };
# Caller calls a function with args. Args can be { source, this, that, ... },
# a path/string or a list of elements.
# If a list is detected, each element is processed as follows:
# If a path is detected, the content is read before calling the downstream function.
# If args is Attrset then proceed, if not turn it into an Attrset with defaults.
# If no list is passed process happens normally.
# This allows to mix argument sources:
# - yamlToJsonFile [ ./tests/services.yml (builtins.readFile ./tests/server-cert.yml) ]
# - yamlToJsonFile [ { source = ./tests/services.yml; } { source = (builtins.readFile ./tests/server-cert.yml); } ]
wrapF =
args: f:
let
processUnit = as:
let
finalArgs = resolveArgs as;
sourceIsPath = isPath finalArgs.source;
in
if sourceIsPath then
kallPackage finalArgs f { source = readFile finalArgs.source; }
else
kallPackage finalArgs f { };
in
if isList args
then map processUnit args
else processUnit args;
}
Turns a Nix list into the Nix equivalent of a Kubernetes resource list (kind: List) by default. You can set the list type using the first argument ("List" will be appended) as well as including { Kind = "SomeValue"; } in the override argument, since it uses `lib.attrsets.recursiveUpdate
.
Useful if you want to generate a single JSON file to feed to the Addon Manager.
toKubernetesList :: (String / null) [a] (Attrset / null) -> kubernetes::ResourceList a
example
toKubernetesList "Pod" [{ apiVersion = "v1"; kind = "Pod"; }] { metadata.name = "my-pod-list"; }
{
apiVersion = "v1";
items = [
{
apiVersion = "v1";
kind = "Pod";
}
];
kind = "PodList";
metadata = { name = "my-pod-list"; };
}
toKubernetesList null [{ apiVersion = "v1"; kind = "Pod"; }] { metadata.name = "my-pod-list"; kind = "MyKindList"; }
{
apiVersion = "v1";
items = [
{
apiVersion = "v1";
kind = "Pod";
}
];
kind = "MyKindList";
metadata = { name = "my-pod-list"; };
}
implementation
{
# Turns a Nix list into a generic Kubernetes Resource List
toKubernetesList =
resourceType: items: overrides:
lib.attrsets.recursiveUpdate ({
apiVersion = "v1";
kind = ''${if resourceType != null then resourceType else ""}List'';
inherit items;
} (if overrides != null then overrides else {}));
}
Wrapped. Takes a JSON kubernetes manifest content
and builds a key-value pair from the manifest.
-
The VALUE is the content of the file itself
-
The KEY is the string interpolation of resource name, resource kind and resource namespace. In case of a non-namespaced resource, the kind is repeated
keyValFromJsonManifest: <wrapped> -> Attrset
example
keyValFromJsonManifest (builtins.readFile ./server-cert.json) # Single
{ mypp-serverauth-certificate-myapp = "{\"apiVersion\":\"cert-manager.io/v1\",\"kind\":\"Certificate\",\"metadata\":{\"name\":\"mypp-serverauth\",\"namespace\":\"myapp\"},\"spec\":{\"dnsNames\":[\"myapp.mydoma
in.com\"],\"issuerRef\":{\"kind\":\"ClusterIssuer\",\"name\":\"letsencrypt-dns\"},\"secretName\":\"myapp-server-tls\",\"usages\":[\"server auth\"]}}"; }
keyValFromJsonManifest (yamlToJson { yamlContent = builtins.readFile ./services.yml; } # Multi)
[
{ myapp-service-myapp = "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app\":\"myapp\",\"component\":\"main\"},\"name\":\"myapp\",\"namespace\":\"myapp\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":\"http\"},{\"name\":\"https\",\"port\":443,\"protocol\":\"TCP\",\"targetPort\":\"https\"}],\"selector\":{\"app\":\"myapp\",\"components\":\"main\"}}}"; }
{ myapp-jobscheduler-service-myapp = "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app\":\"myapp\",\"component\":\"jobscheduler\"},\"name\":\"myapp-jobscheduler\",\"namespace\":\"myapp\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":3000,\"protocol\":\"TCP\",\"targetPort\":\"http\"}],\"selector\":{\"app\":\"myapp\",\"component\":\"jobscheduler\"}}}"; }
]
keyValFromJsonManifest [ ./tests/services.json { source = ./tests/server-cert.json; } { source = builtins.readFile ./tests/server-cert.json; } ]
[
[
{ myapp-service-myapp = "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app\":\"myapp\",\"component\":\"main\"},\"name\":\"myapp\",\"namespace\":\"myapp\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":\"http\"},{\"name\":\"https\",\"port\":443,\"protocol\":\"TCP\",\"targetPort\":\"https\"}],\"selector\":{\"app\":\"myapp\",\"components\":\"main\"}}}"; }
{ myapp-jobscheduler-service-myapp = "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app\":\"myapp\",\"component\":\"jobscheduler\"},\"name\":\"myapp-jobscheduler\",\"namespace\":\"myapp\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":3000,\"protocol\":\"TCP\",\"targetPort\":\"http\"}],\"selector\":{\"app\":\"myapp\",\"component\":\"jobscheduler\"}}}"; }
]
{ mypp-serverauth-certificate-myapp = "{\"apiVersion\":\"cert-manager.io/v1\",\"kind\":\"Certificate\",\"metadata\":{\"name\":\"mypp-serverauth\",\"namespace\":\"myapp\"},\"spec\":{\"dnsNames\":[\"myapp.mydomain.com\"],\"issuerRef\":{\"kind\":\"ClusterIssuer\",\"name\":\"letsencrypt-dns\"},\"secretName\":\"myapp-server-tls\",\"usages\":[\"server auth\"]}}"; }
{ mypp-serverauth-certificate-myapp = "{\"apiVersion\":\"cert-manager.io/v1\",\"kind\":\"Certificate\",\"metadata\":{\"name\":\"mypp-serverauth\",\"namespace\":\"myapp\"},\"spec\":{\"dnsNames\":[\"myapp.mydomain.com\"],\"issuerRef\":{\"kind\":\"ClusterIssuer\",\"name\":\"letsencrypt-dns\"},\"secretName\":\"myapp-server-tls\",\"usages\":[\"server auth\"]}}"; }
]
implementation
{
keyValFromJsonManifest = args: wrapF args _keyValFromJsonManifest;
# Takes a JSON kubernetes manifests and builds a key-value pair
# from the manifest.
# - The VALUE is the content of the file itself
# - The KEY is the string interpolation of resource name, resource kind
# and resource namespace. In case of a non-namespaced resource, the kind is repeated
_keyValFromJsonManifest =
{ source }:
let
nixData = fromJSON source;
process =
c:
let
name = c.metadata.name;
kind = lib.strings.toLower c.kind;
third = c.metadata.namespace or kind;
in
{
"${name}-${kind}-${third}" = toJSON c;
};
f =
nixContent: if isList nixContent then map (cont: process cont) nixContent else process nixContent;
in
f nixData;
}
Wrapped. Turns input YAML into JSON.
It does store data in the nix store.
yamlToJson :: <wrapped> -> String
The input is an Attrset of:
-
yamlContent: YAML content as string
-
outputType, which only makes sense if you know the result will be a list:
-
array (default): simple JSON array.
[ {…},{…},… ]
-
object: JSON object with your data stored in the items key.
{ "items": [ {…},{…},… ] }
-
example
yamlToJson { source = builtins.readFile ./tests/services.yml; }
"[\n {\n \"apiVersion\": \"v1\",\n \"kind\": \"Service\",\n \"metadata\": {\n \"name\": \"m
yapp\",\n \"namespace\": \"myapp\",\n \"labels\": {\n \"app\": \"myapp\",\n \"component\": \"m
ain\"\n }\n },\n \"spec\": {\n \"ports\": [\n {\n \"name\": \"http\",\n \"por
t\": 80,\n \"protocol\": \"TCP\",\n \"targetPort\": \"http\"\n },\n {\n \"nam
e\": \"https\",\n \"port\": 443,\n \"protocol\": \"TCP\",\n \"targetPort\": \"https\"\n
}\n ],\n \"selector\": {\n \"app\": \"myapp\",\n \"components\": \"main\"\n }\n }\
n },\n {\n \"apiVersion\": \"v1\",\n \"kind\": \"Service\",\n \"metadata\": {\n \"name\": \"myapp-jobs
cheduler\",\n \"namespace\": \"myapp\",\n \"labels\": {\n \"app\": \"myapp\",\n \"component\":
\"jobscheduler\"\n }\n },\n \"spec\": {\n \"ports\": [\n {\n \"name\": \"http\",\n
\"port\": 3000,\n \"protocol\": \"TCP\",\n \"targetPort\": \"http\"\n }\n ],\n
\"selector\": {\n \"app\": \"myapp\",\n \"component\": \"jobscheduler\"\n }\n }\n }\n]\n"
yamlToJson ./tests/services.yml
"[\n {\n \"apiVersion\": \"v1\",\n \"kind\": \"Service\",\n \"metadata\": {\n \"name\": \"m
yapp\",\n \"namespace\": \"myapp\",\n \"labels\": {\n \"app\": \"myapp\",\n \"component\": \"m
ain\"\n }\n },\n \"spec\": {\n \"ports\": [\n {\n \"name\": \"http\",\n \"por
t\": 80,\n \"protocol\": \"TCP\",\n \"targetPort\": \"http\"\n },\n {\n \"nam
e\": \"https\",\n \"port\": 443,\n \"protocol\": \"TCP\",\n \"targetPort\": \"https\"\n
}\n ],\n \"selector\": {\n \"app\": \"myapp\",\n \"components\": \"main\"\n }\n }\
n },\n {\n \"apiVersion\": \"v1\",\n \"kind\": \"Service\",\n \"metadata\": {\n \"name\": \"myapp-jobs
cheduler\",\n \"namespace\": \"myapp\",\n \"labels\": {\n \"app\": \"myapp\",\n \"component\":
\"jobscheduler\"\n }\n },\n \"spec\": {\n \"ports\": [\n {\n \"name\": \"http\",\n
\"port\": 3000,\n \"protocol\": \"TCP\",\n \"targetPort\": \"http\"\n }\n ],\n
\"selector\": {\n \"app\": \"myapp\",\n \"component\": \"jobscheduler\"\n }\n }\n }\n]\n"
implementation
{
# Converts YAML content to JSON.
yamlToJson =
args:
let
process = a: wrapF a _yamlToJsonFile;
in
if isList args
then
let paths = map process args;
in map builtins.readFile paths # No need to flatten, nested objects in a file remain in the generated file contents
else readFile (wrapF args _yamlToJsonFile);
}
Wrapped. Similar to yamlToJson but turns input YAML into a single JSON file in the store so it does store data in the nix store.
yamlToJson :: <wrapped> -> Derivation
As an input it supports an Attrset of:
-
yamlContent: YAML content as string
-
outputType, which only makes sense if you know the result will be a list:
-
array (default): simple JSON array.
[ {…},{…},… ]
-
object: JSON object with your data stored in the items key.
{ "items": [ {…},{…},… ] }
-
example
builtins.readFile yamlToJsonFile { yamlContent = builtins.readFile ./services.yml; outputType = "object"; } # Since it returns a derivation
"{\n \"items\": [\n {\n \"apiVersion\": \"v1\",\n \"kind\": \"Service\",\n \"metadata\": {\n \"name\": \"myapp\",\n \"namespace\": \"myapp\",\n \"labels\": {\n \"app\": \"myapp\",\n \"component\": \"main\"\n }\n },\n \"spec\": {\n \"ports\": [\n {\n \"name\": \"http\",\n \"port\": 80,\n \"protocol\": \"TCP\",\n \"targetPort\": \"http\"\n },\n {\n \"name\": \"https\",\n \"port\": 443,\n \"protocol\": \"TCP\",\n \"targetPort\": \"https\"\n }\n ],\n \"selector\": {\n \"app\": \"myapp\",\n \"components\": \"main\"\n }\n }\n },\n {\n \"apiVersion\": \"v1\",\n \"kind\": \"Service\",\n \"metadata\": {\n \"name\": \"myapp-jobscheduler\",\n \"namespace\": \"myapp\",\n \"labels\": {\n \"app\": \"myapp\",\n \"component\": \"jobscheduler\"\n }\n },\n \"spec\": {\n \"ports\": [\n {\n \"name\": \"http\",\n \"port\": 3000,\n \"protocol\": \"TCP\",\n \"targetPort\": \"http\"\n }\n ],\n \"selector\": {\n \"app\": \"myapp\",\n \"component\": \"jobscheduler\"\n }\n }\n }\n ]\n}\n"
builtins.readFile (yamlToJsonFile ./tests/server-cert.yml)
"{\n \"apiVersion\": \"cert-manager.io/v1\",\n \"kind\": \"Certificate\",\n \"metadata\": {\n \"name\": \"mypp-serverauth\",\n \"namespace\": \"myapp\"\n },\n \"spec\": {\n
\"issuerRef\": {\n \"kind\": \"ClusterIssuer\",\n \"name\": \"letsencrypt-dns\"\n },\n \"secretName\": \"myapp-server-tls\",\n \"usages\": [\n \"server auth\"\n
],\n \"dnsNames\": [\n \"myapp.mydomain.com\"\n ]\n }\n}\n"
implementation
{
yamlToJsonFile = args: wrapF args _yamlToJsonFile;
# Turns some YAML content describing ONE OR MORE kubernetes resources
# into a SINGLE JSON file in the store.
# In case of more than one resource the default output is a JSON ARRAY (not an object).
# Call the function with "object" as an outputType and an object with the following structure
# will be returned: { "items": [ {...}, {...}, ... ] }
_yamlToJsonFile =
{
source,
outputType ? "array",
}:
let
jqReturnValue =
if outputType == "array" then
"."
else if outputType == "object" then
"{ items:. }"
else
throw "Unknown output type ${outputType}";
jqCommand = "${pkgs.jq}/bin/jq -n '[inputs] | if length == 1 then .[0] else ${jqReturnValue} end | .'";
in
pkgs.stdenv.mkDerivation {
name = "yaml2jsonfile.json";
inherit source;
passAsFile = [ "source" ];
phases = [ "installPhase" ];
installPhase = "${pkgs.yq-go}/bin/yq $sourcePath -p yaml -o json | ${jqCommand} > $out";
};
}
Wrapped. The purpose of this function is to automate the creation of a set of files compatible with the Kubernetes AddonManager which is the default way of installing addons if you’re running Kubernetes bare metal on NixOS.
It turns YAML source describing ONE OR MORE Kubernetes resources into as many JSON manifests as resources described. The return value is the store path of the directory containing built files.
yamlToMultiJsonFiles :: <wrapped> -> Derivation
yamlToMultiJsonFiles, by default, uses the following yq expression to compute filenames:
.metadata.name + "-" + (.kind | downcase) + "-" + (.metadata.namespace // (.kind | downcase))
Files in the directory are created as follows:
-
The filename is, by default, the string interpolation of resource name, resource kind and resource namespace. In case of a non-namespaced resource, the kind is repeated. Customizable through yqExpression.
-
The value is the content of the file itself.
As an input it supports an Attrset of:
-
yamlContent: YAML content as string
-
yqExpression: yqExpression override in case you want a different naming.
example
builtins.attrNames (builtins.readDir ( yamlToMultiJsonFiles { source = builtins.readFile ./services.yml; } ))
[
"myapp-jobscheduler-service-myapp.json"
"myapp-service-myapp.json"
]
implementation
{
yamlToMultiJsonFiles = args: wrapF args _yamlToMultiJsonFiles;
# Turns some YAML content describing ONE OR MORE Kubernetes resources
# into as many JSON manifests as resources described. The RETURN VALUE is
# the STORE PATH to the directory containing built files.
# This function is useful for directly working with Kubernetes AddonManager.
_yamlToMultiJsonFiles =
{
source,
yqExpression ? null,
}:
let
yqExpr =
if yqExpression == null then
".metadata.name + \"-\" + (.kind | downcase) + \"-\" + (.metadata.namespace // (.kind | downcase))"
else
yqExpression;
in
pkgs.stdenv.mkDerivation {
name = "yaml2multijsonfile";
inherit source;
passAsFile = [ "source" ];
phases = [ "buildPhase" ];
buildPhase = ''
mkdir $out
cd $out
${pkgs.yq-go}/bin/yq -p yaml -o json -s '${yqExpr}' $sourcePath
'';
};
}
It turns YAML source describing ONE OR MORE Kubernetes resources into as many JSON manifests as resources described. The return value is a list of absolute store paths containing built files. This function, unlike yamlToMultiJsonFiles, returns the list of built files.
It does store data in the nix store.
It uses yamlToMultiJsonFiles under the hood, inputs and logic are the same.
yamlToMultiJsonFilePaths :: <wrapped> -> [String]
example
yamlToMultiJsonFilePaths [ ./tests/services.yml { source = ./tests/server-cert.yml; yqExpression = ".metadata.name + \"_\" + (.kind | downcase)"; } ]
[
"/nix/store/i91miasj2chzch49vnamc8ks0s5sndky-yaml2multijsonfile/myapp-jobscheduler-service-myapp.json"
"/nix/store/i91miasj2chzch49vnamc8ks0s5sndky-yaml2multijsonfile/myapp-service-myapp.json"
"/nix/store/hwfnzlkhxapilgmp7m2b9v5ngb87ryv1-yaml2multijsonfile/mypp-serverauth_certificate.json"
]
implementation
{
# Same as yamlToMultiJsonFiles but the RETURN VALUE is a
# list of ABSOLUTE paths to JSON files.
yamlToMultiJsonFilePaths =
args:
let process = as: getGeneratedFiles (wrapF as _yamlToMultiJsonFiles);
in
if isList args
then lib.lists.flatten (map process args)
else process args;
}
Wrapped. Loads and parses YAML definitions into Nix.
It does store data in the nix store.
yamlToNix :: <wrapped> -> Attrset / [Attrset]
example
yamlToNix (builtins.readFile ./server-cert.yml)
{
apiVersion = "cert-manager.io/v1";
kind = "Certificate";
metadata = {
name = "mypp-serverauth";
namespace = "myapp";
};
spec = {
dnsNames = [ "myapp.mydomain.com" ];
issuerRef = {
kind = "ClusterIssuer";
name = "letsencrypt-dns";
};
secretName = "myapp-server-tls";
usages = [ "server auth" ];
};
}
yamlToNix [ (builtins.readFile ./tests/services.yml) ./tests/server-cert.yml ]
[
{
apiVersion = "v1";
kind = "Service";
metadata = {
labels = {
app = "myapp";
component = "main";
};
name = "myapp";
namespace = "myapp";
};
spec = {
ports = [
{
name = "http";
port = 80;
protocol = "TCP";
targetPort = "http";
}
{
name = "https";
port = 443;
protocol = "TCP";
targetPort = "https";
}
];
selector = {
app = "myapp";
components = "main";
};
};
}
{
apiVersion = "v1";
kind = "Service";
metadata = {
labels = {
app = "myapp";
component = "jobscheduler";
};
name = "myapp-jobscheduler";
namespace = "myapp";
};
spec = {
ports = [
{
name = "http";
port = 3000;
protocol = "TCP";
targetPort = "http";
}
];
selector = {
app = "myapp";
component = "jobscheduler";
};
};
}
{
apiVersion = "cert-manager.io/v1";
kind = "Certificate";
metadata = {
name = "mypp-serverauth";
namespace = "myapp";
};
spec = {
dnsNames = [ "myapp.mydomain.com" ];
issuerRef = {
kind = "ClusterIssuer";
name = "letsencrypt-dns";
};
secretName = "myapp-server-tls";
usages = [ "server auth" ];
};
}
]
implementation
{
# Converts YAML content (object or list) to Nix. Evaluates to a list anyway if the
# input is a list of objects.
yamlToNix =
args:
let
json_s = yamlToJson args;
in
if isList json_s
then lib.lists.flatten (map fromJSON json_s)
else fromJSON json_s;
}
Wrapped. Same as yamlToNix but forces the output to be a List.
It does store data in the nix store.
yamlToNixList :: <wrapped> -> [Attrset]
example
yamlToNixList (builtins.readFile ./server-cert.yml)
[
{
apiVersion = "cert-manager.io/v1";
kind = "Certificate";
metadata = {
name = "mypp-serverauth";
namespace = "myapp";
};
spec = {
dnsNames = [ "myapp.mydomain.com" ];
issuerRef = {
kind = "ClusterIssuer";
name = "letsencrypt-dns";
};
secretName = "myapp-server-tls";
usages = [ "server auth" ];
};
}
]
implementation
{
# Converts YAML content to a Nix list forcing the output to be a list.
# So even if a single object is passed the result will be a Nix list
# with a single Attrset in it.
yamlToNixList =
args:
lib.lists.flatten [ (yamlToNix args) ];
}
Converts Nix code to YAML string.
It does store data in the nix store.
nixToYaml :: (Attrset /List / String) -> String
example
builtins.readFile (nixToYaml { a = 1; b = 2; c = 3; })
"a: 1\nb: 2\nc: 3\n"
builtins.readFile (nixToYaml [{ a = 1;} { b = 2;} {c = 3;}])
"- a: 1\n- b: 2\n- c: 3\n"
implementation
{
# Converts Nix to YAML.
nixToYaml =
data:
let
j = toJSON data;
in
pkgs.stdenv.mkDerivation {
inherit j;
name = "nixtoYaml";
passAsFile = [ "j" ];
phases = [ "buildPhase" ];
buildPhase = "${pkgs.yq-go}/bin/yq -p json -o yaml $jPath > $out";
};
}
Wrapped. Turns JSON source into YAML string.
It does store data in the nix store.
jsonToYaml :: <wrapped> -> String
The input is an Attrset of:
-
jsonContent: JSON string data.
-
topLevelKey: by default a JSON array is converted to a bare YAML array. This allows you to turn it into an object specifying the key the array should be put under. It’s ineffective if the content is not a list.
example
jsonToYaml { jsonContent = builtins.readFile ./services.json; topLevelKey = "services"; }
"services:\n - apiVersion: v1\n kind: Service\n metadata:\n name: myapp\n namespace: myapp\n labels:\n app: myapp\n component: main\n spec:\n ports:\n - name: http\n port: 80\n protocol: TCP\n targetPort: http\n - name: https\n port: 443\n protocol: TCP\n targetPort: https\n selector:\n app: myapp\n components: main\n - apiVersion: v1\n kind: Service\n metadata:\n name: myapp-jobscheduler\n namespace: myapp\n labels:\n app: myapp\n component: jobscheduler\n spec:\n ports:\n - name: http\n port: 3000\n protocol: TCP\n targetPort: http\n selector:\n app: myapp\n component: jobscheduler\n"
implementation
{
# Turns JSON source into YAML string
jsonToYaml =
args:
let
result = wrapF args _jsonToYamlFile;
in
handleResult result readFile;
}
Wrapped. Same as jsonToYaml but stores the YAML content in a file.
It does store data in the nix store.
jsonToYamlFile :: <wrapped> -> Derivation
example
builtins.readFile (jsonToYamlFile { jsonContent = builtins.readFile ./server-cert.json; topLevelKey = "services";})
"apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n name: mypp-serverauth\n namespace: myapp\nspec:\n issuerRef:\n kind: ClusterIssuer\n name: letsencrypt-dns\n secretName: myapp-server-tls\n usages:\n - server auth\n dnsNames:\n - myapp.mydomain.com\n"
implementation
{
jsonToYamlFile =
args:
handleResult (wrapF args _jsonToYamlFile) null;
_jsonToYamlFile =
{
source,
topLevelKey ? null,
}:
let
jsonIsList = j: isList (fromJSON j);
in
pkgs.stdenv.mkDerivation rec {
name = "json2yaml";
inherit source topLevelKey;
passAsFile = [ "source" ];
phases = [ "installPhase" ];
yqTransform =
if topLevelKey != null && jsonIsList source then "--expression '{ \"${topLevelKey}\":. }'" else "";
installPhase = "${pkgs.yq-go}/bin/yq $sourcePath -p json -o yaml ${yqTransform} > $out";
};
}