kubernetes: client config as portable home-manager user module (#35) #37

Open
hermes-bot wants to merge 6 commits from issue-35-kube-client-user-module into main
Member

Closes #35.

Adds users/modules/kubernetes.nix, a home-manager user module that re-implements the kubernetes.client system module so the config is portable to the work system. The module uses only stock home-manager arguments (config, lib, pkgs) and no repo-internal imports, so it can be dropped into any home-manager config standalone:

modules = [ (inputs.pickler-nixos + /users/modules/kubernetes.nix) ];

kubernetes.client = {
  enable = true;
  # either declare the config in Nix (cert fields point at secret paths, e.g. sops):
  kubeconfig = { apiVersion = "v1"; kind = "Config"; clusters = [ ... ]; };
  # ...or symlink an externally-managed file (work secret manager, sops template):
  kubeconfigFile = config.sops.templates."work.kubeconfig".path;
};

Design notes:

  • Same option namespace as the system module (kubernetes.client.*) so host and user configs read the same way. kubeconfig and kubeconfigFile are mutually exclusive (module assertion).
  • Default packages: kubectl, kubernetes-helm, kubelogin-oidc. The cfssl/certmgr/openssl trio from the system module is left system-level (cluster-admin cert tooling, not client tooling); still overridable via packages.
  • kubeconfig attrs render via pkgs.formats.yaml — note pkgs.formats.kubernetes no longer exists in nixpkgs (went away with the removed kubernetes service modules), and HM's programs.kubectl completion module was likewise removed upstream; shell completion is kubectl completion <shell> / package-provided now.
  • Wired into rpickler's portable module list (enabled on every host). kubeconfig defaults to null, so nothing writes ~/.kube/config until you declare it — no conflict with existing hand-made kubeconfigs.
  • System module left untouched for headless hosts without home users; hosts can later drop kubernetes.client.enable (system) once the user layer covers them.

Verified with the repo's locked nixpkgs (597283a) + home-manager release-26.05 (24f8f8e):

  • standalone evalModules harness: kubeconfig attrs render a valid YAML kubeconfig to ~/.kube/config; enable/packages/symlink/mutual-exclusion assertion all behave;
  • nix eval .#nixosConfigurations.kansas.config.home-manager.users.rpickler.kubernetes.client.enable → true, user profile carries kubectl, and the rest of rpickler's HM file set evaluates unchanged.
Closes #35. Adds `users/modules/kubernetes.nix`, a home-manager **user** module that re-implements the `kubernetes.client` system module so the config is portable to the work system. The module uses only stock home-manager arguments (`config, lib, pkgs`) and no repo-internal imports, so it can be dropped into any home-manager config standalone: ```nix modules = [ (inputs.pickler-nixos + /users/modules/kubernetes.nix) ]; kubernetes.client = { enable = true; # either declare the config in Nix (cert fields point at secret paths, e.g. sops): kubeconfig = { apiVersion = "v1"; kind = "Config"; clusters = [ ... ]; }; # ...or symlink an externally-managed file (work secret manager, sops template): kubeconfigFile = config.sops.templates."work.kubeconfig".path; }; ``` Design notes: - Same option namespace as the system module (`kubernetes.client.*`) so host and user configs read the same way. `kubeconfig` and `kubeconfigFile` are mutually exclusive (module assertion). - Default packages: `kubectl`, `kubernetes-helm`, `kubelogin-oidc`. The cfssl/certmgr/openssl trio from the system module is left system-level (cluster-admin cert tooling, not client tooling); still overridable via `packages`. - kubeconfig attrs render via `pkgs.formats.yaml` — note `pkgs.formats.kubernetes` no longer exists in nixpkgs (went away with the removed kubernetes service modules), and HM's `programs.kubectl` completion module was likewise removed upstream; shell completion is `kubectl completion <shell>` / package-provided now. - Wired into rpickler's portable module list (enabled on every host). `kubeconfig` defaults to null, so nothing writes `~/.kube/config` until you declare it — no conflict with existing hand-made kubeconfigs. - System module left untouched for headless hosts without home users; hosts can later drop `kubernetes.client.enable` (system) once the user layer covers them. Verified with the repo's locked nixpkgs (597283a) + home-manager release-26.05 (24f8f8e): - standalone `evalModules` harness: kubeconfig attrs render a valid YAML kubeconfig to `~/.kube/config`; enable/packages/symlink/mutual-exclusion assertion all behave; - `nix eval .#nixosConfigurations.kansas.config.home-manager.users.rpickler.kubernetes.client.enable` → `true`, user profile carries kubectl, and the rest of rpickler's HM file set evaluates unchanged.
Re-implements the kubernetes.client system module at the user layer so
it is portable to systems outside this repo (e.g. the work machine):

- users/modules/kubernetes.nix declares kubernetes.client.{enable,
  packages,kubeconfig,kubeconfigFile} using only stock home-manager
  arguments, so it can be imported standalone from any config
- kubeconfig attrs render to ~/.kube/config via pkgs.formats.yaml;
  kubeconfigFile symlinks an externally-managed file (sops templates,
  work secret managers) instead; mutually exclusive via assertion
- wired into rpickler's portable module list (enabled on every host)

The system module stays as-is for headless hosts without home users.

Co-Authored-By: Hermes Agent <hermes-bot@richardpickler.invalid>
Owner

I'm going to need help with a few things on this review.

  • I need the kubernetes pem secret as part of user space sops
  • I don't know how to do this, I have only stubbed out some sops based secrets
  • I want .kube/config fully generated. It's set to null here, so it just uses the existing one from my home directory

Let me know what you need from me, and I can slot in the changes you need from me (secret in particular)

I'm going to need help with a few things on this review. * I need the kubernetes pem secret as part of user space sops * I don't know how to do this, I have only stubbed out some sops based secrets * I want .kube/config fully generated. It's set to null here, so it just uses the existing one from my home directory Let me know what you need from me, and I can slot in the changes you need from me (secret in particular)
Declare the home-cluster kubeconfig (local / https://lurch.lan:6443 /
cluster-admin, same shape as the system module's commented template) via
the kubernetes user module, with the three PEMs sourced from user sops
secrets kube/{certificate-authority,client-certificate,client-key}. Only
secret paths render into ~/.kube/config; force replaces any pre-existing
hand-made config. Gated to @sausage/@kansas: those are the hosts whose
user age key is a recipient of modules/secrets.yaml.

Verified: kansas eval current-context=local, generated kubeconfig builds,
pumpkin stays kubeconfig=null (tooling only).
Author
Member

All three points implemented in 896dd3d — ~/.kube/config is now fully generated on kansas and sausage (no more null / fallback to the hand-made file).

What the code does now

  • users/users/rpickler.nix gained a kubernetes-home-kubeconfig module: declares the home cluster (local, https://lurch.lan:6443, cluster-admin — same shape as the commented template in the system module) as kubernetes.client.kubeconfig attrs, with the three PEM fields pointing at user-space sops secrets kube/certificate-authority, kube/client-certificate, kube/client-key. Only the secret paths render into ~/.kube/config; the key material never touches the Nix store.
  • Gated to @sausage / @kansas deliberately: those are the only hosts whose user age key is a recipient of modules/secrets.yaml (rpicklerAtSausage / rpicklerAtKansas in modules/keys.sops.yaml). Other hosts (pumpkin, vm) keep tooling only — verified kubeconfig = null there.
  • home.file.".kube/config".force = true replaces the existing hand-made config on the first switch. Back it up first (cp ~/.kube/config ~/.kube/config.bak); if it holds clusters beyond local, tell me and I'll add them to the attrs.

Verified with the repo's locked nixpkgs: nix eval .#nixosConfigurations.kansas.config.home-manager.users.rpickler.kubernetes.client.kubeconfig."current-context" → local, the generated kubeconfig derivation builds to valid YAML with sops paths only, pumpkin still evaluates clean with kubeconfig = null.

What I need from you — the three PEMs in modules/secrets.yaml (the same file the user-sops stub already points at). Do this on kansas, where your user SSH key is a recipient:

# 1. convert your SSH key to an age key (matches how sops-nix decrypts at runtime)
nix run nixpkgs#ssh-to-age -- -private-key -i ~/.ssh/id_ed25519 > /tmp/age.key

# 2. open the repo's secrets file with that identity
SOPS_AGE_KEY_FILE=/tmp/age.key nix run nixpkgs#sops -- modules/secrets.yaml

Add three multi-line strings at top level — grab the values from your current kubeconfig (kubectl config view --flatten shows them base64-embedded; base64 -d decodes):

kube:
  certificate-authority: |-
    -----BEGIN CERTIFICATE-----
    ...
  client-certificate: |-
    -----BEGIN CERTIFICATE-----
    ...
  client-key: |-
    -----BEGIN ... PRIVATE KEY-----
    ...

Saving re-encrypts for the existing recipient list automatically; the new keys are inert for everything else until declared, which the module above does. Then commit + push modules/secrets.yaml and rebuild as usual — order matters only in that the first switch on kansas/sausage will fail until the keys are in the file.

The generated file lands at ~/.kube/config with the cert fields pointing at the sops-nix user secret paths (/run/user/$UID/secrets/kube/...), so kubectl works unmodified.

All three points implemented in `896dd3d` — `~/.kube/config` is now fully generated on kansas and sausage (no more `null` / fallback to the hand-made file). **What the code does now** - `users/users/rpickler.nix` gained a `kubernetes-home-kubeconfig` module: declares the home cluster (`local`, `https://lurch.lan:6443`, `cluster-admin` — same shape as the commented template in the system module) as `kubernetes.client.kubeconfig` attrs, with the three PEM fields pointing at user-space sops secrets `kube/certificate-authority`, `kube/client-certificate`, `kube/client-key`. Only the secret *paths* render into `~/.kube/config`; the key material never touches the Nix store. - Gated to `@sausage` / `@kansas` deliberately: those are the only hosts whose *user* age key is a recipient of `modules/secrets.yaml` (`rpicklerAtSausage` / `rpicklerAtKansas` in `modules/keys.sops.yaml`). Other hosts (pumpkin, vm) keep tooling only — verified `kubeconfig = null` there. - `home.file.".kube/config".force = true` replaces the existing hand-made config on the first switch. Back it up first (`cp ~/.kube/config ~/.kube/config.bak`); if it holds clusters beyond `local`, tell me and I'll add them to the attrs. Verified with the repo's locked nixpkgs: `nix eval .#nixosConfigurations.kansas.config.home-manager.users.rpickler.kubernetes.client.kubeconfig."current-context"` → `local`, the generated kubeconfig derivation builds to valid YAML with sops paths only, pumpkin still evaluates clean with `kubeconfig = null`. **What I need from you** — the three PEMs in `modules/secrets.yaml` (the same file the user-sops stub already points at). Do this on kansas, where your user SSH key is a recipient: ```bash # 1. convert your SSH key to an age key (matches how sops-nix decrypts at runtime) nix run nixpkgs#ssh-to-age -- -private-key -i ~/.ssh/id_ed25519 > /tmp/age.key # 2. open the repo's secrets file with that identity SOPS_AGE_KEY_FILE=/tmp/age.key nix run nixpkgs#sops -- modules/secrets.yaml ``` Add three multi-line strings at top level — grab the values from your current kubeconfig (`kubectl config view --flatten` shows them base64-embedded; `base64 -d` decodes): ```yaml kube: certificate-authority: |- -----BEGIN CERTIFICATE----- ... client-certificate: |- -----BEGIN CERTIFICATE----- ... client-key: |- -----BEGIN ... PRIVATE KEY----- ... ``` Saving re-encrypts for the existing recipient list automatically; the new keys are inert for everything else until declared, which the module above does. Then commit + push `modules/secrets.yaml` and rebuild as usual — order matters only in that the first `switch` on kansas/sausage will fail until the keys are in the file. The generated file lands at `~/.kube/config` with the cert fields pointing at the sops-nix user secret paths (`/run/user/$UID/secrets/kube/...`), so `kubectl` works unmodified.
Owner

For the sops secrets, I meant separate home-manager based secrets. I see my home manager currently points to the same modules/secrets.yaml, but my intention is to separate the home-manager user setup from this repository. Therefore, this secret will need to live in a different secrets file. This means the user/modules/sops.nix file will need to change.

For the sops secrets, I meant separate home-manager based secrets. I see my home manager currently points to the same modules/secrets.yaml, but my intention is to separate the home-manager user setup from this repository. Therefore, this secret will need to live in a different secrets file. This means the user/modules/sops.nix file will need to change.
Per review: the home layer must not share modules/secrets.yaml so the
home-manager setup can be extracted from this repo. users/modules/sops.nix
now defaults to users/secrets.yaml; the kube/* PEM keys live there as
placeholders (fill via 'sops edit users/secrets.yaml'), encrypted for the
same 11 age recipients as modules/secrets.yaml. restic-passphrase stays in
the system file for now via an explicit sopsFile override with a
home-split TODO (its plaintext cannot be moved without the decrypt key).

Verified: kansas kubeconfig current-context=local, kube/client-key
sopsFile=users/secrets.yaml, restic-passphrase sopsFile=modules/secrets.yaml,
pumpkin kubeconfig=null.
Author
Member

Separation done in a1c48db — home-manager no longer reads modules/secrets.yaml.

What changed

  • users/modules/sops.nix: defaultSopsFile now points at a new users/secrets.yaml, inside the users/ tree, so the whole home layer (modules + secrets file) moves out of this repo as one unit when you split it. The module comment documents sops updatekeys users/secrets.yaml for re-encrypting to the work recipients at that point.
  • users/secrets.yaml is committed already sops-encrypted (3.13.3, same as the repo file) to the same 11 age recipients as modules/secrets.yaml — the rpicklerAtSausage/rpicklerAtKansas user keys keep working unchanged. It contains kube/certificate-authority, kube/client-certificate, kube/client-key as placeholders so eval and activation build; nothing decrypts to real material until you fill them.
  • backup.nix's restic-passphrase is the one entry still living in modules/secrets.yaml — its plaintext can't be moved without the decrypt key, so it keeps an explicit sopsFile = ../../modules/secrets.yaml override with a TODO(home-split) describing how to move the value when you extract the home setup. That override is the only remaining coupling between users/ and the system secrets.

Your one step — put the real PEMs in the new file (kansas, with your user key as before):

SOPS_AGE_KEY_FILE=/tmp/age.key nix run nixpkgs#sops -- users/secrets.yaml

Replace the three PLACEHOLDER-... blocks (values from kubectl config view --flatten, base64 -d the certificate-authority-data / client-certificate-data / client-key-data fields). Saving re-encrypts for the existing recipient list automatically. Nothing else to edit — rpickler.nix and the kubernetes user module pick the paths up through the new default.

Verified with the repo's locked nixpkgs: kansas kubernetes.client.kubeconfig."current-context" = local; sops.secrets."kube/client-key".sopsFile resolves to users/secrets.yaml; restic-passphrase.sopsFile still resolves to modules/secrets.yaml; pumpkin stays kubeconfig = null (tooling only). No kube/* keys were ever added to modules/secrets.yaml, so there is nothing to migrate out of it.

Separation done in `a1c48db` — home-manager no longer reads `modules/secrets.yaml`. **What changed** - `users/modules/sops.nix`: `defaultSopsFile` now points at a new **`users/secrets.yaml`**, inside the `users/` tree, so the whole home layer (modules + secrets file) moves out of this repo as one unit when you split it. The module comment documents `sops updatekeys users/secrets.yaml` for re-encrypting to the work recipients at that point. - `users/secrets.yaml` is committed already sops-encrypted (3.13.3, same as the repo file) to the **same 11 age recipients** as `modules/secrets.yaml` — the `rpicklerAtSausage`/`rpicklerAtKansas` user keys keep working unchanged. It contains `kube/certificate-authority`, `kube/client-certificate`, `kube/client-key` as **placeholders** so eval and activation build; nothing decrypts to real material until you fill them. - `backup.nix`'s `restic-passphrase` is the one entry still living in `modules/secrets.yaml` — its plaintext can't be moved without the decrypt key, so it keeps an explicit `sopsFile = ../../modules/secrets.yaml` override with a `TODO(home-split)` describing how to move the value when you extract the home setup. That override is the only remaining coupling between `users/` and the system secrets. **Your one step** — put the real PEMs in the new file (kansas, with your user key as before): ```bash SOPS_AGE_KEY_FILE=/tmp/age.key nix run nixpkgs#sops -- users/secrets.yaml ``` Replace the three `PLACEHOLDER-...` blocks (values from `kubectl config view --flatten`, `base64 -d` the `certificate-authority-data` / `client-certificate-data` / `client-key-data` fields). Saving re-encrypts for the existing recipient list automatically. Nothing else to edit — `rpickler.nix` and the kubernetes user module pick the paths up through the new default. Verified with the repo's locked nixpkgs: kansas `kubernetes.client.kubeconfig."current-context"` = `local`; `sops.secrets."kube/client-key".sopsFile` resolves to `users/secrets.yaml`; `restic-passphrase.sopsFile` still resolves to `modules/secrets.yaml`; pumpkin stays `kubeconfig = null` (tooling only). No `kube/*` keys were ever added to `modules/secrets.yaml`, so there is nothing to migrate out of it.
rpickler's kubernetes-home-kubeconfig now renders ~/.kube/config with a
sops-nix template: OIDC issuer/client-id/client-secret are substituted at
activation from users/secrets.yaml (kube/oidc-* keys), so credential values
never land in the Nix store; the cluster CA stays a secret-path reference.
kubeconfigFile points at the rendered file (0400) and ~/.kube/config links
to it. interactiveMode IfAvailable so a fresh host can do the first browser
login (Never would hard-fail it).

Also reverts the hardcoded OIDC attrset that had slipped into
kubernetes.client.kubeconfig's default on this branch: it referenced
undefined identifiers at module scope (config, sops, kubectl), wired a
nonexistent plugin binary ('oidc-login'), mismatched user vs context, and
would have interpolated decrypted secret VALUES into the Nix store via
pkgs.formats.yaml. Default is null again; a corrected, store-safe OIDC exec
pattern lives in the option example with the template guidance.
Author
Member

Implemented the sops-nix template approach you asked for — commit 1960eb9.

How it works (kubernetes-home-kubeconfig in users/users/rpickler.nix): the whole kubeconfig is a sops.templates."kube-config" whose content is your OIDC exec config with ${config.sops.placeholder."kube/oidc-*"} tokens; sops-nix substitutes the real values from users/secrets.yaml at activation. The rendered file (~/.config/sops-nix/secrets/rendered/kube-config, mode 0400) has zero secret material in the Nix store, and kubernetes.client.kubeconfigFile points at it so ~/.kube/config links to it. The CA stays a secret-path reference (it's public anyway).

Action needed from you: fill the three placeholders via sops edit users/secrets.yaml — kube/oidc-issuer-url, kube/oidc-client-id, kube/oidc-client-secret (plus the real CA PEM in kube/certificate-authority).

Your draft's config, fixed on the way in:

  • command: kubectl → ${pkgs.kubelogin-oidc}/bin/kubectl-oidc_login — the credential plugin is that binary, and it must be an absolute store path (kubectl execs it directly, no PATH).
  • "oidc-login" "get-token" → just "get-token" (the binary name is kubectl-oidc_login).
  • --oidc-extra-scope=profile=email → two args --oidc-extra-scope=profile and --oidc-extra-scope=email (the original passed one scope literally named "profile=email").
  • env + $$OIDC_* indirection → secret values go in the exec args directly; kubelogin has no env fallback for --oidc-client-secret (flag set checked on 1.36.4, and exercised on the pinned 1.36.3), and the template substitution already keeps them out of the store.
  • interactiveMode: Never → IfAvailable: on a freshly switched host the kubelogin token cache is empty and Never hard-fails the first browser login. (IfNeeded is not a valid value — client-go rejects it; valid: Never/IfAvailable/Always.)

Also reverted: the OIDC attrset that had ended up as the default of kubernetes.client.kubeconfig in users/modules/kubernetes.nix. It referenced config/sops/kubectl at module scope where none are bound, used the 'oidc-login' binary, and interpolated decrypted secret values through pkgs.formats.yaml — i.e. straight into the Nix store — defeating the placeholder mechanism. Default is null again; the corrected store-safe pattern is now the option example, with a note pointing secret-embedding configs at templates + kubeconfigFile.

Verified:

  • kansas eval: kubeconfigFile = rendered template path; template content shows /nix/store/…-kubelogin-1.36.3/bin/kubectl-oidc_login, placeholders in the three secret args, CA = /home/rpickler/.config/sops-nix/secrets/kube/certificate-authority; all four kube/* secrets resolve against users/secrets.yaml.
  • pumpkin eval: kubeconfig/kubeconfigFile null, no .kube/config entry (host gate intact).
  • End-to-end with the pinned kubectl 1.36.3: parsed the rendered file (placeholder-substituted) and invoked the plugin — kubelogin accepted every flag and proceeded to OIDC discovery against the (dummy) issuer URL. This also caught the interactiveMode value bug above.
Implemented the sops-nix template approach you asked for — commit 1960eb9. **How it works** (`kubernetes-home-kubeconfig` in `users/users/rpickler.nix`): the whole kubeconfig is a `sops.templates."kube-config"` whose content is your OIDC exec config with `${config.sops.placeholder."kube/oidc-*"}` tokens; sops-nix substitutes the real values from `users/secrets.yaml` at activation. The rendered file (`~/.config/sops-nix/secrets/rendered/kube-config`, mode 0400) has zero secret material in the Nix store, and `kubernetes.client.kubeconfigFile` points at it so `~/.kube/config` links to it. The CA stays a secret-path reference (it's public anyway). **Action needed from you:** fill the three placeholders via `sops edit users/secrets.yaml` — `kube/oidc-issuer-url`, `kube/oidc-client-id`, `kube/oidc-client-secret` (plus the real CA PEM in `kube/certificate-authority`). **Your draft's config, fixed on the way in:** - `command: kubectl` → `${pkgs.kubelogin-oidc}/bin/kubectl-oidc_login` — the credential plugin is that binary, and it must be an absolute store path (kubectl execs it directly, no PATH). - `"oidc-login" "get-token"` → just `"get-token"` (the binary name *is* `kubectl-oidc_login`). - `--oidc-extra-scope=profile=email` → two args `--oidc-extra-scope=profile` and `--oidc-extra-scope=email` (the original passed one scope literally named "profile=email"). - `env` + `$$OIDC_*` indirection → secret values go in the exec args directly; kubelogin has no env fallback for `--oidc-client-secret` (flag set checked on 1.36.4, and exercised on the pinned 1.36.3), and the template substitution already keeps them out of the store. - `interactiveMode: Never` → `IfAvailable`: on a freshly switched host the kubelogin token cache is empty and `Never` hard-fails the first browser login. (`IfNeeded` is not a valid value — client-go rejects it; valid: `Never`/`IfAvailable`/`Always`.) **Also reverted:** the OIDC attrset that had ended up as the default of `kubernetes.client.kubeconfig` in `users/modules/kubernetes.nix`. It referenced `config`/`sops`/`kubectl` at module scope where none are bound, used the `'oidc-login'` binary, and interpolated decrypted secret values through `pkgs.formats.yaml` — i.e. straight into the Nix store — defeating the placeholder mechanism. Default is `null` again; the corrected store-safe pattern is now the option *example*, with a note pointing secret-embedding configs at templates + `kubeconfigFile`. **Verified:** - kansas eval: `kubeconfigFile` = rendered template path; template content shows `/nix/store/…-kubelogin-1.36.3/bin/kubectl-oidc_login`, placeholders in the three secret args, CA = `/home/rpickler/.config/sops-nix/secrets/kube/certificate-authority`; all four `kube/*` secrets resolve against `users/secrets.yaml`. - pumpkin eval: `kubeconfig`/`kubeconfigFile` null, no `.kube/config` entry (host gate intact). - End-to-end with the pinned kubectl 1.36.3: parsed the rendered file (placeholder-substituted) and invoked the plugin — kubelogin accepted every flag and proceeded to OIDC discovery against the (dummy) issuer URL. This also caught the `interactiveMode` value bug above.
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue-35-kube-client-user-module:issue-35-kube-client-user-module
git switch issue-35-kube-client-user-module

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff issue-35-kube-client-user-module
git switch issue-35-kube-client-user-module
git rebase main
git switch main
git merge --ff-only issue-35-kube-client-user-module
git switch issue-35-kube-client-user-module
git rebase main
git switch main
git merge --no-ff issue-35-kube-client-user-module
git switch main
git merge --squash issue-35-kube-client-user-module
git switch main
git merge --ff-only issue-35-kube-client-user-module
git switch main
git merge issue-35-kube-client-user-module
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
richardpickler.com/nixos!37
No description provided.