Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save felixscheinost/9e7fc4370fe1ce2dfbe6efb0e75d4f4f to your computer and use it in GitHub Desktop.
Save felixscheinost/9e7fc4370fe1ce2dfbe6efb0e75d4f4f to your computer and use it in GitHub Desktop.
This provides a way have kubenix recognize custom CRD form e.g. Helm charts including those CRDs.
{ kubenix, pkgs, lib, kubenixPath, config, symKubeLib, ... }:
let
# Evaluate the module which contains CRDs
evaluatedObjects = (kubenix.evalModules {
module = { kubenix, ... }: {
imports = with kubenix.modules; [
k8s
helm
# Could be e.g. a Helm chart that incldues CRDs (Don't forget `includeCRDs = true`)
<YOUR MODULES WITH CRDs here>
];
};
specialArgs.symKubeLib = symKubeLib;
}).config.kubernetes.objects;
# Filter evaluated objects to only extract CRDs
crdsFromEvaluation =
builtins.filter (o: o.kind == "CustomResourceDefinition") evaluatedObjects;
# A single CRD objects can contain multiple versions. Flatten them.
schemasFlattened = let
processCrdVersion = crd: version: {
group = crd.spec.group;
version = version.name;
kind = crd.spec.names.kind;
attrName = crd.spec.names.plural;
fqdn = "${crd.spec.group}.${version.name}.${crd.spec.names.kind}";
schema = version.schema.openAPIV3Schema;
};
processCrd = crd:
builtins.map (v: processCrdVersion crd v) crd.spec.versions;
in builtins.concatMap processCrd crdsFromEvaluation;
# Same structure as e.g. https://raw.githubusercontent.com/kubernetes/kubernetes/v1.26.0/api/openapi-spec/swagger.json
# {
# "definitions": {
# "<CRD 1 FQDN>": <OpenAPI schema>,
# "<CRD 2 FQDN>": <OpenAPI schema>
# },
# "paths": {}
# }
allCrdsOpenApiDefinition = pkgs.writeTextFile {
name = "generated-kubenix-crds-schema.json";
text = builtins.toJSON {
definitions = builtins.listToAttrs (builtins.map (x: {
name = x.fqdn;
value = x.schema;
}) schemasFlattened);
paths = { };
};
};
# Call the kubenix generator to generate resource definitins from our custom CRDs
# This is a bit hacky as it requires IFD
generated = import "${kubenixPath}/jobs/generators/k8s" {
name = "kubenix-generated-for-crds";
inherit pkgs lib;
# Warn: IFD
spec = "${allCrdsOpenApiDefinition}";
};
# Warn: IFD 2nd time
definitions = (import "${generated}" {
inherit config lib;
options = null;
}).config.definitions;
in {
kubernetes.customTypes = builtins.map (crdVersion: {
inherit (crdVersion) group version kind attrName;
module = lib.types.submodule (definitions."${crdVersion.fqdn}");
}) schemasFlattened;
}
@felixscheinost
Copy link
Author

Ah sorry, I missed that @Albitos

In my flake.nix (or other .nix file if not using flakes) I pass the path to kubenix as a specialArg.

    # `kubenix` would be a flake input or the result of `import (builtins.fetchGit { ... })` when not using flakes
    kubenix.evalModules.${system} {
        module = { kubenix, ... }: {
            ...
       }
       specialArgs = {
           kubenixPath = "${kubenix}";
       }
    }

@Albitos
Copy link

Albitos commented Mar 23, 2024

@felixscheinost Just wanted you to know that I finally managed to make it work, and it's spectacular 😄 Now I can define databases for my home server using db-operator. Thanks!

@schradert
Copy link

This was failing for me for the same reason motivating this solution in the evaluatedObjects module evaluation until I added the following:

options.kubernetes.api = lib.mkOption {
  type = lib.types.submodule {freeformType = with lib.types; attrsOf anything;};
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment