Skip to content

feat: add -read flag to print current META config to stdout#8

Open
Mickaël Canévet (mcanevet) wants to merge 2 commits into
cozystack:mainfrom
mcanevet:feat/read-flag
Open

feat: add -read flag to print current META config to stdout#8
Mickaël Canévet (mcanevet) wants to merge 2 commits into
cozystack:mainfrom
mcanevet:feat/read-flag

Conversation

@mcanevet

Copy link
Copy Markdown
Contributor

Summary

  • Add readConfig, which loads the ADV from any io.ReaderAt and returns the raw bytes stored under FixedTag.
  • Wire it up behind a -read flag so the tool can inspect what is currently written to the META partition without modifying it.

Test plan

  • go build ./...
  • go vet ./...
  • go test ./... (33 passed, including new TestReadConfigRoundTrip, TestReadConfigEmpty, TestReadConfigBadDevice)

Copilot AI review requested due to automatic review settings July 9, 2026 16:37
Add readConfig which loads the ADV from any io.ReaderAt and returns the
raw bytes stored under FixedTag. Wire it up behind a -read flag so the
tool can inspect what is currently written without modifying the
partition.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Mickaël Canévet <mickael.canevet@proton.ch>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for reading and printing the network configuration from the Talos META partition using a new -read flag, along with corresponding unit tests. The review feedback suggests several improvements: validating command-line flags immediately after parsing to avoid opening the device prematurely and to enforce mutual exclusivity; simplifying the parameter type in readConfig to io.ReaderAt; and correcting the error handling in readConfig to check err != nil instead of adv == nil to prevent masking corruption errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread main.go
Comment on lines 141 to 144
if *devicePath == "" {
fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-config <file> | -config-env <VAR> [-config-env-base64])")
os.Exit(1)
}
if *configPath == "" && *configEnv == "" {
fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-config <file> | -config-env <VAR> [-config-env-base64])")
os.Exit(1)
}
if *configPath != "" && *configEnv != "" {
fmt.Fprintln(os.Stderr, "Error: -config and -config-env are mutually exclusive")
os.Exit(1)
}
if *configEnvBase64 && *configEnv == "" {
fmt.Fprintln(os.Stderr, "Error: -config-env-base64 requires -config-env")
fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-read | -config <file> | -config-env <VAR> [-config-env-base64])")
os.Exit(1)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Validating command-line flags immediately after parsing is a best practice. Currently, if a user provides invalid flags (e.g., missing both -read and -config), the tool will still attempt to open the device with os.O_RDWR and read the GPT partition table before validating the flags and exiting. This can cause unnecessary permission errors or side-effects.

Additionally, this ensures that -read is explicitly validated as mutually exclusive with -config and -config-env rather than silently ignoring them.

	if *devicePath == "" {
		fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-read | -config <file> | -config-env <VAR> [-config-env-base64])")
		os.Exit(1)
	}
	if !*read && *configPath == "" && *configEnv == "" {
		fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-read | -config <file> | -config-env <VAR> [-config-env-base64])")
		os.Exit(1)
	}
	if *read && (*configPath != "" || *configEnv != "") {
		fmt.Fprintln(os.Stderr, "Error: -read is mutually exclusive with -config and -config-env")
		os.Exit(1)
	}
	if *configPath != "" && *configEnv != "" {
		fmt.Fprintln(os.Stderr, "Error: -config and -config-env are mutually exclusive")
		os.Exit(1)
	}
	if *configEnvBase64 && *configEnv == "" {
		fmt.Fprintln(os.Stderr, "Error: -config-env-base64 requires -config-env")
		os.Exit(1)
	}

Comment thread main.go Outdated
Comment on lines +92 to +102
func readConfig(dev interface{ io.ReaderAt }) ([]byte, error) {
adv, err := talos.NewADV(io.NewSectionReader(dev, 0, int64(talos.Size)))
if adv == nil {
return nil, fmt.Errorf("loading ADV: %w", err)
}
data, ok := adv.ReadTagBytes(FixedTag)
if !ok {
return nil, fmt.Errorf("tag %#x not found", FixedTag)
}
return data, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using interface{ io.ReaderAt } as a parameter type is redundant and non-idiomatic in Go; it can be simplified to io.ReaderAt.

Additionally, in readConfig, we should check if err != nil instead of if adv == nil. While writeConfig only checks adv == nil to allow overwriting a corrupt/empty ADV, readConfig is a read-only operation. If the ADV is corrupt or uninitialized (where adv != nil but err != nil), we cannot reliably read tags from it and should report the initialization/corruption error immediately rather than silently falling through and returning a misleading "tag not found" error.

Suggested change
func readConfig(dev interface{ io.ReaderAt }) ([]byte, error) {
adv, err := talos.NewADV(io.NewSectionReader(dev, 0, int64(talos.Size)))
if adv == nil {
return nil, fmt.Errorf("loading ADV: %w", err)
}
data, ok := adv.ReadTagBytes(FixedTag)
if !ok {
return nil, fmt.Errorf("tag %#x not found", FixedTag)
}
return data, nil
}
func readConfig(dev io.ReaderAt) ([]byte, error) {
adv, err := talos.NewADV(io.NewSectionReader(dev, 0, int64(talos.Size)))
if err != nil {
return nil, fmt.Errorf("loading ADV: %w", err)
}
data, ok := adv.ReadTagBytes(FixedTag)
if !ok {
return nil, fmt.Errorf("tag %#x not found", FixedTag)
}
return data, nil
}

Comment thread main.go Outdated
Comment on lines +157 to +177
if *read {
data, err := readConfig(meta)
if err != nil {
log.Fatalf("Error reading config: %v", err)
}
fmt.Print(string(data))
return
}

if *configPath == "" && *configEnv == "" {
fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-read | -config <file> | -config-env <VAR> [-config-env-base64])")
os.Exit(1)
}
if *configPath != "" && *configEnv != "" {
fmt.Fprintln(os.Stderr, "Error: -config and -config-env are mutually exclusive")
os.Exit(1)
}
if *configEnvBase64 && *configEnv == "" {
fmt.Fprintln(os.Stderr, "Error: -config-env-base64 requires -config-env")
os.Exit(1)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since all flag validations have been moved to the beginning of main (immediately after flag.Parse()), we can simplify this block and remove the redundant validation checks.

Suggested change
if *read {
data, err := readConfig(meta)
if err != nil {
log.Fatalf("Error reading config: %v", err)
}
fmt.Print(string(data))
return
}
if *configPath == "" && *configEnv == "" {
fmt.Fprintln(os.Stderr, "Usage: talos-meta-tool -device <disk-device> (-read | -config <file> | -config-env <VAR> [-config-env-base64])")
os.Exit(1)
}
if *configPath != "" && *configEnv != "" {
fmt.Fprintln(os.Stderr, "Error: -config and -config-env are mutually exclusive")
os.Exit(1)
}
if *configEnvBase64 && *configEnv == "" {
fmt.Fprintln(os.Stderr, "Error: -config-env-base64 requires -config-env")
os.Exit(1)
}
if *read {
data, err := readConfig(meta)
if err != nil {
log.Fatalf("Error reading config: %v", err)
}
fmt.Print(string(data))
return
}

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mcanevet, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1cfcd2ca-9d82-40a1-95e7-813bd4dc232f

📥 Commits

Reviewing files that changed from the base of the PR and between c185f62 and 88a8779.

📒 Files selected for processing (3)
  • README.md
  • main.go
  • main_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a non-destructive “read” mode to the Talos META metadata tool so operators can inspect the currently stored network metadata without writing to disk.

Changes:

  • Added readConfig to load an ADV from an io.ReaderAt and extract bytes for FixedTag.
  • Wired readConfig into the CLI behind a new -read flag and documented the usage in the README.
  • Added unit tests covering read round-trip, empty ADV, and bad device behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
README.md Updates documentation and adds an example for -read usage.
main.go Introduces readConfig and adds the -read flag flow in the CLI.
main_test.go Adds tests to validate reading behavior and error cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread main.go Outdated
Comment on lines +92 to +102
func readConfig(dev interface{ io.ReaderAt }) ([]byte, error) {
adv, err := talos.NewADV(io.NewSectionReader(dev, 0, int64(talos.Size)))
if adv == nil {
return nil, fmt.Errorf("loading ADV: %w", err)
}
data, ok := adv.ReadTagBytes(FixedTag)
if !ok {
return nil, fmt.Errorf("tag %#x not found", FixedTag)
}
return data, nil
}
Comment thread main.go
Comment on lines +157 to +161
if *read {
data, err := readConfig(meta)
if err != nil {
log.Fatalf("Error reading config: %v", err)
}
Comment thread README.md Outdated
@@ -1,6 +1,6 @@
# Talos metadata writer tool
- Move flag validation immediately after flag.Parse(), before opening
  the device or reading the GPT table, and make -read mutually
  exclusive with -config/-config-env.
- Open the device O_RDONLY for -read instead of O_RDWR.
- readConfig: take io.ReaderAt directly instead of an inline interface,
  and check err != nil rather than adv == nil so ADV corruption is
  reported instead of masked as "tag not found".
- README: fix title, still said "writer tool" after -read was added.

Signed-off-by: Mickaël Canévet <mickael.canevet@proton.ch>
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