Namespaces

This example demonstrates the use of kubenix submodules, which are built atop of nixos submodule system, to create resources in multiple namespaces.

    default.nix
    
{ kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} {
  module = { kubenix, ... }: {
    imports = [ ./module.nix ];

    kubenix.project = "multi-namespace-example";
    kubernetes.version = "1.24";
  };
}

Here’s a definition of a submodule.

    namespaced.nix
    
{ config
, kubenix
, lib
, # Name of submodule instance.
  name
, # This is a shorthand for config.submodule.args and contains
  # final values of the args options.
  args
, ...
}: {
  imports = with kubenix.modules; [
    # This needs to be imported in order to define a submodule.
    submodule
    # Importing this so that we can set config.kubernetes
    # within the context of this submodule.
    k8s
  ];

  # Args are used to pass information from the parent context.
  options.submodule.args = {
    kubernetes = lib.mkOption {
      description = "Kubernetes config to be applied to a specific namespace.";
      # We are not given a precise type to this since we are using it
      # to set kubernetes options from the k8s module which are already
      # precisely typed.
      type = lib.types.attrs;
      default = { };
    };
  };

  config = {
    submodule = {
      # Used to uniquely identify a submodule. Used to select submodule
      # "prototype" when instantiating.
      name = "namespaced";

      # Passthru allows a submodule instance to set config of the parent
      # context. It's not strictly required but it's useful for combining
      # outputs of multiple submodule instances, without having to write
      # ad hoc code in the parent context.

      # NOTE: passthru has not effect if given options are not defined
      # in the parent context. Therefore in this case we are expecting that
      # parent imports kubinex.k8s module.

      # Here we set kubernetes.objects.
      # This is a list so even if distinct instances of the submodule contain
      # definitions of identical api resources, these will not be merged or
      # cause conflicts. Lists of resources from multiple submodule instances
      # will simply be concatenated.
      passthru.kubernetes.objects = config.kubernetes.objects;
    };

    kubernetes = lib.mkMerge [
      # Use instance name as namespace
      { namespace = name; }
      # Create namespace object
      { resources.namespaces.${name} = { }; }
      # All resources defined here will use the above namespace
      args.kubernetes
    ];
  };
}

And here’s how it can be used.

    module.nix
    
{ config, lib, pkgs, kubenix, ... }: {
  imports = with kubenix.modules; [ submodules k8s ];

  # Import submodule.
  submodules.imports = [
    ./namespaced.nix
  ];

  # We can now create multiple submodule instances.
  submodules.instances.namespace-http = {
    #                  ~~~~~~~~~~~~~~
    #                          ^
    #  ╭-----------------------╯
    # The submodule instance name is injected as the name attribute to
    # the submodule function. In this example, it is used as the namespace
    # name.
    #
    # This needs to match config.submodule.name of an imported submodule.
    submodule = "namespaced";
    # Now we can set the args options defined in the submodule.
    args.kubernetes.resources = {
      services.nginx.spec = {
        ports = [{
          name = "http";
          port = 80;
        }];
        selector.app = "nginx";
      };
    };
  };

  submodules.instances.namespace-https = {
    submodule = "namespaced";
    args.kubernetes.resources = {
      services.nginx.spec = {
        ports = [{
          name = "https";
          port = 443;
        }];
        selector.app = "nginx";
      };
    };
    # Example of how other defaults can be applied to resources
    # within a submodule.
    args.kubernetes.version = "1.26";
  };

  # Resources defined in parent context use namespace set at this
  # level and are not affected by the above submodules.
  kubernetes.namespace = "default";
  kubernetes.resources.services.nginx.spec = {
    selector.app = "nginx-default";
  };
}