Browse code

platformmanagement_public_425 - add quota information to oc describe is

Maciej Szulik authored on 2016/03/01 00:52:09
Showing 5 changed files
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	kapi "k8s.io/kubernetes/pkg/api"
10 10
 	"k8s.io/kubernetes/pkg/api/errors"
11
+	kclient "k8s.io/kubernetes/pkg/client/unversioned"
11 12
 	"k8s.io/kubernetes/pkg/fields"
12 13
 	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
13 14
 	"k8s.io/kubernetes/pkg/watch"
... ...
@@ -70,6 +71,7 @@ type ImportImageOptions struct {
70 70
 	// helpers
71 71
 	out      io.Writer
72 72
 	osClient client.Interface
73
+	kClient  kclient.Interface
73 74
 	isClient client.ImageStreamInterface
74 75
 }
75 76
 
... ...
@@ -86,11 +88,12 @@ func (o *ImportImageOptions) Complete(f *clientcmd.Factory, args []string, out i
86 86
 	}
87 87
 	o.Namespace = namespace
88 88
 
89
-	osClient, _, err := f.Clients()
89
+	osClient, kClient, err := f.Clients()
90 90
 	if err != nil {
91 91
 		return err
92 92
 	}
93 93
 	o.osClient = osClient
94
+	o.kClient = kClient
94 95
 	o.isClient = osClient.ImageStreams(namespace)
95 96
 	o.out = out
96 97
 
... ...
@@ -140,7 +143,7 @@ func (o *ImportImageOptions) Run() error {
140 140
 		fmt.Fprint(o.out, "The import completed successfully.\n\n")
141 141
 
142 142
 		// optimization, use the image stream returned by the call
143
-		d := describe.ImageStreamDescriber{Interface: o.osClient}
143
+		d := describe.ImageStreamDescriber{OSClient: o.osClient, KubeClient: o.kClient}
144 144
 		info, err := d.Describe(o.Namespace, stream.Name)
145 145
 		if err != nil {
146 146
 			return err
... ...
@@ -185,7 +188,7 @@ func (o *ImportImageOptions) Run() error {
185 185
 
186 186
 	fmt.Fprint(o.out, "The import completed successfully.\n\n")
187 187
 
188
-	d := describe.ImageStreamDescriber{Interface: o.osClient}
188
+	d := describe.ImageStreamDescriber{OSClient: o.osClient, KubeClient: o.kClient}
189 189
 	info, err := d.Describe(updatedStream.Namespace, updatedStream.Name)
190 190
 	if err != nil {
191 191
 		return err
... ...
@@ -29,6 +29,7 @@ import (
29 29
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
30 30
 	imageapi "github.com/openshift/origin/pkg/image/api"
31 31
 	projectapi "github.com/openshift/origin/pkg/project/api"
32
+
32 33
 	routeapi "github.com/openshift/origin/pkg/route/api"
33 34
 	templateapi "github.com/openshift/origin/pkg/template/api"
34 35
 	userapi "github.com/openshift/origin/pkg/user/api"
... ...
@@ -41,7 +42,7 @@ func describerMap(c *client.Client, kclient kclient.Interface, host string) map[
41 41
 		deployapi.Kind("DeploymentConfig"):            NewDeploymentConfigDescriber(c, kclient),
42 42
 		authorizationapi.Kind("Identity"):             &IdentityDescriber{c},
43 43
 		imageapi.Kind("Image"):                        &ImageDescriber{c},
44
-		imageapi.Kind("ImageStream"):                  &ImageStreamDescriber{c},
44
+		imageapi.Kind("ImageStream"):                  &ImageStreamDescriber{c, kclient},
45 45
 		imageapi.Kind("ImageStreamTag"):               &ImageStreamTagDescriber{c},
46 46
 		imageapi.Kind("ImageStreamImage"):             &ImageStreamImageDescriber{c},
47 47
 		routeapi.Kind("Route"):                        &RouteDescriber{c, kclient},
... ...
@@ -586,12 +587,13 @@ func (d *ImageStreamImageDescriber) Describe(namespace, name string) (string, er
586 586
 
587 587
 // ImageStreamDescriber generates information about a ImageStream
588 588
 type ImageStreamDescriber struct {
589
-	client.Interface
589
+	OSClient   client.Interface
590
+	KubeClient kclient.Interface
590 591
 }
591 592
 
592 593
 // Describe returns the description of an imageStream
593 594
 func (d *ImageStreamDescriber) Describe(namespace, name string) (string, error) {
594
-	c := d.ImageStreams(namespace)
595
+	c := d.OSClient.ImageStreams(namespace)
595 596
 	imageStream, err := c.Get(name)
596 597
 	if err != nil {
597 598
 		return "", err
... ...
@@ -600,6 +602,7 @@ func (d *ImageStreamDescriber) Describe(namespace, name string) (string, error)
600 600
 	return tabbedString(func(out *tabwriter.Writer) error {
601 601
 		formatMeta(out, imageStream.ObjectMeta)
602 602
 		formatString(out, "Docker Pull Spec", imageStream.Status.DockerImageRepository)
603
+		formatImageStreamQuota(out, d.OSClient, d.KubeClient, imageStream)
603 604
 		formatImageStreamTags(out, imageStream)
604 605
 		return nil
605 606
 	})
... ...
@@ -121,7 +121,7 @@ func TestDescribers(t *testing.T) {
121 121
 		&BuildDescriber{c, fakeKube},
122 122
 		&BuildConfigDescriber{c, ""},
123 123
 		&ImageDescriber{c},
124
-		&ImageStreamDescriber{c},
124
+		&ImageStreamDescriber{c, fakeKube},
125 125
 		&ImageStreamTagDescriber{c},
126 126
 		&ImageStreamImageDescriber{c},
127 127
 		&RouteDescriber{c, fakeKube},
... ...
@@ -11,12 +11,15 @@ import (
11 11
 	"github.com/docker/docker/pkg/units"
12 12
 
13 13
 	"k8s.io/kubernetes/pkg/api"
14
+	"k8s.io/kubernetes/pkg/api/resource"
15
+	kclient "k8s.io/kubernetes/pkg/client/unversioned"
14 16
 	"k8s.io/kubernetes/pkg/labels"
15 17
 	"k8s.io/kubernetes/pkg/util/sets"
16 18
 
17 19
 	buildapi "github.com/openshift/origin/pkg/build/api"
18 20
 	"github.com/openshift/origin/pkg/client"
19 21
 	imageapi "github.com/openshift/origin/pkg/image/api"
22
+	imagequota "github.com/openshift/origin/pkg/quota/image"
20 23
 )
21 24
 
22 25
 const emptyString = "<none>"
... ...
@@ -301,3 +304,53 @@ func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream)
301 301
 		}
302 302
 	}
303 303
 }
304
+
305
+func formatImageStreamQuota(out *tabwriter.Writer, c client.Interface, kc kclient.Interface, stream *imageapi.ImageStream) {
306
+	quotas, err := kc.ResourceQuotas(stream.Namespace).List(api.ListOptions{})
307
+	if err != nil {
308
+		return
309
+	}
310
+
311
+	var limit *resource.Quantity
312
+	for _, item := range quotas.Items {
313
+		// search for smallest ImageStream quota
314
+		if value, ok := item.Spec.Hard[imageapi.ResourceImageStreamSize]; ok {
315
+			if limit == nil || limit.Cmp(value) > 0 {
316
+				limit = &value
317
+			}
318
+		}
319
+	}
320
+	if limit != nil {
321
+		quantity := imagequota.GetImageStreamSize(c, stream, make(map[string]*imageapi.Image))
322
+		scale := mega
323
+		if quantity.Value() >= (1<<giga.scale) || limit.Value() >= (1<<giga.scale) {
324
+			scale = giga
325
+		}
326
+		formatString(out, "Quota Usage", fmt.Sprintf("%s / %s",
327
+			formatQuantity(quantity, scale), formatQuantity(limit, scale)))
328
+	}
329
+}
330
+
331
+type scale struct {
332
+	scale uint64
333
+	unit  string
334
+}
335
+
336
+var (
337
+	mega = scale{20, "MiB"}
338
+	giga = scale{30, "GiB"}
339
+)
340
+
341
+// formatQuantity prints quantity according to passed scale. Manual scaling was
342
+// done here to make sure we print correct binary values for quantity.
343
+func formatQuantity(quantity *resource.Quantity, scale scale) string {
344
+	integer := quantity.Value() >> scale.scale
345
+	// fraction is the reminder of a division shifted by one order of magnitude
346
+	fraction := (quantity.Value() % (1 << scale.scale)) >> (scale.scale - 10)
347
+	// additionally we present only 2 digits after dot, so divide by 10
348
+	fraction = fraction / 10
349
+	if fraction > 0 {
350
+		return fmt.Sprintf("%d.%02d%s", integer, fraction, scale.unit)
351
+	}
352
+	return fmt.Sprintf("%d%s", integer, scale.unit)
353
+}
... ...
@@ -6,9 +6,11 @@ import (
6 6
 	"text/tabwriter"
7 7
 	"time"
8 8
 
9
-	imageapi "github.com/openshift/origin/pkg/image/api"
10 9
 	kapi "k8s.io/kubernetes/pkg/api"
10
+	"k8s.io/kubernetes/pkg/api/resource"
11 11
 	"k8s.io/kubernetes/pkg/api/unversioned"
12
+
13
+	imageapi "github.com/openshift/origin/pkg/image/api"
12 14
 )
13 15
 
14 16
 func TestFormatImageStreamTags(t *testing.T) {
... ...
@@ -79,3 +81,45 @@ func TestFormatImageStreamTags(t *testing.T) {
79 79
 	actual := string(buf.String())
80 80
 	t.Logf("\n%s", actual)
81 81
 }
82
+
83
+func TestFormatQuantity(t *testing.T) {
84
+	testCases := []struct {
85
+		value    int64
86
+		scale    scale
87
+		expected string
88
+	}{
89
+		{
90
+			value:    1 << 30,
91
+			scale:    giga,
92
+			expected: "1GiB",
93
+		},
94
+		{
95
+			value:    1 << 20,
96
+			scale:    mega,
97
+			expected: "1MiB",
98
+		},
99
+		{
100
+			value:    10 * (1 << 20),
101
+			scale:    giga,
102
+			expected: "0.01GiB",
103
+		},
104
+		{
105
+			value:    (1 << 30) + (1 << 20),
106
+			scale:    giga,
107
+			expected: "1GiB",
108
+		},
109
+		{
110
+			value:    (1 << 30) + 10*(1<<20),
111
+			scale:    giga,
112
+			expected: "1.01GiB",
113
+		},
114
+	}
115
+
116
+	for idx, test := range testCases {
117
+		quantity := resource.NewQuantity(test.value, resource.BinarySI)
118
+		actual := formatQuantity(quantity, test.scale)
119
+		if actual != test.expected {
120
+			t.Errorf("(%d) expected '%s', got '%s'", idx, test.expected, actual)
121
+		}
122
+	}
123
+}