Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/utils/jsontools.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ func FastMarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return data, nil
}

// WriteJSONArray marshals values as a JSON array.
func WriteJSONArray[T any](w io.Writer, values []T) error {
if len(values) == 0 {
_, err := w.Write([]byte("[]"))
return err
}
return WriteJSON(w, values)
}

// WriteJSONObject marshals m as a JSON object.
func WriteJSONObject[M ~map[K]V, K comparable, V any](w io.Writer, m M) error {
if len(m) == 0 {
_, err := w.Write([]byte("{}"))
return err
}
return WriteJSON(w, m)
}

// WriteJSON marshals multiple documents as a JSON list with indentation.
func WriteJSON(w io.Writer, values interface{}) error {
encoder := json.NewEncoder(w)
Expand Down Expand Up @@ -156,6 +174,10 @@ func WriteYAML(w io.Writer, values interface{}) error {
}
// first pass makes sure that all values are documents (objects or maps)
slice := reflect.ValueOf(values)
if slice.Len() == 0 {
_, err := w.Write([]byte("[]"))
return err
}
allDocs := func() bool {
for i := 0; i < slice.Len(); i++ {
if !isDoc(slice.Index(i)) {
Expand Down
4 changes: 2 additions & 2 deletions tool/tctl/common/acl_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (c *ACLCommand) List(ctx context.Context, client auth.ClientI) error {
}
}

if len(accessLists) == 0 {
if len(accessLists) == 0 && c.format == teleport.Text {
fmt.Println("no access lists")
return nil
}
Expand Down Expand Up @@ -222,7 +222,7 @@ func displayAccessLists(format string, accessLists ...*accesslist.AccessList) er
case teleport.YAML:
return trace.Wrap(utils.WriteYAML(os.Stdout, accessLists))
case teleport.JSON:
return trace.Wrap(utils.WriteJSON(os.Stdout, accessLists))
return trace.Wrap(utils.WriteJSONArray(os.Stdout, accessLists))
case teleport.Text:
return trace.Wrap(displayAccessListsText(accessLists...))
}
Expand Down
2 changes: 1 addition & 1 deletion tool/tctl/common/alert_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (c *AlertCommand) List(ctx context.Context, client auth.ClientI) error {
return trace.Wrap(err)
}

if len(alerts) == 0 {
if len(alerts) == 0 && c.format == teleport.Text {
fmt.Println("no alerts")
return nil
}
Expand Down
3 changes: 1 addition & 2 deletions tool/tctl/common/bots_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,10 @@ func (c *BotsCommand) ListBots(ctx context.Context, client auth.ClientI) error {
}
fmt.Println(t.AsBuffer().String())
} else {
out, err := json.MarshalIndent(users, "", " ")
err := utils.WriteJSONArray(os.Stdout, users)
if err != nil {
return trace.Wrap(err, "failed to marshal users")
}
fmt.Print(string(out))
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion tool/tctl/common/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (s *serverCollection) writeYAML(w io.Writer) error {
}

func (s *serverCollection) writeJSON(w io.Writer) error {
return utils.WriteJSON(w, s.servers)
return utils.WriteJSONArray(w, s.servers)
}

type userCollection struct {
Expand Down
16 changes: 16 additions & 0 deletions tool/tctl/common/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,22 @@ func mustDecodeJSON[T any](t *testing.T, r io.Reader) T {
return out
}

func mustDecodeYAMLDocuments[T any](t *testing.T, r io.Reader, out *[]T) error {
decoder := yaml.NewDecoder(r)
for {
var entry T
if err := decoder.Decode(&entry); err != nil {
// Break when there are no more documents to decode
if err != io.EOF {
return err
}
break
}
*out = append(*out, entry)
}
return nil
}

func mustDecodeYAML[T any](t *testing.T, r io.Reader) T {
var out T
err := yaml.NewDecoder(r).Decode(&out)
Expand Down
8 changes: 3 additions & 5 deletions tool/tctl/common/token_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ func (c *TokensCommand) List(ctx context.Context, client auth.ClientI) error {
if err != nil {
return trace.Wrap(err)
}
if len(tokens) == 0 {
if len(tokens) == 0 && c.format == teleport.Text {
fmt.Fprintln(c.stdout, "No active tokens found.")
return nil
}
Expand All @@ -376,17 +376,15 @@ func (c *TokensCommand) List(ctx context.Context, client auth.ClientI) error {

switch c.format {
case teleport.JSON:
data, err := json.MarshalIndent(tokens, "", " ")
err := utils.WriteJSONArray(c.stdout, tokens)
if err != nil {
return trace.Wrap(err, "failed to marshal tokens")
}
fmt.Fprint(c.stdout, string(data))
case teleport.YAML:
data, err := yaml.Marshal(tokens)
err := utils.WriteYAML(c.stdout, tokens)
if err != nil {
return trace.Wrap(err, "failed to marshal tokens")
}
fmt.Fprint(c.stdout, string(data))
case teleport.Text:
for _, token := range tokens {
fmt.Fprintln(c.stdout, token.GetName())
Expand Down
4 changes: 3 additions & 1 deletion tool/tctl/common/token_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ func TestTokens(t *testing.T) {

buf, err = runTokensCommand(t, fileConfig, []string{"ls", "--format", teleport.YAML})
require.NoError(t, err)
yamlOut := mustDecodeYAML[[]listedToken](t, buf)
yamlOut := []listedToken{}
err = mustDecodeYAMLDocuments(t, buf, &yamlOut)
require.NoError(t, err)
require.Len(t, yamlOut, 4)

require.Equal(t, jsonOut, yamlOut)
Expand Down
5 changes: 3 additions & 2 deletions tool/tctl/common/user_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"os"
"strconv"
"strings"
"time"
Expand All @@ -37,6 +38,7 @@ import (
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/utils/gcp"
)

Expand Down Expand Up @@ -492,11 +494,10 @@ func (u *UserCommand) List(ctx context.Context, client auth.ClientI) error {
}
fmt.Println(t.AsBuffer().String())
} else {
out, err := json.MarshalIndent(users, "", " ")
err := utils.WriteJSONArray(os.Stdout, users)
if err != nil {
return trace.Wrap(err, "failed to marshal users")
}
fmt.Print(string(out))
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion tool/tsh/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -1617,7 +1617,7 @@ func exportSession(cf *CLIConf) error {

switch format {
case teleport.JSON:
if err := utils.WriteJSON(os.Stdout, events); err != nil {
if err := utils.WriteJSONArray(os.Stdout, events); err != nil {
return trace.Wrap(err)
}
case teleport.YAML:
Expand Down