/
get.go
144 lines (126 loc) · 4.56 KB
/
get.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package get
import (
"context"
"encoding/json"
"fmt"
"github.com/google/uuid"
"github.com/jedib0t/go-pretty/table"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/tinkerbell/tink/client"
"github.com/tinkerbell/tink/cmd/tink-cli/cmd/internal/clientctx"
)
type Options struct {
// Headers is the list of headers you want to print as part of the list
Headers []string
// RetrieveData reaches out to Tinkerbell and it gets the required data
RetrieveData func(context.Context, *client.FullClient) ([]interface{}, error)
// RetrieveByID is used when a get command has a list of arguments
RetrieveByID func(context.Context, *client.FullClient, string) (interface{}, error)
// RetrieveByName is used when a get command has a list of arguments
RetrieveByName func(context.Context, *client.FullClient, string) (interface{}, error)
// PopulateTable populates a table with the data retrieved with the RetrieveData function.
PopulateTable func([]interface{}, table.Writer) error
// Format specifies the format you want the list of resources printed
// out. By default it is table but it can be JSON ar CSV.
Format string
// NoHeaders does not print the header line
NoHeaders bool
}
const shortDescr = `display one or many resources`
const longDescr = `Prints a table containing the most important information about a specific
resource. You can specify the kind of output you want to receive. It can be
table, csv or json.
`
const exampleDescr = `# List all hardware in table output format.
tink hardware get
# List all workflow in csv output format.
tink template get --format csv
# List a single template in json output format.
tink workflow get --format json [id]
`
func NewGetCommand(opt Options) *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Short: shortDescr,
Long: longDescr,
Example: exampleDescr,
DisableFlagsInUseLine: true,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
var data []interface{}
t := table.NewWriter()
t.SetOutputMirror(cmd.OutOrStdout())
client := clientctx.Get(cmd.Context())
if len(args) != 0 {
data, err = retrieveMulti(cmd.Context(), opt, client, args)
} else {
if opt.RetrieveData == nil {
return errors.New("get-all-data is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one")
}
data, err = opt.RetrieveData(cmd.Context(), client)
}
if err != nil {
return err
}
if !opt.NoHeaders {
header := table.Row{}
for _, h := range opt.Headers {
header = append(header, h)
}
t.AppendHeader(header)
}
// TODO(gianarb): Technically this is not needed for
// all the output formats but for now that's fine
if err := opt.PopulateTable(data, t); err != nil {
return err
}
switch opt.Format {
case "json":
// TODO(gianarb): the table library we use do
// not support JSON right now. I am not even
// sure I like tables! So complicated...
b, err := json.Marshal(struct {
Data interface{} `json:"data"`
}{Data: data})
if err != nil {
return err
}
fmt.Fprint(cmd.OutOrStdout(), string(b))
case "csv":
t.RenderCSV()
default:
t.Render()
}
return nil
},
}
cmd.PersistentFlags().StringVarP(&opt.Format, "format", "", "table", "The format you expect the list to be printed out. Currently supported format are table, JSON and CSV")
cmd.PersistentFlags().BoolVar(&opt.NoHeaders, "no-headers", false, "Table contains an header with the columns' name. You can disable it from being printed out")
return cmd
}
func retrieveMulti(ctx context.Context, opt Options, fc *client.FullClient, args []string) ([]interface{}, error) {
var data []interface{}
for _, arg := range args {
var retriever func(context.Context, *client.FullClient, string) (interface{}, error)
if _, err := uuid.Parse(arg); err != nil {
// arg is invalid UUID, search for arg in `name` field of db
if opt.RetrieveByName == nil {
return nil, errors.New("get by Name is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one")
}
retriever = opt.RetrieveByName
} else {
// arg is a valid UUID, search for arg in `id` field of db
if opt.RetrieveByID == nil {
return nil, errors.New("get by ID is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one")
}
retriever = opt.RetrieveByID
}
s, err := retriever(ctx, fc, arg)
if err != nil {
continue
}
data = append(data, s)
}
return data, nil
}