@@ -34,6 +34,8 @@ import (
34
34
// PruneImagesRecommendedName is the recommended command name
35
35
const PruneImagesRecommendedName = "images"
36
36
37
+ var errNoToken = errors .New ("you must use a client config with a token" )
38
+
37
39
var (
38
40
imagesLongDesc = templates .LongDesc (`
39
41
Remove image stream tags, images, and image layers by age or usage
@@ -53,11 +55,14 @@ var (
53
55
authority other than the one present in current user's config, you may need to specify it
54
56
using --certificate-authority flag.
55
57
56
- Insecure connection is allowed in following cases unless certificate-authority is specified:
57
- 1. --force-insecure is given
58
- 2. user's config allows for insecure connection (the user logged in to the cluster with
59
- --insecure-skip-tls-verify or allowed for insecure connection)
60
- 3. registry url is not given or it's a private/link-local address` )
58
+ Insecure connection is allowed if certificate-authority is not specified and one of the following
59
+ conditions holds true:
60
+
61
+ 1. --force-insecure is given
62
+ 2. provided registry-url is prefixed with http://
63
+ 3. registry url is a private or link-local address
64
+ 4. user's config allows for insecure connection (the user logged in to the cluster with
65
+ --insecure-skip-tls-verify or allowed for insecure connection)` )
61
66
62
67
imagesExample = templates .Examples (`
63
68
# See, what the prune command would delete if only images more than an hour old and obsoleted
72
77
%[1]s %[2]s --prune-over-size-limit
73
78
74
79
# To actually perform the prune operation, the confirm flag must be appended
75
- %[1]s %[2]s --prune-over-size-limit --confirm` )
80
+ %[1]s %[2]s --prune-over-size-limit --confirm
81
+
82
+ # Force the insecure http protocol with the particular registry host name
83
+ %[1]s %[2]s --registry-url=http://registry.example.org --confirm
84
+
85
+ # Force a secure connection with a custom certificate authority to the particular registry host name
86
+ %[1]s %[2]s --registry-url=registry.example.org --certificate-authority=/path/to/custom/ca.crt --confirm` )
76
87
)
77
88
78
89
var (
@@ -93,11 +104,10 @@ type PruneImagesOptions struct {
93
104
Namespace string
94
105
ForceInsecure bool
95
106
96
- OSClient client.Interface
97
- KClient kclientset.Interface
98
- RegistryClient * http.Client
99
- Out io.Writer
100
- Insecure bool
107
+ ClientConfig * restclient.Config
108
+ OSClient client.Interface
109
+ KubeClient kclientset.Interface
110
+ Out io.Writer
101
111
}
102
112
103
113
// NewCmdPruneImages implements the OpenShift cli prune images command.
@@ -131,7 +141,7 @@ func NewCmdPruneImages(f *clientcmd.Factory, parentName, name string, out io.Wri
131
141
cmd .Flags ().IntVar (opts .KeepTagRevisions , "keep-tag-revisions" , * opts .KeepTagRevisions , "Specify the number of image revisions for a tag in an image stream that will be preserved." )
132
142
cmd .Flags ().BoolVar (opts .PruneOverSizeLimit , "prune-over-size-limit" , * opts .PruneOverSizeLimit , "Specify if images which are exceeding LimitRanges (see 'openshift.io/Image'), specified in the same namespace, should be considered for pruning. This flag cannot be combined with --keep-younger-than nor --keep-tag-revisions." )
133
143
cmd .Flags ().StringVar (& opts .CABundle , "certificate-authority" , opts .CABundle , "The path to a certificate authority bundle to use when communicating with the managed Docker registries. Defaults to the certificate authority data from the current user's config file. It cannot be used together with --force-insecure." )
134
- cmd .Flags ().StringVar (& opts .RegistryUrlOverride , "registry-url" , opts .RegistryUrlOverride , "The address to use when contacting the registry, instead of using the default value. This is useful if you can't resolve or reach the registry (e.g.; the default is a cluster-internal URL) but you do have an alternative route that works." )
144
+ cmd .Flags ().StringVar (& opts .RegistryUrlOverride , "registry-url" , opts .RegistryUrlOverride , "The address to use when contacting the registry, instead of using the default value. This is useful if you can't resolve or reach the registry (e.g.; the default is a cluster-internal URL) but you do have an alternative route that works. Particular transport protocol can be enforced using '<scheme>://' prefix. " )
135
145
cmd .Flags ().BoolVar (& opts .ForceInsecure , "force-insecure" , opts .ForceInsecure , "If true, allow an insecure connection to the docker registry that is hosted via HTTP or has an invalid HTTPS certificate. Whenever possible, use --certificate-authority instead of this dangerous option." )
136
146
137
147
return cmd
@@ -169,18 +179,14 @@ func (o *PruneImagesOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command,
169
179
if err != nil {
170
180
return err
171
181
}
182
+ o .ClientConfig = clientConfig
172
183
173
- o .Insecure = o .ForceInsecure
174
- if ! o .Insecure && len (o .CABundle ) == 0 {
175
- o .Insecure = clientConfig .TLSClientConfig .Insecure || len (o .RegistryUrlOverride ) == 0 || netutils .IsPrivateAddress (o .RegistryUrlOverride )
176
- }
177
- osClient , kClient , registryClient , err := getClients (f , o .CABundle , o .Insecure )
184
+ osClient , kubeClient , err := getClients (f )
178
185
if err != nil {
179
186
return err
180
187
}
181
188
o .OSClient = osClient
182
- o .KClient = kClient
183
- o .RegistryClient = registryClient
189
+ o .KubeClient = kubeClient
184
190
185
191
return nil
186
192
}
@@ -196,12 +202,15 @@ func (o PruneImagesOptions) Validate() error {
196
202
if o .KeepTagRevisions != nil && * o .KeepTagRevisions < 0 {
197
203
return fmt .Errorf ("--keep-tag-revisions must be greater than or equal to 0" )
198
204
}
199
- if _ , err := url . Parse (o .RegistryUrlOverride ); err != nil {
205
+ if err := imageapi . ValidateRegistryURL (o .RegistryUrlOverride ); len ( o . RegistryUrlOverride ) > 0 && err != nil {
200
206
return fmt .Errorf ("invalid --registry-url flag: %v" , err )
201
207
}
202
208
if o .ForceInsecure && len (o .CABundle ) > 0 {
203
209
return fmt .Errorf ("--certificate-authority cannot be specified with --force-insecure" )
204
210
}
211
+ if len (o .CABundle ) > 0 && strings .HasPrefix (o .RegistryUrlOverride , "http://" ) {
212
+ return fmt .Errorf ("--cerificate-authority cannot be specified for insecure http protocol" )
213
+ }
205
214
return nil
206
215
}
207
216
@@ -217,12 +226,12 @@ func (o PruneImagesOptions) Run() error {
217
226
return err
218
227
}
219
228
220
- allPods , err := o .KClient .Core ().Pods (o .Namespace ).List (metav1.ListOptions {})
229
+ allPods , err := o .KubeClient .Core ().Pods (o .Namespace ).List (metav1.ListOptions {})
221
230
if err != nil {
222
231
return err
223
232
}
224
233
225
- allRCs , err := o .KClient .Core ().ReplicationControllers (o .Namespace ).List (metav1.ListOptions {})
234
+ allRCs , err := o .KubeClient .Core ().ReplicationControllers (o .Namespace ).List (metav1.ListOptions {})
226
235
if err != nil {
227
236
return err
228
237
}
@@ -246,7 +255,7 @@ func (o PruneImagesOptions) Run() error {
246
255
return err
247
256
}
248
257
249
- limitRangesList , err := o .KClient .Core ().LimitRanges (o .Namespace ).List (metav1.ListOptions {})
258
+ limitRangesList , err := o .KubeClient .Core ().LimitRanges (o .Namespace ).List (metav1.ListOptions {})
250
259
if err != nil {
251
260
return err
252
261
}
@@ -261,6 +270,43 @@ func (o PruneImagesOptions) Run() error {
261
270
limitRangesMap [limit .Namespace ] = limits
262
271
}
263
272
273
+ var (
274
+ registryHost = o .RegistryUrlOverride
275
+ registryClient * http.Client
276
+ registryPinger prune.RegistryPinger
277
+ )
278
+
279
+ if o .Confirm {
280
+ if len (registryHost ) == 0 {
281
+ registryHost , err = prune .DetermineRegistryHost (allImages , allStreams )
282
+ if err != nil {
283
+ return fmt .Errorf ("unable to determine registry: %v" , err )
284
+ }
285
+ }
286
+
287
+ insecure := o .ForceInsecure
288
+ if ! insecure && len (o .CABundle ) == 0 {
289
+ insecure = o .ClientConfig .TLSClientConfig .Insecure || netutils .IsPrivateAddress (registryHost ) ||
290
+ strings .HasPrefix (registryHost , "http://" )
291
+ }
292
+
293
+ registryClient , err = getRegistryClient (o .ClientConfig , o .CABundle , insecure )
294
+ if err != nil {
295
+ return err
296
+ }
297
+ registryPinger = & prune.DefaultRegistryPinger {
298
+ Client : registryClient ,
299
+ Insecure : insecure ,
300
+ }
301
+ } else {
302
+ registryPinger = & prune.DryRunRegistryPinger {}
303
+ }
304
+
305
+ registryURL , err := registryPinger .Ping (registryHost )
306
+ if err != nil {
307
+ return fmt .Errorf ("error communicating with registry %s: %v" , registryHost , err )
308
+ }
309
+
264
310
options := prune.PrunerOptions {
265
311
KeepYoungerThan : o .KeepYoungerThan ,
266
312
KeepTagRevisions : o .KeepTagRevisions ,
@@ -275,9 +321,8 @@ func (o PruneImagesOptions) Run() error {
275
321
DCs : allDCs ,
276
322
LimitRanges : limitRangesMap ,
277
323
DryRun : o .Confirm == false ,
278
- RegistryClient : o .RegistryClient ,
279
- RegistryURL : o .RegistryUrlOverride ,
280
- Insecure : o .Insecure ,
324
+ RegistryClient : registryClient ,
325
+ RegistryURL : registryURL ,
281
326
}
282
327
if o .Namespace != metav1 .NamespaceAll {
283
328
options .Namespace = o .Namespace
@@ -316,7 +361,11 @@ type describingImageStreamDeleter struct {
316
361
317
362
var _ prune.ImageStreamDeleter = & describingImageStreamDeleter {}
318
363
319
- func (p * describingImageStreamDeleter ) DeleteImageStream (stream * imageapi.ImageStream , image * imageapi.Image , updatedTags []string ) (* imageapi.ImageStream , error ) {
364
+ func (p * describingImageStreamDeleter ) GetImageStream (stream * imageapi.ImageStream ) (* imageapi.ImageStream , error ) {
365
+ return stream , nil
366
+ }
367
+
368
+ func (p * describingImageStreamDeleter ) UpdateImageStream (stream * imageapi.ImageStream , image * imageapi.Image , updatedTags []string ) (* imageapi.ImageStream , error ) {
320
369
if ! p .headerPrinted {
321
370
p .headerPrinted = true
322
371
fmt .Fprintln (p .w , "Deleting references from image streams to images ..." )
@@ -329,7 +378,7 @@ func (p *describingImageStreamDeleter) DeleteImageStream(stream *imageapi.ImageS
329
378
return stream , nil
330
379
}
331
380
332
- updatedStream , err := p .delegate .DeleteImageStream (stream , image , updatedTags )
381
+ updatedStream , err := p .delegate .UpdateImageStream (stream , image , updatedTags )
333
382
if err != nil {
334
383
fmt .Fprintf (os .Stderr , "error updating image stream %s/%s to remove references to image %s: %v\n " , stream .Namespace , stream .Name , image .Name , err )
335
384
}
@@ -378,7 +427,7 @@ type describingLayerLinkDeleter struct {
378
427
379
428
var _ prune.LayerLinkDeleter = & describingLayerLinkDeleter {}
380
429
381
- func (p * describingLayerLinkDeleter ) DeleteLayerLink (registryClient * http.Client , registryURL , repo , name string ) error {
430
+ func (p * describingLayerLinkDeleter ) DeleteLayerLink (registryClient * http.Client , registryURL * url. URL , repo , name string ) error {
382
431
if ! p .headerPrinted {
383
432
p .headerPrinted = true
384
433
fmt .Fprintln (p .w , "\n Deleting registry repository layer links ..." )
@@ -409,7 +458,7 @@ type describingBlobDeleter struct {
409
458
410
459
var _ prune.BlobDeleter = & describingBlobDeleter {}
411
460
412
- func (p * describingBlobDeleter ) DeleteBlob (registryClient * http.Client , registryURL , layer string ) error {
461
+ func (p * describingBlobDeleter ) DeleteBlob (registryClient * http.Client , registryURL * url. URL , layer string ) error {
413
462
if ! p .headerPrinted {
414
463
p .headerPrinted = true
415
464
fmt .Fprintln (p .w , "\n Deleting registry layer blobs ..." )
@@ -441,7 +490,7 @@ type describingManifestDeleter struct {
441
490
442
491
var _ prune.ManifestDeleter = & describingManifestDeleter {}
443
492
444
- func (p * describingManifestDeleter ) DeleteManifest (registryClient * http.Client , registryURL , repo , manifest string ) error {
493
+ func (p * describingManifestDeleter ) DeleteManifest (registryClient * http.Client , registryURL * url. URL , repo , manifest string ) error {
445
494
if ! p .headerPrinted {
446
495
p .headerPrinted = true
447
496
fmt .Fprintln (p .w , "\n Deleting registry repository manifest data ..." )
@@ -456,46 +505,48 @@ func (p *describingManifestDeleter) DeleteManifest(registryClient *http.Client,
456
505
457
506
err := p .delegate .DeleteManifest (registryClient , registryURL , repo , manifest )
458
507
if err != nil {
459
- fmt .Fprintf (os .Stderr , "error deleting data for repository %s image manifest %s from the registry : %v\n " , repo , manifest , err )
508
+ fmt .Fprintf (os .Stderr , "error deleting manifest %s from repository %s : %v\n " , manifest , repo , err )
460
509
}
461
510
462
511
return err
463
512
}
464
513
465
- // getClients returns a Kube client, OpenShift client, and registry client. Note that
466
- // registryCABundle and registryInsecure=true are mutually exclusive. If registryInsecure=true is
467
- // specified, the ca bundle is ignored.
468
- func getClients (f * clientcmd.Factory , registryCABundle string , registryInsecure bool ) (* client.Client , kclientset.Interface , * http.Client , error ) {
514
+ // getClients returns a OpenShift client and Kube client.
515
+ func getClients (f * clientcmd.Factory ) (* client.Client , kclientset.Interface , error ) {
469
516
clientConfig , err := f .ClientConfig ()
470
517
if err != nil {
471
- return nil , nil , nil , err
518
+ return nil , nil , err
519
+ }
520
+
521
+ if len (clientConfig .BearerToken ) == 0 {
522
+ return nil , nil , errNoToken
472
523
}
473
524
525
+ osClient , kubeClient , err := f .Clients ()
526
+ if err != nil {
527
+ return nil , nil , err
528
+ }
529
+ return osClient , kubeClient , err
530
+ }
531
+
532
+ // getRegistryClient returns a registry client. Note that registryCABundle and registryInsecure=true are
533
+ // mutually exclusive. If registryInsecure=true is specified, the ca bundle is ignored.
534
+ func getRegistryClient (clientConfig * restclient.Config , registryCABundle string , registryInsecure bool ) (* http.Client , error ) {
474
535
var (
475
- token string
476
- osClient * client. Client
477
- kClient kclientset. Interface
478
- registryClient * http. Client
536
+ err error
537
+ cadata [] byte
538
+ registryCABundleIncluded = false
539
+ token = clientConfig . BearerToken
479
540
)
480
541
481
- switch {
482
- case len (clientConfig .BearerToken ) > 0 :
483
- osClient , kClient , err = f .Clients ()
484
- if err != nil {
485
- return nil , nil , nil , err
486
- }
487
- token = clientConfig .BearerToken
488
- default :
489
- err = errors .New ("you must use a client config with a token" )
490
- return nil , nil , nil , err
542
+ if len (token ) == 0 {
543
+ return nil , errNoToken
491
544
}
492
545
493
- cadata := []byte {}
494
- registryCABundleIncluded := false
495
546
if len (registryCABundle ) > 0 {
496
547
cadata , err = ioutil .ReadFile (registryCABundle )
497
548
if err != nil {
498
- return nil , nil , nil , fmt .Errorf ("failed to read registry ca bundle: %v" , err )
549
+ return nil , fmt .Errorf ("failed to read registry ca bundle: %v" , err )
499
550
}
500
551
}
501
552
@@ -531,7 +582,7 @@ func getClients(f *clientcmd.Factory, registryCABundle string, registryInsecure
531
582
532
583
tlsConfig , err := restclient .TLSConfigFor (& registryClientConfig )
533
584
if err != nil {
534
- return nil , nil , nil , err
585
+ return nil , err
535
586
}
536
587
537
588
// Add the CA bundle to the client config's CA roots if provided and we haven't done that already.
@@ -549,12 +600,10 @@ func getClients(f *clientcmd.Factory, registryCABundle string, registryInsecure
549
600
550
601
wrappedTransport , err := restclient .HTTPWrappersForConfig (& registryClientConfig , transport )
551
602
if err != nil {
552
- return nil , nil , nil , err
603
+ return nil , err
553
604
}
554
605
555
- registryClient = & http.Client {
606
+ return & http.Client {
556
607
Transport : wrappedTransport ,
557
- }
558
-
559
- return osClient , kClient , registryClient , nil
608
+ }, nil
560
609
}
0 commit comments