Skip to content

feat(cmd): add cobbler check command - #106

Merged
SchoolGuy merged 25 commits into
mainfrom
feat/cobbler-check
Nov 10, 2026
Merged

SchoolGuy merged 25 commits into
mainfrom
feat/cobbler-check

Conversation

@SchoolGuy

Copy link
Copy Markdown
Member
  • Add a new top-level cobbler check command that calls cobblerclient's existing Check() action and reports potential Cobbler server configuration problems (settings, missing dependencies, boot-loader files, services, etc.).
  • Output follows the classic Cobbler CLI UX: "No configuration problems found." when there are none, otherwise a numbered list of suggestions plus a reminder to restart cobblerd and run cobbler sync.
  • Register the command alphabetically in cmd/root.go, add a CHANGELOG.md entry, and add docs (docs/commands/check.rst + toctree entry).

@SchoolGuy SchoolGuy added this to the v1.0.0 milestone Oct 19, 2026
@github-project-automation github-project-automation Bot moved this to Pull Requests in Golang CLI Oct 19, 2026
SchoolGuy and others added 2 commits November 7, 2026 21:44
Expose cobblerclient's Check() action as a new top-level, zero-flag
command that reports potential Cobbler server configuration problems
in the classic numbered-list format.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The published cobblerd test image omits wget, which the server's
check action shells out to probe. Without it, cobbler check surfaces
as an XML-RPC fault instead of a reported suggestion, which broke
Test_CheckCmd in CI. Install wget on container startup via zypper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SchoolGuy and others added 21 commits November 10, 2026 14:07
Also fix the coverage-upload step's version guard, which had been left
pointing at v4.0.0b5 since the previous bump to b6 and so never fired.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cobbler 4.0.0b7 split the XML-RPC bind-address env var into separate
IPv4/IPv6 variants and dropped the old combined COBBLER_XMLRPC_BIND_ADDRESS
name. Without this, cobblerd silently falls back to binding 127.0.0.1 only,
which breaks the published 25151 port the dev-stack script waits on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.0.0-rc6's Settings.ConvertServerToIp (bool) can't decode Cobbler
4.0.0b7's reshaped convert_server_to_ip ({enabled, protocol}), which made
`cobbler setting edit`/`setting report` fail outright. v1.0.0-rc8 fixes the
field's type; verified `setting report`/`setting edit` and the full test
suite pass against a live v4.0.0b7 server.

v1.0.0-rc9 additionally fixes a server-side NetworkInterface bug this PR's
`cobbler check` work surfaced: a stray, unbacked IPv6Option._default_gateway
attribute corrupted the ipv6 dict's key set on every transaction clone,
which broke this client's decode heuristic for the whole ipv6 struct (not
just the gateway). if_gateway/ipv6_default_gateway are now DefaultGateway
on IPv4Option/IPv6Option - see a later commit in this branch for the
corresponding cmd/interface.go update.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds build-rpm-docker-leap16, mirroring the existing Tumbleweed target
but against registry.opensuse.org/opensuse/leap:16.0, which is the
actual release-validation target distro for Cobbler 4.0.0 (SLES 16.0
compatible). Needed to build and install cobbler-cli locally for
manual release validation without relying on the OBS package feed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both printField (cmd/root.go) and printDumpVars (cmd/utils.go) formatted
values with %s in their default case. %s on a value whose kind isn't
explicitly handled and doesn't implement Stringer renders as literal
"%!s(uint=42)" garbage instead of its content. Neither switch's default
case is currently reachable with garbled output given today's known XML-RPC
response kinds, but %v is the strictly safer default for whatever kind a
future Cobbler API field or decoder change adds, with no behavior change
for the kinds that reach it today.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
profileBoolFlagMetadata's "enable-ipxe" and "enable-menu" entries were
missing IsInheritable: true (unlike the equivalent systemBoolFlagMetadata
entry), so addBoolFlags() never registered their "-inherit" companion
flags. The shared switch-case in updateProfileFromFlags() unconditionally
looks up the "-inherit" flag once the base flag changes, dereferencing a
nil *pflag.Flag and panicking on any "profile edit/add --enable-ipxe" or
"--enable-menu" invocation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
rsync_sync() in the cobbler server hard-requires mirror_locally=True
for the rsync breed, but repo add/edit had no flag to set it, so no
rsync-breed repo could be synced through the documented CLI. Wire up
a plain bool flag (mirror_locally is non-inheritable on both the
Python item and the vendored client's Repo struct, so no -inherit
companion is needed, matching the existing keep-updated convention).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A failed new-mode add (any item type) permanently leaked a dangling
name claim server-side, because cobbler-cli issued its
new_*/modify_*/save_* XML-RPC calls with no transaction_begin/commit/
abort wrapping, going through cobblerd's older, leak-prone
no-transaction path. Add a withTransaction helper (cmd/root.go) and
wrap the mutating RPC sequence of add/edit/remove commands in it,
aborting on any error so a failed attempt is cleanly discarded
server-side instead of lingering in cobblerd's unsaved_items cache for
up to an hour.

Initial scope was narrower than "every mutating command" because
testing against a live cobbler-dev container surfaced several
independent, pre-existing server/client bugs that a transaction
exposes but a non-transactional call silently swallows:

- Template and the network interface commands are plain BaseItem
  types; cobbler's transaction-aware modify_item/remove_item code
  unconditionally touches attributes (kernel_options, depth) that only
  exist on InheritableItem/BootableItem subtypes, crashing with an
  AttributeError.
- The three *_group item types are InheritableItem but not
  BootableItem, hitting the same kernel_options crash on
  add/edit/copy/rename.
- Every copy/rename command's post-mutate "look up the new/renamed
  item" step uses cobblerclient's type-specific Get<Type>Handle, which
  never forwards the session token, so it couldn't see an item staged
  earlier in the same open transaction.
- System add/edit hit a cobblerclient bug independent of the above: a
  stray top-level VirtPXEBoot field with no matching server-side
  attribute (the real one is nested under virt.pxe_boot).

All of the above have since been fixed upstream (BaseItem/InheritableItem
transaction-aware code, Get<Type>Handle token forwarding, and the
VirtPXEBoot field), so every remaining item type's add/edit/copy/rename
-- template, network interface, the three *_group types, and System's
add/edit/copy/rename -- is now wrapped in withTransaction too, closing
the dangling-name-leak bug across the board.

Regression test: cmd/distro_test.go's
Test_DistroAddCmd_FailedAddDoesNotLeakDanglingName - a distro add with
a bad initrd path fails after new_distro already claimed the name;
immediately retrying with the same name and valid fields now succeeds
right away instead of failing with "already exists". Confirmed
failing before this fix and passing after.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The flag was parsed but never consulted, so `setting report --name <x>`
always dumped every setting instead of just the one requested. Reuse
settingsFieldByMapstructureTag (already used by setting edit) to look
up the named field and print only it, matching the error message style
used there for unknown names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`template report` printed Content as a blank field whenever it held
real text, giving no hint that `template content --name <x>` exists
to view it. Replace the raw value with a short "<N bytes, not shown
-- see 'cobbler template content --name <name>'>" indicator when
content is non-empty, while leaving empty content printed as before.
The full content is intentionally never inlined, since it can be an
arbitrarily large autoinstall template body.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NetworkInterface.SystemName was never populated by Client.GetNetworkInterfaces()
(cobbler's system_name is a derived property with no backing attribute, so
BaseItem.to_dict() never includes it), so `interface list` grouped every
interface under a blank system header. Fixed by resolving system_uid -> name
client-side via Client.GetSystems() instead.

`interface report --name <x>` also silently ignored --system-name whenever
--name was set, so it failed identically with or without --system-name
whenever the name was ambiguous across systems (interface names are only
unique per-system, not globally). Fixed by filtering on system_uid (resolved
from --system-name) when both flags are given, and by adding a dedicated
errItemAmbiguous sentinel so the report command can replace resolveUID's
generic "use --uid to disambiguate" message with one that explains the
--system-name requirement. The --system-name-only report branch had the same
system_name-matching defect and is fixed the same way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cobblerclient.BuildisoOptions.ExcludeSystems and the Python API's
build_iso(exclude_systems=...) parameter already existed end-to-end,
but NewBuildisoCmd never registered a --exclude-systems flag to set
it. With no --systems given, buildiso includes every system in the
collection by design, but operators had no practical way to narrow
that list without explicitly naming every system to include. Add a
plain bool flag mirroring the existing --airgapped/--standalone/
--exclude-dns flags.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cobblerclient's System struct dropped the stray top-level VirtPXEBoot
field in favor of the correct nested Virt.PxeBoot; updateSystemFromFlags
still wrote the old field name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cobbler replicate has only ever been exercised between two Cobbler servers
running the exact same version; mixed-version replication is untested at
this time and may fail outright or silently transfer incompatible/
incomplete data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
distro gains --remote-boot-iso, needed for casper (Ubuntu
live-server/Subiquity) netboot to locate its live filesystem over
the network -- cobblerclient's Distro struct already had the
underlying field missing entirely.

distro/profile/system gain --autoinstall-meta-json: a raw JSON
object string merged on top of --autoinstall-meta, since the
existing flag's pflag StringToString type can only represent flat
key=value strings and cannot carry the list/nested-object values
some autoinstall_meta keys require (e.g. cloud-init's
cloud_init_user_data_modules).

Also fixes a copy-paste bug in profile's --autoinstall-meta-inherit
handling (checked "boot-loaders-inherit" instead of
"autoinstall-meta-inherit", borrowed from the adjacent case block)
and the same gap entirely missing from distro's handling (always
treated the flag as setting new data rather than marking inherited).
system's handling was already correct and used as the reference.

image's --kernel-options, --kernel-options-post, --autoinstall-meta,
and --template-files flags were entirely non-functional: the
switch-case handling for them existed in updateImageFromFlags, but
no command ever called addMapFlags to register the underlying flags
on any of the five image subcommands, unlike every other item type.
Fixed by registering the same distroMapFlagMetadata profile already
reuses (the four keys are identical). Also fixes two further bugs in
the same block: --autoinstall-meta-inherit checked
"boot-loaders-inherit".Changed instead of
"autoinstall-meta-inherit".Changed (same copy-paste as profile's),
and the kernel-options-post branch assigned its new value to
image.KernelOptions.Data instead of image.KernelOptionsPost.Data,
silently corrupting kernel-options whenever kernel-options-post was
set. image also gains --autoinstall-meta-json for parity with the
other three item types.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
profile/system's virt-ram and virt-file-size int/float flag metadata was
missing IsInheritable, so addIntFlags/addFloatFlags never registered
their "-inherit" companion flags. The shared switch-case's
cmd.Flags().Lookup("virt-ram-inherit").Changed (and the file-size
equivalent) then dereferenced a nil *pflag.Flag, segfaulting
"cobbler profile/system edit --virt-ram=<n>" and --virt-file-size.

profile.go's two case bodies additionally had their Lookup() target
copy-pasted from the unrelated "virt-auto-boot-inherit" case (and the
virt-file-size branch set profile.Virt.AutoBoot instead of
profile.Virt.FileSize) -- fixed to look up and set their own fields.

system's virt-cpus had the same missing-IsInheritable bug, fixed here too:
systemIntFlagMetadata's "virt-cpus" entry now registers its
"virt-cpus-inherit" companion flag.

addStringFlags was the only one of the add*Flags helpers (addBoolFlags,
addIntFlags, addFloatFlags all already did this) that ignored
IsInheritable entirely, so no string-typed metadata could ever get its
"-inherit" companion flag registered. repoStringFlagMetadata's
"createrepo-flags"/"proxy" entries and interface.go's "virt-bridge"
flag hit the same nil-Lookup panic as a result -- fixed by the same
mechanism, closing the systemic gap across int/float/bool/string-typed
inheritable flags.

image.go has a further, differently-shaped version of this bug (checks
"owners-inherit" and misuses GetBool for both fields) -- deliberately
left for a separate fix, not touched here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cobbler replicate can now also replicate menus, (non built-in) templates,
network interfaces, and distro/profile/system groups, each selectable via
its own --<type>s pattern flag, matching the new fields on
cobblerclient.ReplicateOptions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add --kernel-options-json, --kernel-options-post-json, and
--template-files-json, mirroring the existing --autoinstall-meta-json
across distro/profile/system/image: a raw JSON object merged on top of
the flat stringToString flag, for values the key=value syntax can't
represent (a list/nested-object, or a value containing a comma). With
the previous commit's implicit JSON-sniffing gone, this is the only way
left to set a structured value.

Shared the merge logic behind a new mergeJSONObjectFlag/
mergeJSONStringMapFlag helper pair in cmd/utils.go and switched the
four existing --autoinstall-meta-json call sites to use it too, instead
of each repeating its own inline json.Unmarshal loop.

Also fixes a copy-paste bug found while touching this code: distro.go's
kernel-options-post handling wrote the result into
distro.KernelOptions.Data instead of distro.KernelOptionsPost.Data, so
--kernel-options-post silently never took effect on distro
(profile/system/image were not affected).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mirrors the cobblerclient/cobbler server fix for the IPv6Option dict
corruption (the if_gateway/ipv6_default_gateway fields were removed
from NetworkInterface; both are now DefaultGateway on
IPv4Option/IPv6Option). This fixes the Test_InterfaceEditCmd_AllFlags
integration failure: the corrupted ipv6 dict key set previously broke
cobblerclient's decode heuristic for the whole ipv6 struct, not just
the gateway field, so ipv6-address silently failed to round-trip too.

Requires the cobblerclient v1.0.0-rc9 bump from a prior commit on this
branch, which already contains the server-side fix.
@SchoolGuy
SchoolGuy merged commit d79704b into main Nov 10, 2026
20 of 22 checks passed
@github-project-automation github-project-automation Bot moved this from Pull Requests to Done in Golang CLI Nov 10, 2026
@SchoolGuy
SchoolGuy deleted the feat/cobbler-check branch November 10, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant