Skip to content

tsh request search, add support for --format flag#62015

Merged
tangyatsu merged 2 commits intomasterfrom
tangyatsu/tsh-request-search-add-support-for-format-flag
Dec 9, 2025
Merged

tsh request search, add support for --format flag#62015
tangyatsu merged 2 commits intomasterfrom
tangyatsu/tsh-request-search-add-support-for-format-flag

Conversation

@tangyatsu
Copy link
Copy Markdown
Contributor

@tangyatsu tangyatsu commented Dec 4, 2025

What

Resolves #41953

This PR adds support for the --format flag to tsh request search, enabling output in json and yaml formats in addition to text format.

This PR also adds a new helper func MakeAsciitableColumnsAndRows() in teleport/lib/asciitable.
The function converts a slice of structs into column names and row values suitable for use with MakeTable(). Column names are derived from struct field names.
It also supports struct tags:

  • asciitable:"Custom Name": sets a custom column name
  • asciitable:"-": excludes the field from output
  • no tag: name is derived from CamelCase (e.g., ResourceID toResource ID)

changelog: Added support for --format flag for tsh request search

Manual Tests

A local test cluster was created with an Web application, SSH node, a PostgreSQL database and Minikube.

The following tests were performed:

  • tsh request search --kind=db prints table
  • tsh request search --kind=db --format text prints table
  • tsh request search --kind=db --format json prints valid json
  • tsh request search --kind=db --format yaml prints valid yaml

same for other resources like node, kube_cluster, etc.

@tangyatsu tangyatsu force-pushed the tangyatsu/tsh-request-search-add-support-for-format-flag branch 2 times, most recently from b53ef20 to c09a073 Compare December 4, 2025 20:34
@tangyatsu tangyatsu marked this pull request as ready for review December 4, 2025 20:35
@tangyatsu tangyatsu requested a review from ravicious December 4, 2025 20:36
@github-actions github-actions bot added size/lg tsh tsh - Teleport's command line tool for logging into nodes running Teleport. labels Dec 4, 2025
@tangyatsu tangyatsu requested review from rosstimothy and removed request for juliaogris and probakowski December 4, 2025 20:36
var err error

if format == teleport.JSON {
out, err = utils.FastMarshalIndent(rows, "", " ")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we use utils.WriteJSONArray here instead? I think that ensures we write an empty [] if there was no data.

(Similarly, we have utils.WriteYAML for the same purpose)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I ended up adding utils.WriteYAMLArray, because utils.WriteYAML serializes slices as multi-document YAML, which isn’t consistent with how other tsh commands print YAML.
There was also an issue with utils.WriteJSONArray, it printed an empty array without a trailing newline, so I changed it.

@tangyatsu tangyatsu force-pushed the tangyatsu/tsh-request-search-add-support-for-format-flag branch 2 times, most recently from 46e3e54 to 783b3cb Compare December 8, 2025 17:30
Copy link
Copy Markdown
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

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

Submitted a couple of suggestions but no major issues, looks fine.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I noticed that the order of the columns is consistent between text and json but not yaml. Do you think it's something we could improve?

Example output between three formats
$ tsh request search --kind=node --format text
Name                                 Hostname             Labels Resource ID
------------------------------------ -------------------- ------ -----------------------------------------------------------
339cc7f2-2d53-458a-92e2-09e96798c64d mbp.enterprise-local        /enterprise-local/node/339cc7f2-2d53-458a-92e2-09e96798c64d

To request access to these resources, run
> tsh request create --resource /enterprise-local/node/339cc7f2-2d53-458a-92e2-09e96798c64d \
    --reason <request reason>

$ tsh request search --kind=node --format json
[
    {
        "Name": "339cc7f2-2d53-458a-92e2-09e96798c64d",
        "Hostname": "mbp.enterprise-local",
        "Labels": "",
        "ResourceID": "/enterprise-local/node/339cc7f2-2d53-458a-92e2-09e96798c64d"
    }
]
$ tsh request search --kind=node --format yaml
- Hostname: mbp.enterprise-local
  Labels: ""
  Name: 339cc7f2-2d53-458a-92e2-09e96798c64d
  ResourceID: /enterprise-local/node/339cc7f2-2d53-458a-92e2-09e96798c64d

Copy link
Copy Markdown
Contributor Author

@tangyatsu tangyatsu Dec 8, 2025

Choose a reason for hiding this comment

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

I'm not sure this is something I can improve in this PR.
The inconsistency comes from the YAML library we use github.com/ghodss/yaml, which firstly converts structs into JSON, then unmarshals that JSON into a generic interface{} using gopkg.in/yaml.v2, and then marshals it back to YAML.

Here is the function it uses:

func yamlToJSON(y []byte, jsonTarget *reflect.Value) ([]byte, error) {
	// Convert the YAML to an object.
	var yamlObj interface{}
	err := yaml.Unmarshal(y, &yamlObj)
	if err != nil {
		return nil, err
	}

	// YAML objects are not completely compatible with JSON objects (e.g. you
	// can have non-string keys in YAML). So, convert the YAML-compatible object
	// to a JSON-compatible object, failing with an error if irrecoverable
	// incompatibilties happen along the way.
	jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget)
	if err != nil {
		return nil, err
	}

	// Convert this object to JSON and return the data.
	return json.Marshal(jsonObj)
}

Unmarshalling into interface{} turns the object into a map[interface{}]interface{}, and yaml.v2 marshals map keys in alphabetical order.

This library also depends on gopkg.in/yaml.v2, even though v3 already exists, and the library itself hasn’t been updated in over four years.

To fix this, we would likely need to switch to using Go’s gopkg.in/yaml.v3 directly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for looking into this. In that case let's just not worry about it. It'd have been nice but it doesn't seem like it's worth the effort.

@tangyatsu tangyatsu force-pushed the tangyatsu/tsh-request-search-add-support-for-format-flag branch from 783b3cb to 626a237 Compare December 8, 2025 21:04
@tangyatsu tangyatsu requested a review from zmb3 December 8, 2025 21:11
@tangyatsu tangyatsu added this pull request to the merge queue Dec 9, 2025
Merged via the queue into master with commit 9eaaa50 Dec 9, 2025
43 checks passed
@tangyatsu tangyatsu deleted the tangyatsu/tsh-request-search-add-support-for-format-flag branch December 9, 2025 15:10
@backport-bot-workflows
Copy link
Copy Markdown
Contributor

@tangyatsu See the table below for backport results.

Branch Result
branch/v18 Create PR

21KennethTran pushed a commit that referenced this pull request Jan 6, 2026
* tsh request search, add support for --format flag

* fix missing newline when WriteJSONArray serializes empty arrays
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/branch/v18 size/lg tsh tsh - Teleport's command line tool for logging into nodes running Teleport.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tsh request search should support --format

3 participants