forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbase_helpers.go
272 lines (230 loc) · 6.56 KB
/
base_helpers.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
package command
import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/hashicorp/vault/api"
kvbuilder "github.com/hashicorp/vault/helper/kv-builder"
"github.com/kr/text"
homedir "github.com/mitchellh/go-homedir"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/ryanuber/columnize"
)
// extractListData reads the secret and returns a typed list of data and a
// boolean indicating whether the extraction was successful.
func extractListData(secret *api.Secret) ([]interface{}, bool) {
if secret == nil || secret.Data == nil {
return nil, false
}
k, ok := secret.Data["keys"]
if !ok || k == nil {
return nil, false
}
i, ok := k.([]interface{})
return i, ok
}
// sanitizePath removes any leading or trailing things from a "path".
func sanitizePath(s string) string {
return ensureNoTrailingSlash(ensureNoLeadingSlash(strings.TrimSpace(s)))
}
// ensureTrailingSlash ensures the given string has a trailing slash.
func ensureTrailingSlash(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
for len(s) > 0 && s[len(s)-1] != '/' {
s = s + "/"
}
return s
}
// ensureNoTrailingSlash ensures the given string has a trailing slash.
func ensureNoTrailingSlash(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
for len(s) > 0 && s[len(s)-1] == '/' {
s = s[:len(s)-1]
}
return s
}
// ensureNoLeadingSlash ensures the given string has a trailing slash.
func ensureNoLeadingSlash(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
for len(s) > 0 && s[0] == '/' {
s = s[1:]
}
return s
}
// columnOuput prints the list of items as a table with no headers.
func columnOutput(list []string, c *columnize.Config) string {
if len(list) == 0 {
return ""
}
if c == nil {
c = &columnize.Config{}
}
if c.Glue == "" {
c.Glue = " "
}
if c.Empty == "" {
c.Empty = "n/a"
}
return columnize.Format(list, c)
}
// tableOutput prints the list of items as columns, where the first row is
// the list of headers.
func tableOutput(list []string, c *columnize.Config) string {
if len(list) == 0 {
return ""
}
delim := "|"
if c != nil && c.Delim != "" {
delim = c.Delim
}
underline := ""
headers := strings.Split(list[0], delim)
for i, h := range headers {
h = strings.TrimSpace(h)
u := strings.Repeat("-", len(h))
underline = underline + u
if i != len(headers)-1 {
underline = underline + delim
}
}
list = append(list, "")
copy(list[2:], list[1:])
list[1] = underline
return columnOutput(list, c)
}
// parseArgsData parses the given args in the format key=value into a map of
// the provided arguments. The given reader can also supply key=value pairs.
func parseArgsData(stdin io.Reader, args []string) (map[string]interface{}, error) {
builder := &kvbuilder.Builder{Stdin: stdin}
if err := builder.Add(args...); err != nil {
return nil, err
}
return builder.Map(), nil
}
// parseArgsDataString parses the args data and returns the values as strings.
// If the values cannot be represented as strings, an error is returned.
func parseArgsDataString(stdin io.Reader, args []string) (map[string]string, error) {
raw, err := parseArgsData(stdin, args)
if err != nil {
return nil, err
}
var result map[string]string
if err := mapstructure.WeakDecode(raw, &result); err != nil {
return nil, errors.Wrap(err, "failed to convert values to strings")
}
return result, nil
}
// parseArgsDataStringLists parses the args data and returns the values as
// string lists. If the values cannot be represented as strings, an error is
// returned.
func parseArgsDataStringLists(stdin io.Reader, args []string) (map[string][]string, error) {
raw, err := parseArgsData(stdin, args)
if err != nil {
return nil, err
}
var result map[string][]string
if err := mapstructure.WeakDecode(raw, &result); err != nil {
return nil, errors.Wrap(err, "failed to convert values to strings")
}
return result, nil
}
// truncateToSeconds truncates the given duration to the number of seconds. If
// the duration is less than 1s, it is returned as 0. The integer represents
// the whole number unit of seconds for the duration.
func truncateToSeconds(d time.Duration) int {
d = d.Truncate(1 * time.Second)
// Handle the case where someone requested a ridiculously short increment -
// increments must be larger than a second.
if d < 1*time.Second {
return 0
}
return int(d.Seconds())
}
// printKeyStatus prints the KeyStatus response from the API.
func printKeyStatus(ks *api.KeyStatus) string {
return columnOutput([]string{
fmt.Sprintf("Key Term | %d", ks.Term),
fmt.Sprintf("Install Time | %s", ks.InstallTime.UTC().Format(time.RFC822)),
}, nil)
}
// expandPath takes a filepath and returns the full expanded path, accounting
// for user-relative things like ~/.
func expandPath(s string) string {
if s == "" {
return ""
}
e, err := homedir.Expand(s)
if err != nil {
return s
}
return e
}
// wrapAtLengthWithPadding wraps the given text at the maxLineLength, taking
// into account any provided left padding.
func wrapAtLengthWithPadding(s string, pad int) string {
wrapped := text.Wrap(s, maxLineLength-pad)
lines := strings.Split(wrapped, "\n")
for i, line := range lines {
lines[i] = strings.Repeat(" ", pad) + line
}
return strings.Join(lines, "\n")
}
// wrapAtLength wraps the given text to maxLineLength.
func wrapAtLength(s string) string {
return wrapAtLengthWithPadding(s, 0)
}
// ttlToAPI converts a user-supplied ttl into an API-compatible string. If
// the TTL is 0, this returns the empty string. If the TTL is negative, this
// returns "system" to indicate to use the system values. Otherwise, the
// time.Duration ttl is used.
func ttlToAPI(d time.Duration) string {
if d == 0 {
return ""
}
if d < 0 {
return "system"
}
return d.String()
}
// humanDuration prints the time duration without those pesky zeros.
func humanDuration(d time.Duration) string {
if d == 0 {
return "0s"
}
s := d.String()
if strings.HasSuffix(s, "m0s") {
s = s[:len(s)-2]
}
if idx := strings.Index(s, "h0m"); idx > 0 {
s = s[:idx+1] + s[idx+3:]
}
return s
}
// humanDurationInt prints the given int as if it were a time.Duration number
// of seconds.
func humanDurationInt(i interface{}) interface{} {
switch i.(type) {
case int:
return humanDuration(time.Duration(i.(int)) * time.Second)
case int64:
return humanDuration(time.Duration(i.(int64)) * time.Second)
case json.Number:
if i, err := i.(json.Number).Int64(); err == nil {
return humanDuration(time.Duration(i) * time.Second)
}
}
// If we don't know what type it is, just return the original value
return i
}