1
1
package main
2
2
3
3
import (
4
+ "context"
4
5
"encoding/json"
5
6
"errors"
6
7
"flag"
@@ -38,9 +39,28 @@ var rootCmd = &cobra.Command{
38
39
Use : "kubectl view-cert [flags] [secret-name [secret-key]]" ,
39
40
SilenceUsage : true , // for when RunE returns an error
40
41
Short : "View certificate information stored in secrets" ,
41
- Example : "kubectl view-cert" ,
42
- RunE : run ,
43
- Version : versionString (),
42
+ Example : "# List certificates from secrets in current namespace \n " +
43
+ "kubectl view-cert \n " +
44
+ "\n " +
45
+ "# List certificates from secrets in all namespaces \n " +
46
+ "kubectl view-cert -A \n " +
47
+ "\n " +
48
+ "# List expired certificates from secrets in all namespaces \n " +
49
+ "kubectl view-cert -A -E \n " +
50
+ "\n " +
51
+ "# List certificates that will expire in 90 days in all namespaces \n " +
52
+ "kubectl view-cert -A -D 90 \n " +
53
+ "\n " +
54
+ "# If you want to include CA certificate informations you can use -S flag \n " +
55
+ "\n " +
56
+ "# View certificate from a specific secret (secret is parsed only if its type is kubernetes.io.tls) \n " +
57
+ "kubectl view-cert mysecret \n " +
58
+ "\n " +
59
+ "# View certificate from a specific key in a specific secret (secret type could be anything as long as secret key contains base64 pem encoded data) \n " +
60
+ "kubectl view-cert mysecret mykey \n " ,
61
+
62
+ RunE : run ,
63
+ Version : versionString (),
44
64
}
45
65
46
66
// versionString returns the version prefixed by 'v'
@@ -93,61 +113,78 @@ func getNamespace() string {
93
113
func main () {
94
114
defer klog .Flush ()
95
115
if err := rootCmd .Execute (); err != nil {
96
- klog .Error (err )
97
116
return
98
117
}
99
118
}
100
119
101
- func run (command * cobra.Command , args []string ) error {
102
- klog .V (1 ).Info ("Run kubectl view-cert" )
103
-
104
- // Parse flags and arguments
120
+ func parseFlagsAndArguments (command * cobra.Command , args []string ) (allNs , expired , showCaCert bool , expiredInDays int , secretName , secretKey string ) {
105
121
allNs , err := command .Flags ().GetBool (allNamespacesFlag )
106
122
if err != nil {
107
123
allNs = false
108
124
}
109
125
110
- expired , err : = command .Flags ().GetBool (expiredFlag )
126
+ expired , err = command .Flags ().GetBool (expiredFlag )
111
127
if err != nil {
112
128
expired = false
113
129
}
114
130
115
- showCaCert , err : = command .Flags ().GetBool (showCaCertFlag )
131
+ showCaCert , err = command .Flags ().GetBool (showCaCertFlag )
116
132
if err != nil {
117
133
showCaCert = false
118
134
}
119
135
120
- expiredInDays , err : = command .Flags ().GetInt (expiredDaysFromNowFlag )
136
+ expiredInDays , err = command .Flags ().GetInt (expiredDaysFromNowFlag )
121
137
if err != nil {
122
138
expiredInDays = 0
123
139
}
124
140
125
- var secretName string
126
141
if len (args ) > 0 {
127
142
secretName = args [0 ]
128
143
}
129
144
130
- var secretKey string
131
145
if len (args ) > 1 {
132
146
secretKey = args [1 ]
133
147
}
134
148
149
+ return
150
+ }
151
+
152
+ func run (command * cobra.Command , args []string ) error {
153
+ klog .V (1 ).Info ("Run kubectl view-cert" )
154
+
155
+ ctx := context .Background ()
156
+
157
+ // Parse flags and arguments
158
+ allNs , expired , showCaCert , expiredInDays , secretName , secretKey := parseFlagsAndArguments (command , args )
159
+
160
+ // Validate inputs
161
+ if allNs && secretName != "" {
162
+ return errors .New ("a resource cannot be retrieved by name across all namespaces" )
163
+ }
164
+
165
+ if secretName != "" && (expired || expiredInDays != 0 || showCaCert ) {
166
+ return errors .New ("when specifying secret name, no flags are allowed, only a second argument with secret key is allowed" )
167
+ }
168
+
135
169
// Prepare clients to interact with kubernetes api
136
170
ns , ri , err := getResourceInterface (allNs , secretName )
137
171
if err != nil {
138
172
return err
139
173
}
140
174
141
175
if secretName != "" {
142
- datas , err := getData (secretName , ns , secretKey , ri )
176
+ datas , err := getData (ctx , secretName , ns , secretKey , ri )
143
177
if err != nil {
144
178
return err
145
179
}
146
180
147
181
// Display
148
- displayDatas (datas )
182
+ err = displayDatas (datas )
183
+ if err != nil {
184
+ return err
185
+ }
149
186
} else {
150
- datas , err := getDatas (ri )
187
+ datas , err := getDatas (ctx , ri )
151
188
if err != nil {
152
189
return err
153
190
}
@@ -168,17 +205,20 @@ func run(command *cobra.Command, args []string) error {
168
205
}
169
206
170
207
// Display
171
- displayDatas (filteredDatas )
208
+ err = displayDatas (filteredDatas )
209
+ if err != nil {
210
+ return err
211
+ }
172
212
}
173
213
174
214
return nil
175
215
}
176
216
177
- func getDatas (ri dynamic.ResourceInterface ) ([]* Certificate , error ) {
217
+ func getDatas (ctx context. Context , ri dynamic.ResourceInterface ) ([]* Certificate , error ) {
178
218
klog .V (1 ).Info ("Scanning secrets" )
179
219
datas := make ([]* Certificate , 0 )
180
220
181
- tlsSecrets , err := ri .List (v1.ListOptions {FieldSelector : "type=kubernetes.io/tls" })
221
+ tlsSecrets , err := ri .List (ctx , v1.ListOptions {FieldSelector : "type=kubernetes.io/tls" })
182
222
if err != nil {
183
223
return datas , fmt .Errorf ("failed to get secrets: %w" , err )
184
224
}
@@ -201,12 +241,12 @@ func getDatas(ri dynamic.ResourceInterface) ([]*Certificate, error) {
201
241
return datas , nil
202
242
}
203
243
204
- func getData (secretName , ns , secretKey string , ri dynamic.ResourceInterface ) ([]* Certificate , error ) {
244
+ func getData (ctx context. Context , secretName , ns , secretKey string , ri dynamic.ResourceInterface ) ([]* Certificate , error ) {
205
245
klog .V (1 ).Infof ("Get secret name %s in namespace %s" , secretName , ns )
206
246
207
247
datas := make ([]* Certificate , 0 )
208
248
209
- secret , err := ri .Get (secretName , v1.GetOptions {})
249
+ secret , err := ri .Get (ctx , secretName , v1.GetOptions {})
210
250
if err != nil {
211
251
return datas , fmt .Errorf ("failed to get secret with name %s: %w" , secretName , err )
212
252
}
@@ -227,13 +267,10 @@ func getData(secretName, ns, secretKey string, ri dynamic.ResourceInterface) ([]
227
267
return datas , nil
228
268
}
229
269
230
- func displayDatas (datas []* Certificate ) {
270
+ func displayDatas (datas []* Certificate ) error {
231
271
encoder := json .NewEncoder (os .Stdout )
232
272
encoder .SetIndent ("" , " " )
233
- err := encoder .Encode (& datas )
234
- if err != nil {
235
- klog .Error (err )
236
- }
273
+ return encoder .Encode (& datas )
237
274
}
238
275
239
276
func getResourceInterface (allNs bool , secretName string ) (string , dynamic.ResourceInterface , error ) {
@@ -281,14 +318,12 @@ func parseData(ns, secretName string, data map[string]interface{}, secretKey str
281
318
282
319
secretCertData , err := parse .NewCertificateData (ns , secretName , data , secretKey )
283
320
if err != nil {
284
- klog .Errorf ("failed to parse secret with name %s in namespace %s %v" , secretName , ns , err )
285
- return nil , nil , err
321
+ return nil , nil , fmt .Errorf ("failed to parse secret with name %s in namespace %s %v" , secretName , ns , err )
286
322
}
287
323
288
324
parsedCerts , err := secretCertData .ParseCertificates ()
289
325
if err != nil {
290
- klog .Errorf ("unable to parse certificates for secret %s in namespace %s %v" , secretName , ns , err )
291
- return nil , nil , err
326
+ return nil , nil , fmt .Errorf ("unable to parse certificates for secret %s in namespace %s %v" , secretName , ns , err )
292
327
}
293
328
294
329
if parsedCerts .Certificate != nil {
0 commit comments