From 47eb74ba317d0b61920d4dac510bce72e1a6f230 Mon Sep 17 00:00:00 2001 From: Erno Aapa Date: Wed, 28 Mar 2018 06:47:33 +0300 Subject: [PATCH] Update 'eli describe node' to print mounts in human-readable sizes Previously `eli describe node` were printing just bytes of mount sizes. This wasn't really user friendly and "human-readable" as it should so updated the output to print the sizes in GB, MB, etc. --- pkg/printers/humanreadable.go | 4 + .../humanreadable/NodeDetailsTemplate.go | 2 +- vendor.conf | 3 +- vendor/github.com/c2h5oh/datasize/LICENSE | 21 ++ vendor/github.com/c2h5oh/datasize/README.md | 66 ++++++ vendor/github.com/c2h5oh/datasize/datasize.go | 217 ++++++++++++++++++ 6 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 vendor/github.com/c2h5oh/datasize/LICENSE create mode 100644 vendor/github.com/c2h5oh/datasize/README.md create mode 100644 vendor/github.com/c2h5oh/datasize/datasize.go diff --git a/pkg/printers/humanreadable.go b/pkg/printers/humanreadable.go index 679f0d26..73d0c7a2 100644 --- a/pkg/printers/humanreadable.go +++ b/pkg/printers/humanreadable.go @@ -18,6 +18,7 @@ import ( "github.com/ernoaapa/eliot/pkg/utils" "github.com/pkg/errors" + "github.com/c2h5oh/datasize" "github.com/hako/durafmt" ) @@ -109,6 +110,9 @@ func (p *HumanReadablePrinter) PrintNode(info *node.Info, writer io.Writer) erro "Subtract": func(a, b uint64) uint64 { return a - b }, + "FormatBytes": func(v uint64) string { + return datasize.ByteSize(v).HumanReadable() + }, }) t, err := t.Parse(humanreadable.NodeDetailsTemplate) if err != nil { diff --git a/pkg/printers/humanreadable/NodeDetailsTemplate.go b/pkg/printers/humanreadable/NodeDetailsTemplate.go index a5906b32..a7a81be0 100644 --- a/pkg/printers/humanreadable/NodeDetailsTemplate.go +++ b/pkg/printers/humanreadable/NodeDetailsTemplate.go @@ -20,7 +20,7 @@ Filesystems: Filesystem Type Size Used Available Use% Mounted on ---------- ---- ---- ---- --------- ---- ---------- {{- range .Filesystems}} - {{.Filesystem}} {{.TypeName}} {{.Total}} {{Subtract .Total .Free}} {{.Available}} {{FormatPercent .Total .Free .Available}} {{.MountDir}} + {{.Filesystem}} {{.TypeName}} {{FormatBytes .Total}} {{Subtract .Total .Free | FormatBytes}} {{FormatBytes .Available}} {{FormatPercent .Total .Free .Available}} {{.MountDir}} {{- end}} {{- end}} ` diff --git a/vendor.conf b/vendor.conf index 2f64e179..cbc9b209 100644 --- a/vendor.conf +++ b/vendor.conf @@ -41,4 +41,5 @@ k8s.io/kubernetes v1.9.0-alpha.2 github.com/thejerf/suture v2.0.1 github.com/ghodss/yaml v1.0.0 github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 -github.com/hako/durafmt 987f93c94e473e74aadc826871e61ae6b3360ebb \ No newline at end of file +github.com/hako/durafmt 987f93c94e473e74aadc826871e61ae6b3360ebb +github.com/c2h5oh/datasize 4eba002a5eaea69cf8d235a388fc6b65ae68d2dd \ No newline at end of file diff --git a/vendor/github.com/c2h5oh/datasize/LICENSE b/vendor/github.com/c2h5oh/datasize/LICENSE new file mode 100644 index 00000000..f2ba916e --- /dev/null +++ b/vendor/github.com/c2h5oh/datasize/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Maciej Lisiewski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/c2h5oh/datasize/README.md b/vendor/github.com/c2h5oh/datasize/README.md new file mode 100644 index 00000000..d21f136b --- /dev/null +++ b/vendor/github.com/c2h5oh/datasize/README.md @@ -0,0 +1,66 @@ +# datasize [![Build Status](https://travis-ci.org/c2h5oh/datasize.svg?branch=master)](https://travis-ci.org/c2h5oh/datasize) + +Golang helpers for data sizes + + +### Constants +Just like `time` package provides `time.Second`, `time.Day` constants `datasize` provides: +* `datasize.B` 1 byte +* `datasize.KB` 1 kilobyte +* `datasize.MB` 1 megabyte +* `datasize.GB` 1 gigabyte +* `datasize.TB` 1 terabyte +* `datasize.PB` 1 petabyte +* `datasize.EB` 1 exabyte + +### Helpers +Just like `time` package provides `duration.Nanoseconds() uint64 `, `duration.Hours() float64` helpers `datasize` has +* `ByteSize.Bytes() uint64` +* `ByteSize.Kilobytes() float4` +* `ByteSize.Megabytes() float64` +* `ByteSize.Gigabytes() float64` +* `ByteSize.Terabytes() float64` +* `ByteSize.Petebytes() float64` +* `ByteSize.Exabytes() float64` + +Warning: see limitations at the end of this document about a possible precission loss + +### Parsing strings +`datasize.ByteSize` implements `TextUnmarshaler` interface and will automatically parse human readable strings into correct values where it is used: +* `"10 MB"` -> `10* datasize.MB` +* `"10240 g"` -> `10 * datasize.TB` +* `"2000"` -> `2000 * datasize.B` +* `"1tB"` -> `datasize.TB` +* `"5 peta"` -> `5 * datasize.PB` +* `"28 kilobytes"` -> `28 * datasize.KB` +* `"1 gigabyte"` -> `1 * datasize.GB` + +You can also do it manually: +```go +var v datasize.ByteSize +err := v.UnmarshalText([]byte("100 mb")) +``` + +### Printing +`Bytesize.String()` uses largest unit allowing an integer value: + * `(102400 * datasize.MB).String()` -> `"100GB"` + * `(datasize.MB + datasize.KB).String()` -> `"1025KB"` + +Use `%d` format string to get value in bytes without a unit + +### JSON and other encoding +Both `TextMarshaler` and `TextUnmarshaler` interfaces are implemented - JSON will just work. Other encoders will work provided they use those interfaces. + +### Human readable +`ByteSize.HumanReadable()` or `ByteSize.HR()` returns a string with 1-3 digits, followed by 1 decimal place, a space and unit big enough to get 1-3 digits + + * `(102400 * datasize.MB).String()` -> `"100.0 GB"` + * `(datasize.MB + 512 * datasize.KB).String()` -> `"1.5 MB"` + +### Limitations +* The underlying data type for `data.ByteSize` is `uint64`, so values outside of 0 to 2^64-1 range will overflow +* size helper functions (like `ByteSize.Kilobytes()`) return `float64`, which can't represent all possible values of `uint64` accurately: + * if the returned value is supposed to have no fraction (ie `(10 * datasize.MB).Kilobytes()`) accuracy loss happens when value is more than 2^53 larger than unit: `.Kilobytes()` over 8 petabytes, `.Megabytes()` over 8 exabytes + * if the returned value is supposed to have a fraction (ie `(datasize.PB + datasize.B).Megabytes()`) in addition to the above note accuracy loss may occur in fractional part too - larger integer part leaves fewer bytes to store fractional part, the smaller the remainder vs unit the move bytes are required to store the fractional part +* Parsing a string with `Mb`, `Tb`, etc units will return a syntax error, because capital followed by lower case is commonly used for bits, not bytes +* Parsing a string with value exceeding 2^64-1 bytes will return 2^64-1 and an out of range error diff --git a/vendor/github.com/c2h5oh/datasize/datasize.go b/vendor/github.com/c2h5oh/datasize/datasize.go new file mode 100644 index 00000000..67547881 --- /dev/null +++ b/vendor/github.com/c2h5oh/datasize/datasize.go @@ -0,0 +1,217 @@ +package datasize + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +type ByteSize uint64 + +const ( + B ByteSize = 1 + KB = B << 10 + MB = KB << 10 + GB = MB << 10 + TB = GB << 10 + PB = TB << 10 + EB = PB << 10 + + fnUnmarshalText string = "UnmarshalText" + maxUint64 uint64 = (1 << 64) - 1 + cutoff uint64 = maxUint64 / 10 +) + +var ErrBits = errors.New("unit with capital unit prefix and lower case unit (b) - bits, not bytes ") + +func (b ByteSize) Bytes() uint64 { + return uint64(b) +} + +func (b ByteSize) KBytes() float64 { + v := b / KB + r := b % KB + return float64(v) + float64(r)/float64(KB) +} + +func (b ByteSize) MBytes() float64 { + v := b / MB + r := b % MB + return float64(v) + float64(r)/float64(MB) +} + +func (b ByteSize) GBytes() float64 { + v := b / GB + r := b % GB + return float64(v) + float64(r)/float64(GB) +} + +func (b ByteSize) TBytes() float64 { + v := b / TB + r := b % TB + return float64(v) + float64(r)/float64(TB) +} + +func (b ByteSize) PBytes() float64 { + v := b / PB + r := b % PB + return float64(v) + float64(r)/float64(PB) +} + +func (b ByteSize) EBytes() float64 { + v := b / EB + r := b % EB + return float64(v) + float64(r)/float64(EB) +} + +func (b ByteSize) String() string { + switch { + case b == 0: + return fmt.Sprint("0B") + case b%EB == 0: + return fmt.Sprintf("%dEB", b/EB) + case b%PB == 0: + return fmt.Sprintf("%dPB", b/PB) + case b%TB == 0: + return fmt.Sprintf("%dTB", b/TB) + case b%GB == 0: + return fmt.Sprintf("%dGB", b/GB) + case b%MB == 0: + return fmt.Sprintf("%dMB", b/MB) + case b%KB == 0: + return fmt.Sprintf("%dKB", b/KB) + default: + return fmt.Sprintf("%dB", b) + } +} + +func (b ByteSize) HR() string { + return b.HumanReadable() +} + +func (b ByteSize) HumanReadable() string { + switch { + case b > EB: + return fmt.Sprintf("%.1f EB", b.EBytes()) + case b > PB: + return fmt.Sprintf("%.1f PB", b.PBytes()) + case b > TB: + return fmt.Sprintf("%.1f TB", b.TBytes()) + case b > GB: + return fmt.Sprintf("%.1f GB", b.GBytes()) + case b > MB: + return fmt.Sprintf("%.1f MB", b.MBytes()) + case b > KB: + return fmt.Sprintf("%.1f KB", b.KBytes()) + default: + return fmt.Sprintf("%d B", b) + } +} + +func (b ByteSize) MarshalText() ([]byte, error) { + return []byte(b.String()), nil +} + +func (b *ByteSize) UnmarshalText(t []byte) error { + var val uint64 + var unit string + + // copy for error message + t0 := t + + var c byte + var i int + +ParseLoop: + for i < len(t) { + c = t[i] + switch { + case '0' <= c && c <= '9': + if val > cutoff { + goto Overflow + } + + c = c - '0' + val *= 10 + + if val > val+uint64(c) { + // val+v overflows + goto Overflow + } + val += uint64(c) + i++ + + default: + if i == 0 { + goto SyntaxError + } + break ParseLoop + } + } + + unit = strings.TrimSpace(string(t[i:])) + switch unit { + case "Kb", "Mb", "Gb", "Tb", "Pb", "Eb": + goto BitsError + } + unit = strings.ToLower(unit) + switch unit { + case "", "b", "byte": + // do nothing - already in bytes + + case "k", "kb", "kilo", "kilobyte", "kilobytes": + if val > maxUint64/uint64(KB) { + goto Overflow + } + val *= uint64(KB) + + case "m", "mb", "mega", "megabyte", "megabytes": + if val > maxUint64/uint64(MB) { + goto Overflow + } + val *= uint64(MB) + + case "g", "gb", "giga", "gigabyte", "gigabytes": + if val > maxUint64/uint64(GB) { + goto Overflow + } + val *= uint64(GB) + + case "t", "tb", "tera", "terabyte", "terabytes": + if val > maxUint64/uint64(TB) { + goto Overflow + } + val *= uint64(TB) + + case "p", "pb", "peta", "petabyte", "petabytes": + if val > maxUint64/uint64(PB) { + goto Overflow + } + val *= uint64(PB) + + case "E", "EB", "e", "eb", "eB": + if val > maxUint64/uint64(EB) { + goto Overflow + } + val *= uint64(EB) + + default: + goto SyntaxError + } + + *b = ByteSize(val) + return nil + +Overflow: + *b = ByteSize(maxUint64) + return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrRange} + +SyntaxError: + *b = 0 + return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrSyntax} + +BitsError: + *b = 0 + return &strconv.NumError{fnUnmarshalText, string(t0), ErrBits} +}