Skip to content

Alfablos/nix-kubelib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kubelib: Nix utilities for Kubernetes

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.

Dependencies

  • 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

Lib

The library has 2 main focuses: one is handling YAML, JSON and Nix conversions, while the other is managing Helm Charts.

YAML Lib

Table 1. -: self type, builtin: builtin in Nix
Input type YAML JSON JSON file Multiple JSON files Nix Nix List

YAML content (1 object)

-

yamlToJson

yamlToJsonFile

yamlToMultiJsonFiles

yamlToNix

yamlToNix

YAML content (multiple objects)

-

yamlToJson

yamlToJsonFile

yamlToMultiJsonFiles

yamlToNix

yamlToNixList

YAML file (single object)

-

yamlToJson

yamlToJsonFile

yamlToMultiJsonFiles

yamlToNix

yamlToNixList

YAML file (multiple objects)

-

yamlToJson

yamlToJsonFile

yamlToMultiJsonFiles

yamlToNix

yamlToNixList

Nix

nixToYaml

builtin

builtin

no

-

-

JSON content

jsonToYaml jsonToYamlFile

-

-

no

builtin

builtin

JSON file

jsonToYamlFile

-

-

no

builtin

builtin

Table 2. Additional utils
toKubernetesList Turns a Nix list into the Nix equivalent of a Kubernetes resource list (kind: List).

keyValFromJsonManifest

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.

keyValFromJsonManifestFiles

Sane as keyValFromJsonManifest but allows multiple paths as input.

From YAML functions:

From JSON functions:

From Nix functions:

Others:

Functions Naming

Where possible the following naming convention is used: ($inputFormat)To($outputFormat)(?Files?)

Functions Signatures

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;
}

Helm lib

  • downloadHelmChart

  • buildHelmChart

Lib Functions

toKubernetesList

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 {}));
}

keyValFromJsonManifest

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;
}

yamlToJson

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);
}

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";
    };
}

yamlToMultiJsonFiles

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
      '';
    };
}

yamlToMultiJsonFilePaths

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;
}

yamlToNix

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;
}

yamlToNixList

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) ];
}

nixToYaml

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";
    };
}

jsonToYaml

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;
}

jsonToYamlFile

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";
    };
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages