Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add output format flag to the k0s sysinfo command #5264

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

emosbaugh
Copy link
Contributor

Description

Adds --output flag to the sysinfo command with formats human|yaml|json.

sysinfo.json

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

How Has This Been Tested?

  • Manual test
  • Auto test added

Checklist:

  • My code follows the style guidelines of this project
  • My commit messages are signed-off
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

@emosbaugh emosbaugh requested review from a team as code owners November 15, 2024 18:36
@twz123
Copy link
Member

twz123 commented Nov 15, 2024

Ha, wanted to do this since its inception ^^

The initial idea was to have a separate JSON reporter for this, though.

@emosbaugh
Copy link
Contributor Author

Ha, wanted to do this since its inception ^^

The initial idea was to have a separate JSON reporter for this, though.

@twz123 Could you provide some feedback on the Probe struct? After that I'm happy to refactor.

Copy link
Member

@twz123 twz123 left a comment

Choose a reason for hiding this comment

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

Not sure about the JOSN/YAML structure. Did you consider to model the sysinfo hierarchy into the output structure? Alternatively, have the paths be the keys in a big map (urgh, there's no way to keep the order of things in there, right? :-( )? I'd also use camelCase for the keys, and omit empty fields. I'm pondering about a "somewhat natural" jq/jsonpointer expression to select individual probes 🤔

w io.Writer
colors aurora.Aurora
failed bool
type cliReporter interface {
Copy link
Member

@twz123 twz123 Nov 26, 2024

Choose a reason for hiding this comment

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

What I had in mind:

  • Leave the cliReporter unmodified.
  • Just have the resultsCollector to collect the tree.

Have the switch statement in the command look like this:

switch outputFormat {
case "human":
	cli := &cliReporter{
		w:      out,
		colors: aurora.NewAurora(term.IsTerminal(out)),
	}
	if err := probes.Probe(cli); err != nil {
		return err
	}
	if cli.failed {
		return errors.New("sysinfo failed")
	}
	return nil

case "json":
	return collectAndPrint(probes, out, json.Marshal)

case "yaml":
	return collectAndPrint(probes, out, yaml.Marshal)

default:
	return fmt.Errorf("unknown output format: %q", outputFormat)
}

And the collectAndPrint function could look like this:

func collectAndPrint(probe probes.Probe, out io.Writer, marshal func(any) ([]byte, error)) error {
	var c resultsCollector
	if err := probe.Probe(&c); err != nil {
		return err
	}
	if c.failed {
		return errors.New("sysinfo failed")
	}
	bytes, err := marshal(c.results)
	if err != nil {
		return err
	}

	_, err = out.Write(bytes)
	return err
}

@@ -64,68 +80,177 @@ func NewSysinfoCmd() *cobra.Command {
flags.BoolVar(&sysinfoSpec.ControllerRoleEnabled, "controller", true, "Include controller-specific sysinfo")
flags.BoolVar(&sysinfoSpec.WorkerRoleEnabled, "worker", true, "Include worker-specific sysinfo")
flags.StringVar(&sysinfoSpec.DataDir, "data-dir", constant.DataDirDefault, "Data Directory for k0s")
flags.StringVarP(&outputFormat, "output", "o", "human", "Output format. Must be one of human|yaml|json")
Copy link
Member

Choose a reason for hiding this comment

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

This is how it's done in similar flags on other subcommands.

Suggested change
flags.StringVarP(&outputFormat, "output", "o", "human", "Output format. Must be one of human|yaml|json")
flags.StringVarP(&outputFormat, "output", "o", "human", "Output format (valid values: human, json, yaml)")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated although I copied the flag from here.


var cli cliReporter
switch outputFormat {
case "human":
Copy link
Member

Choose a reason for hiding this comment

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

Maybe "text" or "cli"?

@emosbaugh emosbaugh marked this pull request as draft November 26, 2024 19:36
@emosbaugh
Copy link
Contributor Author

emosbaugh commented Nov 26, 2024

Not sure about the JOSN/YAML structure. Did you consider to model the sysinfo hierarchy into the output structure? Alternatively, have the paths be the keys in a big map (urgh, there's no way to keep the order of things in there, right? :-( )? I'd also use camelCase for the keys, and omit empty fields. I'm pondering about a "somewhat natural" jq/jsonpointer expression to select individual probes 🤔

For nested, how about something like this?

{
  "results": {
    "os": {
      "displayName": "Operating system",
      "prop": "Linux",
      "message": "",
      "category": "pass",
      "error": null,
      "results": {
        "net": {
          "displayName": "CONFIG_NET: Networking support",
          "prop": "built-in",
          "message": "",
          "category": "pass",
          "error": null,
          "results": {
            "bridge": {
              "displayName": "CONFIG_BRIDGE: 802.1d Ethernet Bridging",
              "prop": "built-in",
              "message": "",
              "category": "pass",
              "error": null,
              "results": {
                "stp": {
                  "displayName": "CONFIG_LLC",
                  "prop": "built-in",
                  "message": "",
                  "category": "pass",
                  "error": null
                }
              }
            }
          }
        }
      }
    }
  }
}

Corresponding jq

 jq '.results.os.results.net.results.bridge.results.stp' cmd/sysinfo/results.json
{
  "displayName": "CONFIG_LLC",
  "prop": "built-in",
  "message": "",
  "category": "pass",
  "Error": null
}

Alternatively, for a simpler approach and one that is ordered, how about joining the path with dot?

[
  {
    "path": "os.NET.BRIDGE.STP",
    "displayName": "CONFIG_LLC",
    "prop": "built-in",
    "message": "",
    "category": "pass",
    "error": null
  }
]

jq

jq '.[] | select(.path=="os.NET.BRIDGE.STP")' cmd/sysinfo/results.json
{
  "path": "os.NET.BRIDGE.STP",
  "displayName": "CONFIG_LLC",
  "prop": "built-in",
  "message": "",
  "category": "pass",
  "error": null
}

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