Skip to content

Merge into ~/.kube/config instead of round-tripping it through a model - #88

Merged
popen2 merged 1 commit into
mainfrom
claude/kubectl-frost-config-gen-55ewjv
Aug 30, 2026
Merged

Merge into ~/.kube/config instead of round-tripping it through a model#88
popen2 merged 1 commit into
mainfrom
claude/kubectl-frost-config-gen-55ewjv

Conversation

@popen2

@popen2 popen2 commented Aug 30, 2026

Copy link
Copy Markdown
Owner

One entry the @kubernetes/client-node loader disliked cost the user every
user, every context and their current-context.

readExistingConfig() called loadFromString, which throws on an entry
that fails its validation - contexts[i].context.cluster is missing being
the easy one to hit, since that is what kubectl config set-context <name> --namespace=<ns> writes. The throw was caught and logged at debug, and
the half-loaded KubeConfig it left behind (loadFromOptions assigns
clusters first, so those survive and nothing else does) was then merged
into and written over the file. What is left has no current context, so
kubectl falls back to localhost:8080 and every call fails.

The same round trip was lossy even when it did not throw: the model carries
only the fields it knows about, so preferences, per-entry extensions,
as/as-groups, disable-compression and anything kubectl grows later
were dropped on every write, while a token-file was read and inlined into
the file as a bare token.

So drop the model. mergeKubeconfig(existing, clusters) parses the YAML,
edits the three lists in place and dumps it back; everything else in the
document is handed back untouched. A same-named entry is updated by merge
and stays where it is, so a namespace set on one of Frost's contexts
survives the next refresh. A file that will not parse is left alone and
logged at error - skipping an update beats replacing a file we could not
read. Unchanged contents skip the write, and lineWidth: -1 keeps
certificate data and the authenticator path on one line, as
aws eks update-kubeconfig writes them.

Also fixes getNamePattern's uniqueClusters, which compared the lengths
of two lists built by mapping over the same array and so was always true.
It now compares distinct cluster names against distinct cluster ids, which
is what having both lists was for: two different clusters sharing a name
used to collapse onto one context, silently losing one of them.

@kubernetes/client-node was used nowhere else, so it and the @types/ws
devDep it needed are gone.

Verified with kubectl against the generated files: a config carrying a
cluster-less context keeps all of its entries through a merge, two clusters
sharing a name get two contexts pointing at two endpoints, the exec plugin
runs and the CA parses, and a second run is a no-op.

One entry the @kubernetes/client-node loader disliked cost the user every
user, every context and their current-context.

`readExistingConfig()` called `loadFromString`, which throws on an entry
that fails its validation - `contexts[i].context.cluster is missing` being
the easy one to hit, since that is what `kubectl config set-context <name>
--namespace=<ns>` writes. The throw was caught and logged at `debug`, and
the half-loaded KubeConfig it left behind (`loadFromOptions` assigns
clusters first, so those survive and nothing else does) was then merged
into and written over the file. What is left has no current context, so
kubectl falls back to localhost:8080 and every call fails.

The same round trip was lossy even when it did not throw: the model carries
only the fields it knows about, so `preferences`, per-entry `extensions`,
`as`/`as-groups`, `disable-compression` and anything kubectl grows later
were dropped on every write, while a `token-file` was read and inlined into
the file as a bare `token`.

So drop the model. `mergeKubeconfig(existing, clusters)` parses the YAML,
edits the three lists in place and dumps it back; everything else in the
document is handed back untouched. A same-named entry is updated by merge
and stays where it is, so a namespace set on one of Frost's contexts
survives the next refresh. A file that will not parse is left alone and
logged at `error` - skipping an update beats replacing a file we could not
read. Unchanged contents skip the write, and `lineWidth: -1` keeps
certificate data and the authenticator path on one line, as
`aws eks update-kubeconfig` writes them.

Also fixes `getNamePattern`'s `uniqueClusters`, which compared the lengths
of two lists built by mapping over the same array and so was always true.
It now compares distinct cluster names against distinct cluster ids, which
is what having both lists was for: two different clusters sharing a name
used to collapse onto one context, silently losing one of them.

@kubernetes/client-node was used nowhere else, so it and the @types/ws
devDep it needed are gone.

Verified with kubectl against the generated files: a config carrying a
cluster-less context keeps all of its entries through a merge, two clusters
sharing a name get two contexts pointing at two endpoints, the exec plugin
runs and the CA parses, and a second run is a no-op.
@popen2
popen2 force-pushed the claude/kubectl-frost-config-gen-55ewjv branch from 1125340 to 05befa7 Compare August 30, 2026 06:19
@popen2
popen2 enabled auto-merge August 30, 2026 06:19
@popen2
popen2 merged commit 38b434a into main Aug 30, 2026
9 checks passed
@popen2
popen2 deleted the claude/kubectl-frost-config-gen-55ewjv branch August 30, 2026 06:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants