hall / kubenix

Kubernetes management with Nix
https://kubenix.org/
MIT License
319 stars 30 forks source link

Default namespace is incorrectly applied to clusterwide resources #69

Open nicolasbernard opened 5 months ago

nicolasbernard commented 5 months ago

Hello,

Defining kubernetes.namespace is a practical way to set the namespace to all resources but it's also applied to ones which shouldn't have it.

let
  kubenix = import (builtins.fetchGit {
    url = "https://github.com/hall/kubenix.git";
    ref = "main";
  });
in
  (kubenix.evalModules.${builtins.currentSystem} {
    module = {kubenix, ...}: {
      imports = [
        kubenix.modules.k8s
        { kubernetes.resources.namespaces.a-namespace = {}; }
       ];
      kubernetes.namespace = "an-other-namespace";
    };
  })
  .config
  .kubernetes
  .objects
$ nix eval -f poc.nix --json | jq .
[
  {
    "apiVersion": "v1",
    "kind": "Namespace",
    "metadata": {
      "annotations": {
        "kubenix/k8s-version": "1.30",
        "kubenix/project-name": "kubenix"
      },
      "name": "a-namespace",
      "namespace": "an-other-namespace"
    }
  }
]

Kubenix should be aware of namespaced/clusterwide resources to decide if the namespace is needed.

I'm not familiar enough with the codebase to open a PR but I can probably do it with some direction pointing :) .

pizzapim commented 4 months ago

I haven't looked in the code, but looked at the swagger API JSON which Kubenix consumes. It seems this information is curiously not present in the resource definitions, which shows that a pod and a namespace has the same metadata definition:

"io.k8s.api.core.v1.Pod": {
  "properties": {
    "metadata": {
      "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta",
      "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
    },
  },
},
"io.k8s.api.core.v1.Namespace": {
  "properties": {
    "metadata": {
      "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta",
      "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
    },
  },
},

Perhaps we can the swagger API paths to determine whether an object is namespaced?

"paths": {
  "/api/v1/namespaces": { },
  "/api/v1/namespaces/{namespace}/pods": { },
},

Does seem a bit hacky though... Another angle might be to determine how kubectl does this:

$ kubectl api-resources                                                                                                                                               
NAME                                SHORTNAMES           APIVERSION                        NAMESPACED   KIND
bindings                                                 v1                                true         Binding
componentstatuses                   cs                   v1                                false        ComponentStatus
configmaps                          cm                   v1                                true         ConfigMap
endpoints                           ep                   v1                                true         Endpoints
events                              ev                   v1                                true         Event
limitranges                         limits               v1                                true         LimitRange
namespaces                          ns                   v1                                false        Namespace
nodes                               no                   v1                                false        Node
persistentvolumeclaims              pvc                  v1                                true         PersistentVolumeClaim
persistentvolumes                   pv                   v1                                false        PersistentVolume
pizzapim commented 4 months ago

BTW, is there any problem you encounter when you specify the namespace on resources that don't need it? Take this example:

apiVersion: v1
kind: Namespace
metadata:
  name: test
  namespace: default

It is created fine:

$ kubectl create -f ./ns.yaml
namespace/test created

The namespace is simply removed:

apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: "2024-07-13T13:47:53Z"
  labels:
    kubernetes.io/metadata.name: test
  name: test
  resourceVersion: "23410350"
  uid: 1506b413-dbf6-45a5-aaf1-f6a28631641c
spec:
  finalizers:
  - kubernetes
status:
  phase: Active