From 113662522cb790e5dc8ac34cce72e739727ef776 Mon Sep 17 00:00:00 2001 From: yaacov Date: Tue, 18 Feb 2020 21:32:38 +0200 Subject: [PATCH] escape identifiers, support si units, support lists --- cmd/kubesql/main.go | 6 +++ cmd/kubesql/print.go | 121 +++++++++++++++++++++++++++++++++++++++---- go.mod | 2 +- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/cmd/kubesql/main.go b/cmd/kubesql/main.go index 1821970..5cf3cc2 100644 --- a/cmd/kubesql/main.go +++ b/cmd/kubesql/main.go @@ -53,6 +53,12 @@ func main() { Value: "table", Usage: "Output format, options: table, yaml or json.", }, + &cli.BoolFlag{ + Name: "si-units", + Aliases: []string{"s"}, + Value: false, + Usage: "Parse values with SI units as numbers, (e.g. '1Ki' will be 1024).", + }, &cli.BoolFlag{ Name: "all-namespaces", Aliases: []string{"A"}, diff --git a/cmd/kubesql/print.go b/cmd/kubesql/print.go index d84f4ca..441cb51 100644 --- a/cmd/kubesql/print.go +++ b/cmd/kubesql/print.go @@ -22,6 +22,8 @@ import ( "encoding/json" "fmt" "log" + "math" + "strconv" "strings" "time" @@ -62,11 +64,35 @@ func evalFactory(c *cli.Context, item unstructured.Unstructured) semantics.EvalF } if len(key) > 7 && key[:7] == "labels." { - return item.GetLabels()[key[7:]], len(item.GetLabels()[key]) > 0 + value, ok := item.GetLabels()[key[7:]] + + // Empty label represent the label is present + if ok && len(value) == 0 { + value = "true" + } + + // Missing value + if !ok { + return nil, true + } + + return value, ok } if len(key) > 12 && key[:12] == "annotations." { - return item.GetAnnotations()[key[12:]], len(item.GetLabels()[key]) > 0 + value, ok := item.GetLabels()[key[12:]] + + // Empty annotations represent the annotations is present + if ok && len(value) == 0 { + value = "true" + } + + // Missing value + if !ok { + return nil, true + } + + return value, ok } if key == "created" { @@ -80,16 +106,87 @@ func evalFactory(c *cli.Context, item unstructured.Unstructured) semantics.EvalF // Split the key to work with the `unstructured.Nested` functions. keys := strings.Split(key, ".") - if value, ok, err := unstructured.NestedInt64(item.Object, keys...); ok && err == nil { - return value, true - } + var object interface{} + var objectList []interface{} + var objectMap map[string]interface{} + ok := true + object = item.Object + + for _, key := range keys { + if i, err := strconv.ParseUint(key, 10, 64); err == nil && i > 0 { + objectList, ok = object.([]interface{}) + if !ok { + break + } + + ok = i < uint64(len(objectList)) + if !ok { + break + } - if value, ok, err := unstructured.NestedFloat64(item.Object, keys...); ok && err == nil { - return value, true + object = objectList[i] + } else { + objectMap, ok = object.(map[string]interface{}) + if !ok { + break + } + + object, ok = objectMap[key] + if !ok { + break + } + } } - if value, ok, err := unstructured.NestedString(item.Object, keys...); ok && err == nil { - return value, true + if ok { + switch object.(type) { + case float64: + return object.(float64), true + case int64: + return float64(object.(int64)), true + case string: + if c.Bool("si-units") { + multiplier := 0.0 + s := object.(string) + + // Remove SI `i` if exist + // Note: we support "K", "M", "G" and "Ki", "Mi", "Gi" postfix + if len(s) > 1 && s[len(s)-1:] == "i" { + s = s[:len(s)-1] + } + + // Check for SI postfix + if len(s) > 1 { + postfix := s[len(s)-1:] + switch postfix { + case "K": + multiplier = 1024.0 + case "M": + multiplier = math.Pow(1024, 2) + case "G": + multiplier = math.Pow(1024, 3) + case "T": + multiplier = math.Pow(1024, 4) + case "P": + multiplier = math.Pow(1024, 5) + } + + if multiplier >= 1.0 { + s = s[:len(s)-1] + + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + newValue := float64(i) * multiplier + if c.Bool("verbose") { + log.Printf("converting units, %v (%f)\n", object, newValue) + } + + return newValue, true + } + } + } + } + return object.(string), true + } } if c.Bool("verbose") { @@ -197,6 +294,10 @@ func getFields(kind string) []tableField { title: "PHASE", name: "status.phase", }, + { + title: "hostIP", + name: "status.hostIP", + }, { title: "CREATION_TIME(RFC3339)", name: "created", @@ -269,7 +370,7 @@ func printItemsTable(c *cli.Context, items []unstructured.Unstructured) { for _, field := range fields { if field.width > 0 { - if value, found := evalFunc(field.name); found { + if value, found := evalFunc(field.name); found && value != nil { fmt.Printf(field.template, value) } else { fmt.Printf(field.template, "") diff --git a/go.mod b/go.mod index 492bdd9..a93de4d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/imdario/mergo v0.3.8 // indirect github.com/urfave/cli/v2 v2.1.1 - github.com/yaacov/tree-search-language/v5 v5.1.2 + github.com/yaacov/tree-search-language/v5 v5.1.4 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/yaml.v2 v2.2.8