You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Can we move the netbox configuration to environment variables and don't initialize it within the main function but the controller?
Rationale is, although kubebuilder scaffolds some default options, it's a hassle to pass config via flags with kubernetes, especially secrets.
In my mind, exposing the configuration variables over the cli is essential for making them self-documenting (i.e cmd --help).
I'd like to avoid reading config values from environment variables in "random places" in the code.
**AI-generated research notes ** The capabilities and licenses below are based on documentation and source review; integration testing and complete dependency-license verification are still needed.
Configuration interface requirements
The configuration interface should meet these requirements:
Each module/controller owns a typed options struct and receives the whole struct through its setup function.
Adding a supported field to an existing options group should automatically expose a CLI flag and an environment binding. No new per-field registration in main, handwritten BindFlags, or setup parameters.
Preserve a self-documenting CLI: --help should describe options, defaults, and environment bindings.
Parse configuration at the startup boundary, rather than reading environment variables in arbitrary controller code.
Prefer explicit configuration instances over package-level globals. This is a preference, not an absolute prohibition.
Go source is the source of truth. External/schema-first definitions are out. Generating bindings from Go declarations is still an option.
Accepted license families: Apache-2.0, MIT, BSD, ISC, and Creative Commons. Check the selected components and their dependencies; a top-level license alone is not blanket approval.
A new module/group can require one-time inclusion and wiring. A new field within an existing group should not require more transport code. Using the new value in controller logic and testing it are separate from exposing it as configuration.
Current ceremony
charts/openstack-hypervisor-operator/values.yaml and templates/deployment.yaml currently transport several settings through environment variables that are expanded into CLI arguments (--option=$(ENV_VAR)). cmd/main.go then binds/parses individual settings and forwards them through setup parameters or globals.
A generic Helm env map or flag map could remove the per-setting deployment-template mappings. Native env parsing would also remove the env-to-argument expansion layer while retaining CLI arguments for direct use and overrides. Secret references, OpenStack environment variables, and runtime settings such as GOMEMLIMIT need separate handling.
Go-source-based approaches and libraries
1. Automatic struct discovery, optionally using tags for metadata
Struct-to-flag adapters for multiple parsers; env capabilities depend on the adapter.
Additional struct-driven candidates: go-arg (BSD-2-Clause), go-flags (BSD-3-Clause), mcli (MIT), and structcli (MIT). structcli brings Cobra and Viper, which matters when comparing smaller/composable alternatives.
Tags are not required for basic field discovery. A bare Go field supplies its name and type, however, not its semantic description or non-zero default; that metadata must live somewhere.
2. Field comments → generated bindings
No maintained turnkey library was found in this research that generates CLI bindings, env bindings, and help from Go field comments alone. This is a possible design, not an identified drop-in solution.
Building blocks:
envdoc — MIT; extracts field comments and generates environment documentation, not parsing/bindings.
Jennifer — MIT; emits Go code, not configuration-field discovery.
Go's go/ast, go/parser, go/doc, and go/types — BSD-3-Clause; read authoritative source declarations and comments.
This path would require maintaining a generator. Generated bindings would be output, not a second source of truth.
3. Self-describing field types
Mostly extension mechanisms in existing registrars:
Kong supports custom text decoding/mappers and type-level validation.
Dials supports custom text decoding and typed enums whose allowed values appear in flag help.
go-flagsfiller supports custom text-unmarshal types and extensible conversion.
These can move parsing and validation into Go types. They do not universally discover type-owned defaults, per-setting descriptions, and env policy. A complete self-describing-type design would still need glue. Adding a field of an existing supported type can be automatic; inventing a new option type may need additional code.
boa's programmatic API — MIT; discovers plain fields and attaches metadata through typed API calls.
urfave/cli v3 — MIT; declarative Go flag objects with destinations, env sources, defaults, and help.
Boa still discovers fields automatically; its API calls supply metadata. Enflag and urfave/cli require a declaration/binding per option. That can live in the owning module rather than main, but is a weaker fit for strict “add only a field.”
Composable source-loading alternatives
Koanf has an MIT-licensed core; its reviewed core dependencies are also MIT. Providers are separately selectable. It handles source loading/merging and unmarshalling, but does not itself generate CLI flags/help from fields; pair it with a struct-to-flag adapter.
Similarly, ff (Apache-2.0) handles flags-first source precedence, not struct discovery. It can use an adapter-generated FlagSet. Both deliberately support composition rather than requiring a larger integrated framework.
Relevant evolution: ff/v3 ignores empty env values, while the available v4.0.0-beta.1 uses LookupEnv and has an upstream test for an explicitly empty env value overriding a non-empty default. V4 remains a prerelease.
Compatibility checks before choosing
Candidate implementations should be checked for the following behaviour:
Every intended options field is registered, or explicitly excluded; unsupported types must not silently lose a setting.
Existing CLI names and Boolean syntax remain usable, especially --leader-elect and --metrics-secure=false.
Explicit empty regex values survive transport and parsing. An empty include regex means “include all interfaces,” not “use the default regex.”
CLI/env/default precedence is deliberate, including collection replacement versus appending.
Preserve comma-separated namespaces, deduplication, TRAIT=N overrides, and migration-limit validation.
--help and --version work without required runtime configuration. Handling invalid lower-priority env values needs explicit tests.
Preserve ctrlzap.Options.BindFlags, which uses a standard flag.FlagSet, without manual per-application-option wiring in main.
Controllers/tests can receive independent options instances without saving/restoring global configuration.
Specific caveats from source review: go-flagsfiller's native env parsing occurs during flag registration, so invalid env can prevent help or a later CLI override; its reviewed implementation does not directly bind map[string]int. aconfig has old/new parser modes whose CLI compatibility needs checking.
These are candidate designs rather than an implementation recommendation. They should be evaluated against the configuration-interface requirements, compatibility checks, and license policy before selecting an implementation.
Four comparison PRs implement this with different configuration libraries. Each follows the same rule: a setting is declared in the options struct of its owning controller, with no central per-variable registration in cmd.
Diff stats are the complete PR diff against each PR's own base (merge base with main). Churn is added + deleted lines.
All four share the same layout: eviction options in internal/controller/eviction/controller.go, certificate options in internal/controller/node_certificate_controller.go, shared settings in internal/controller/hypervisor_controller.go. internal/config no longer exists in any of them.
The PR bodies document the parameter encodings and any known deviations from the current behavior.
Activity
fwiesel commentedon Nov 21, 2024
Most often used are:
There are reasonable concerns with the viper.
Alternatives I found:
In my mind, exposing the configuration variables over the cli is essential for making them self-documenting (i.e
cmd --help).I'd like to avoid reading config values from environment variables in "random places" in the code.
Personally, I like "koanf".
github-actions commentedon Feb 20, 2025
This issue is stale because it has been open for 90 days with no activity.
github-actions commentedon Mar 6, 2025
This issue was closed because it has been inactive for 14 days since being marked as stale.
github-actions commentedon Aug 7, 2025
This issue is stale because it has been open for 90 days with no activity.
github-actions commentedon Aug 22, 2025
This issue was closed because it has been inactive for 14 days since being marked as stale.
fwiesel commentedon Oct 29, 2026
Agent-generated research notes — 2026-10-29
Configuration interface requirements
The configuration interface should meet these requirements:
main, handwrittenBindFlags, or setup parameters.--helpshould describe options, defaults, and environment bindings.A new module/group can require one-time inclusion and wiring. A new field within an existing group should not require more transport code. Using the new value in controller logic and testing it are separate from exposing it as configuration.
Current ceremony
charts/openstack-hypervisor-operator/values.yamlandtemplates/deployment.yamlcurrently transport several settings through environment variables that are expanded into CLI arguments (--option=$(ENV_VAR)).cmd/main.gothen binds/parses individual settings and forwards them through setup parameters or globals.A generic Helm env map or flag map could remove the per-setting deployment-template mappings. Native env parsing would also remove the env-to-argument expansion layer while retaining CLI arguments for direct use and overrides. Secret references, OpenStack environment variables, and runtime settings such as
GOMEMLIMITneed separate handling.Go-source-based approaches and libraries
1. Automatic struct discovery, optionally using tags for metadata
flag.FlagSet; optional native env bindings, automatic naming, defaults, and help.FlagSetfor additional flags.Additional struct-driven candidates: go-arg (BSD-2-Clause), go-flags (BSD-3-Clause), mcli (MIT), and structcli (MIT).
structclibrings Cobra and Viper, which matters when comparing smaller/composable alternatives.Tags are not required for basic field discovery. A bare Go field supplies its name and type, however, not its semantic description or non-zero default; that metadata must live somewhere.
2. Field comments → generated bindings
No maintained turnkey library was found in this research that generates CLI bindings, env bindings, and help from Go field comments alone. This is a possible design, not an identified drop-in solution.
Building blocks:
go/ast,go/parser,go/doc, andgo/types— BSD-3-Clause; read authoritative source declarations and comments.This path would require maintaining a generator. Generated bindings would be output, not a second source of truth.
3. Self-describing field types
Mostly extension mechanisms in existing registrars:
These can move parsing and validation into Go types. They do not universally discover type-owned defaults, per-setting descriptions, and env policy. A complete self-describing-type design would still need glue. Adding a field of an existing supported type can be automatic; inventing a new option type may need additional code.
4. Typed descriptors/programmatic metadata
Boa still discovers fields automatically; its API calls supply metadata. Enflag and urfave/cli require a declaration/binding per option. That can live in the owning module rather than
main, but is a weaker fit for strict “add only a field.”Composable source-loading alternatives
Koanf has an MIT-licensed core; its reviewed core dependencies are also MIT. Providers are separately selectable. It handles source loading/merging and unmarshalling, but does not itself generate CLI flags/help from fields; pair it with a struct-to-flag adapter.
Similarly, ff (Apache-2.0) handles flags-first source precedence, not struct discovery. It can use an adapter-generated
FlagSet. Both deliberately support composition rather than requiring a larger integrated framework.Relevant evolution:
ff/v3ignores empty env values, while the availablev4.0.0-beta.1usesLookupEnvand has an upstream test for an explicitly empty env value overriding a non-empty default. V4 remains a prerelease.Compatibility checks before choosing
Candidate implementations should be checked for the following behaviour:
--leader-electand--metrics-secure=false.TRAIT=Noverrides, and migration-limit validation.--helpand--versionwork without required runtime configuration. Handling invalid lower-priority env values needs explicit tests.ctrlzap.Options.BindFlags, which uses a standardflag.FlagSet, without manual per-application-option wiring inmain.Specific caveats from source review: go-flagsfiller's native env parsing occurs during flag registration, so invalid env can prevent help or a later CLI override; its reviewed implementation does not directly bind
map[string]int. aconfig has old/new parser modes whose CLI compatibility needs checking.These are candidate designs rather than an implementation recommendation. They should be evaluated against the configuration-interface requirements, compatibility checks, and license policy before selecting an implementation.
fwiesel commentedon Nov 2, 2026
Four comparison PRs implement this with different configuration libraries. Each follows the same rule: a setting is declared in the options struct of its owning controller, with no central per-variable registration in
cmd.Diff stats are the complete PR diff against each PR's own base (merge base with
main). Churn is added + deleted lines.6921cfc2f83233dcfd50a6286311Notes:
8f1100d3, Parse configuration into typed options structs with cobra #379:f4b13698, Read configuration from env into typed options structs (koanf) #381 and Read configuration from env into typed options structs #382:f5172c4). Newermaincommits do not overlap the changed files, so the comparison holds.internal/controller/eviction/controller.go, certificate options ininternal/controller/node_certificate_controller.go, shared settings ininternal/controller/hypervisor_controller.go.internal/configno longer exists in any of them.