Bumping k8s dependencies to 1.13
This commit is contained in:
40
vendor/k8s.io/kubernetes/pkg/printers/BUILD
generated
vendored
40
vendor/k8s.io/kubernetes/pkg/printers/BUILD
generated
vendored
@@ -13,28 +13,23 @@ go_library(
|
||||
"customcolumn_flags.go",
|
||||
"humanreadable.go",
|
||||
"interface.go",
|
||||
"jsonpath.go",
|
||||
"jsonpath_flags.go",
|
||||
"kube_template_flags.go",
|
||||
"tabwriter.go",
|
||||
"template.go",
|
||||
"template_flags.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/printers",
|
||||
deps = [
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/kubectl/scheme:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/jsonpath:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/jsonpath:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -61,18 +56,15 @@ go_test(
|
||||
"customcolumn_flags_test.go",
|
||||
"customcolumn_test.go",
|
||||
"humanreadable_test.go",
|
||||
"jsonpath_flags_test.go",
|
||||
"template_flags_test.go",
|
||||
"template_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
],
|
||||
)
|
||||
|
4
vendor/k8s.io/kubernetes/pkg/printers/customcolumn.go
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/printers/customcolumn.go
generated
vendored
@@ -28,14 +28,14 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/client-go/util/jsonpath"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
)
|
||||
|
||||
var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$")
|
||||
|
||||
// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
|
||||
// * metadata.name (no leading '.' or curly brances '{...}'
|
||||
// * metadata.name (no leading '.' or curly braces '{...}'
|
||||
// * {metadata.name} (no leading '.')
|
||||
// * .metadata.name (no curly braces '{...}')
|
||||
// * {.metadata.name} (complete expression)
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/printers/customcolumn_flags.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/printers/customcolumn_flags.go
generated
vendored
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/printers/customcolumn_flags_test.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/printers/customcolumn_flags_test.go
generated
vendored
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
func TestPrinterSupportsExpectedCustomColumnFormats(t *testing.T) {
|
||||
|
14
vendor/k8s.io/kubernetes/pkg/printers/customcolumn_test.go
generated
vendored
14
vendor/k8s.io/kubernetes/pkg/printers/customcolumn_test.go
generated
vendored
@@ -286,8 +286,8 @@ bar
|
||||
},
|
||||
},
|
||||
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
|
||||
expectedOutput: `NAME API_VERSION
|
||||
foo baz
|
||||
expectedOutput: `NAME API_VERSION
|
||||
foo baz
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -306,8 +306,8 @@ foo baz
|
||||
},
|
||||
},
|
||||
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
|
||||
expectedOutput: `NAME API_VERSION NOT_FOUND
|
||||
foo baz <none>
|
||||
expectedOutput: `NAME API_VERSION NOT_FOUND
|
||||
foo baz <none>
|
||||
`,
|
||||
},
|
||||
}
|
||||
@@ -349,9 +349,9 @@ func TestIndividualPrintObjOnExistingTabWriter(t *testing.T) {
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"label1": "foo", "label2": "foo"}}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{"label1": "bar", "label2": "bar"}}},
|
||||
}
|
||||
expectedOutput := `NAME LONG COLUMN NAME LABEL 2
|
||||
foo foo foo
|
||||
bar bar bar
|
||||
expectedOutput := `NAME LONG COLUMN NAME LABEL 2
|
||||
foo foo foo
|
||||
bar bar bar
|
||||
`
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
|
32
vendor/k8s.io/kubernetes/pkg/printers/humanreadable.go
generated
vendored
32
vendor/k8s.io/kubernetes/pkg/printers/humanreadable.go
generated
vendored
@@ -114,17 +114,25 @@ func (h *HumanReadablePrinter) EnsurePrintHeaders() {
|
||||
// See ValidatePrintHandlerFunc for required method signature.
|
||||
func (h *HumanReadablePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
|
||||
var columnDefinitions []metav1beta1.TableColumnDefinition
|
||||
for _, column := range columns {
|
||||
for i, column := range columns {
|
||||
format := ""
|
||||
if i == 0 && strings.EqualFold(column, "name") {
|
||||
format = "name"
|
||||
}
|
||||
|
||||
columnDefinitions = append(columnDefinitions, metav1beta1.TableColumnDefinition{
|
||||
Name: column,
|
||||
Type: "string",
|
||||
Name: column,
|
||||
Description: column,
|
||||
Type: "string",
|
||||
Format: format,
|
||||
})
|
||||
}
|
||||
for _, column := range columnsWithWide {
|
||||
columnDefinitions = append(columnDefinitions, metav1beta1.TableColumnDefinition{
|
||||
Name: column,
|
||||
Type: "string",
|
||||
Priority: 1,
|
||||
Name: column,
|
||||
Description: column,
|
||||
Type: "string",
|
||||
Priority: 1,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -363,6 +371,11 @@ func PrintTable(table *metav1beta1.Table, output io.Writer, options PrintOptions
|
||||
for _, row := range table.Rows {
|
||||
first := true
|
||||
for i, cell := range row.Cells {
|
||||
if i >= len(table.ColumnDefinitions) {
|
||||
// https://issue.k8s.io/66379
|
||||
// don't panic in case of bad output from the server, with more cells than column definitions
|
||||
break
|
||||
}
|
||||
column := table.ColumnDefinitions[i]
|
||||
if !options.Wide && column.Priority != 0 {
|
||||
continue
|
||||
@@ -631,6 +644,13 @@ func (h *HumanReadablePrinter) legacyPrinterToTable(obj runtime.Object, handler
|
||||
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(buf), reflect.ValueOf(options)}
|
||||
|
||||
if meta.IsListType(obj) {
|
||||
listInterface, ok := obj.(metav1.ListInterface)
|
||||
if ok {
|
||||
table.ListMeta.SelfLink = listInterface.GetSelfLink()
|
||||
table.ListMeta.ResourceVersion = listInterface.GetResourceVersion()
|
||||
table.ListMeta.Continue = listInterface.GetContinue()
|
||||
}
|
||||
|
||||
// TODO: this uses more memory than it has to, as we refactor printers we should remove the need
|
||||
// for this.
|
||||
args[0] = reflect.ValueOf(obj)
|
||||
|
91
vendor/k8s.io/kubernetes/pkg/printers/internalversion/BUILD
generated
vendored
91
vendor/k8s.io/kubernetes/pkg/printers/internalversion/BUILD
generated
vendored
@@ -20,6 +20,7 @@ go_test(
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/coordination:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/networking:go_default_library",
|
||||
@@ -27,25 +28,25 @@ go_test(
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/util/pointer:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/yaml:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/yaml:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -65,6 +66,7 @@ go_library(
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/certificates:go_default_library",
|
||||
"//pkg/apis/coordination:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/core/helper/qos:go_default_library",
|
||||
@@ -84,35 +86,36 @@ go_library(
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/autoscaling/v2beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/coordination/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/storage/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/duration:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/github.com/fatih/camelcase:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/autoscaling/v2beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/storage/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/duration:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
280
vendor/k8s.io/kubernetes/pkg/printers/internalversion/describe.go
generated
vendored
280
vendor/k8s.io/kubernetes/pkg/printers/internalversion/describe.go
generated
vendored
@@ -44,6 +44,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/duration"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/dynamic"
|
||||
@@ -275,19 +276,22 @@ func printUnstructuredContent(w PrefixWriter, level int, content map[string]inte
|
||||
func smartLabelFor(field string) string {
|
||||
commonAcronyms := []string{"API", "URL", "UID", "OSB", "GUID"}
|
||||
|
||||
splitted := camelcase.Split(field)
|
||||
for i := 0; i < len(splitted); i++ {
|
||||
part := splitted[i]
|
||||
parts := camelcase.Split(field)
|
||||
result := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if part == "_" {
|
||||
continue
|
||||
}
|
||||
|
||||
if slice.ContainsString(commonAcronyms, strings.ToUpper(part), nil) {
|
||||
part = strings.ToUpper(part)
|
||||
} else {
|
||||
part = strings.Title(part)
|
||||
}
|
||||
splitted[i] = part
|
||||
result = append(result, part)
|
||||
}
|
||||
|
||||
return strings.Join(splitted, " ")
|
||||
return strings.Join(result, " ")
|
||||
}
|
||||
|
||||
// DefaultObjectDescriber can describe the default Kubernetes objects.
|
||||
@@ -639,7 +643,7 @@ func describePod(pod *api.Pod, events *api.EventList) (string, error) {
|
||||
printLabelsMultiline(w, "Labels", pod.Labels)
|
||||
printAnnotationsMultiline(w, "Annotations", pod.Annotations)
|
||||
if pod.DeletionTimestamp != nil {
|
||||
w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestamp(*pod.DeletionTimestamp))
|
||||
w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampUntil(*pod.DeletionTimestamp))
|
||||
w.Write(LEVEL_0, "Termination Grace Period:\t%ds\n", *pod.DeletionGracePeriodSeconds)
|
||||
} else {
|
||||
w.Write(LEVEL_0, "Status:\t%s\n", string(pod.Status.Phase))
|
||||
@@ -1125,6 +1129,48 @@ func printCSIPersistentVolumeSource(csi *api.CSIPersistentVolumeSource, w Prefix
|
||||
" VolumeHandle:\t%v\n"+
|
||||
" ReadOnly:\t%v\n",
|
||||
csi.Driver, csi.VolumeHandle, csi.ReadOnly)
|
||||
printCSIPersistentVolumeAttributesMultiline(w, "VolumeAttributes", csi.VolumeAttributes)
|
||||
}
|
||||
|
||||
func printCSIPersistentVolumeAttributesMultiline(w PrefixWriter, title string, annotations map[string]string) {
|
||||
printCSIPersistentVolumeAttributesMultilineIndent(w, "", title, "\t", annotations, sets.NewString())
|
||||
}
|
||||
|
||||
func printCSIPersistentVolumeAttributesMultilineIndent(w PrefixWriter, initialIndent, title, innerIndent string, attributes map[string]string, skip sets.String) {
|
||||
w.Write(LEVEL_2, "%s%s:%s", initialIndent, title, innerIndent)
|
||||
|
||||
if len(attributes) == 0 {
|
||||
w.WriteLine("<none>")
|
||||
return
|
||||
}
|
||||
|
||||
// to print labels in the sorted order
|
||||
keys := make([]string, 0, len(attributes))
|
||||
for key := range attributes {
|
||||
if skip.Has(key) {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
if len(attributes) == 0 {
|
||||
w.WriteLine("<none>")
|
||||
return
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i != 0 {
|
||||
w.Write(LEVEL_2, initialIndent)
|
||||
w.Write(LEVEL_2, innerIndent)
|
||||
}
|
||||
line := fmt.Sprintf("%s=%s", key, attributes[key])
|
||||
if len(line) > maxAnnotationLen {
|
||||
w.Write(LEVEL_2, "%s...\n", line[:maxAnnotationLen])
|
||||
} else {
|
||||
w.Write(LEVEL_2, "%s\n", line)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
type PersistentVolumeDescriber struct {
|
||||
@@ -1193,12 +1239,12 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
w.Write(LEVEL_0, "Name:\t%s\n", pv.Name)
|
||||
printLabelsMultiline(w, "Labels", pv.Labels)
|
||||
printAnnotationsMultiline(w, "Annotations", pv.Annotations)
|
||||
printLabelsMultiline(w, "Labels", pv.ObjectMeta.Labels)
|
||||
printAnnotationsMultiline(w, "Annotations", pv.ObjectMeta.Annotations)
|
||||
w.Write(LEVEL_0, "Finalizers:\t%v\n", pv.ObjectMeta.Finalizers)
|
||||
w.Write(LEVEL_0, "StorageClass:\t%s\n", helper.GetPersistentVolumeClass(pv))
|
||||
if pv.ObjectMeta.DeletionTimestamp != nil {
|
||||
w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestamp(*pv.ObjectMeta.DeletionTimestamp))
|
||||
w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampUntil(*pv.ObjectMeta.DeletionTimestamp))
|
||||
} else {
|
||||
w.Write(LEVEL_0, "Status:\t%v\n", pv.Status.Phase)
|
||||
}
|
||||
@@ -1287,19 +1333,59 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri
|
||||
return "", err
|
||||
}
|
||||
|
||||
pc := d.Core().Pods(namespace)
|
||||
|
||||
mountPods, err := getMountPods(pc, pvc.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
events, _ := d.Core().Events(namespace).Search(legacyscheme.Scheme, pvc)
|
||||
|
||||
return describePersistentVolumeClaim(pvc, events)
|
||||
return describePersistentVolumeClaim(pvc, events, mountPods)
|
||||
}
|
||||
|
||||
func describePersistentVolumeClaim(pvc *api.PersistentVolumeClaim, events *api.EventList) (string, error) {
|
||||
func getMountPods(c coreclient.PodInterface, pvcName string) ([]api.Pod, error) {
|
||||
nsPods, err := c.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return []api.Pod{}, err
|
||||
}
|
||||
|
||||
var pods []api.Pod
|
||||
|
||||
for _, pod := range nsPods.Items {
|
||||
pvcs := getPvcs(pod.Spec.Volumes)
|
||||
|
||||
for _, pvc := range pvcs {
|
||||
if pvc.PersistentVolumeClaim.ClaimName == pvcName {
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
func getPvcs(volumes []api.Volume) []api.Volume {
|
||||
var pvcs []api.Volume
|
||||
|
||||
for _, volume := range volumes {
|
||||
if volume.VolumeSource.PersistentVolumeClaim != nil {
|
||||
pvcs = append(pvcs, volume)
|
||||
}
|
||||
}
|
||||
|
||||
return pvcs
|
||||
}
|
||||
|
||||
func describePersistentVolumeClaim(pvc *api.PersistentVolumeClaim, events *api.EventList, mountPods []api.Pod) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
w.Write(LEVEL_0, "Name:\t%s\n", pvc.Name)
|
||||
w.Write(LEVEL_0, "Namespace:\t%s\n", pvc.Namespace)
|
||||
w.Write(LEVEL_0, "StorageClass:\t%s\n", helper.GetPersistentVolumeClaimClass(pvc))
|
||||
if pvc.ObjectMeta.DeletionTimestamp != nil {
|
||||
w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestamp(*pvc.ObjectMeta.DeletionTimestamp))
|
||||
w.Write(LEVEL_0, "Status:\tTerminating (lasts %s)\n", translateTimestampUntil(*pvc.ObjectMeta.DeletionTimestamp))
|
||||
} else {
|
||||
w.Write(LEVEL_0, "Status:\t%v\n", pvc.Status.Phase)
|
||||
}
|
||||
@@ -1338,6 +1424,8 @@ func describePersistentVolumeClaim(pvc *api.PersistentVolumeClaim, events *api.E
|
||||
DescribeEvents(events, w)
|
||||
}
|
||||
|
||||
printPodsMultiline(w, "Mounted By", mountPods)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1423,13 +1511,17 @@ func describeContainerCommand(container api.Container, w PrefixWriter) {
|
||||
if len(container.Command) > 0 {
|
||||
w.Write(LEVEL_2, "Command:\n")
|
||||
for _, c := range container.Command {
|
||||
w.Write(LEVEL_3, "%s\n", c)
|
||||
for _, s := range strings.Split(c, "\n") {
|
||||
w.Write(LEVEL_3, "%s\n", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(container.Args) > 0 {
|
||||
w.Write(LEVEL_2, "Args:\n")
|
||||
for _, arg := range container.Args {
|
||||
w.Write(LEVEL_3, "%s\n", arg)
|
||||
for _, s := range strings.Split(arg, "\n") {
|
||||
w.Write(LEVEL_3, "%s\n", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1512,7 +1604,13 @@ func describeContainerEnvVars(container api.Container, resolverFn EnvVarResolver
|
||||
|
||||
for _, e := range container.Env {
|
||||
if e.ValueFrom == nil {
|
||||
w.Write(LEVEL_3, "%s:\t%s\n", e.Name, e.Value)
|
||||
for i, s := range strings.Split(e.Value, "\n") {
|
||||
if i == 0 {
|
||||
w.Write(LEVEL_3, "%s:\t%s\n", e.Name, s)
|
||||
} else {
|
||||
w.Write(LEVEL_3, "\t%s\n", s)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1598,7 +1696,12 @@ type EnvVarResolverFunc func(e api.EnvVar) string
|
||||
// EnvValueFrom is exported for use by describers in other packages
|
||||
func EnvValueRetriever(pod *api.Pod) EnvVarResolverFunc {
|
||||
return func(e api.EnvVar) string {
|
||||
internalFieldPath, _, err := legacyscheme.Scheme.ConvertFieldLabel(e.ValueFrom.FieldRef.APIVersion, "Pod", e.ValueFrom.FieldRef.FieldPath, "")
|
||||
gv, err := schema.ParseGroupVersion(e.ValueFrom.FieldRef.APIVersion)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
gvk := gv.WithKind("Pod")
|
||||
internalFieldPath, _, err := legacyscheme.Scheme.ConvertFieldLabel(gvk, e.ValueFrom.FieldRef.FieldPath, "")
|
||||
if err != nil {
|
||||
return "" // pod validation should catch this on create
|
||||
}
|
||||
@@ -1852,6 +1955,12 @@ func describeJob(job *batch.Job, events *api.EventList) (string, error) {
|
||||
if job.Status.StartTime != nil {
|
||||
w.Write(LEVEL_0, "Start Time:\t%s\n", job.Status.StartTime.Time.Format(time.RFC1123Z))
|
||||
}
|
||||
if job.Status.CompletionTime != nil {
|
||||
w.Write(LEVEL_0, "Completed At:\t%s\n", job.Status.CompletionTime.Time.Format(time.RFC1123Z))
|
||||
}
|
||||
if job.Status.StartTime != nil && job.Status.CompletionTime != nil {
|
||||
w.Write(LEVEL_0, "Duration:\t%s\n", duration.HumanDuration(job.Status.CompletionTime.Sub(job.Status.StartTime.Time)))
|
||||
}
|
||||
if job.Spec.ActiveDeadlineSeconds != nil {
|
||||
w.Write(LEVEL_0, "Active Deadline Seconds:\t%ds\n", *job.Spec.ActiveDeadlineSeconds)
|
||||
}
|
||||
@@ -2905,50 +3014,50 @@ func describeHorizontalPodAutoscaler(hpa *autoscaling.HorizontalPodAutoscaler, e
|
||||
for i, metric := range hpa.Spec.Metrics {
|
||||
switch metric.Type {
|
||||
case autoscaling.ExternalMetricSourceType:
|
||||
if metric.External.TargetAverageValue != nil {
|
||||
if metric.External.Target.AverageValue != nil {
|
||||
current := "<unknown>"
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].External != nil &&
|
||||
hpa.Status.CurrentMetrics[i].External.CurrentAverageValue != nil {
|
||||
current = hpa.Status.CurrentMetrics[i].External.CurrentAverageValue.String()
|
||||
&hpa.Status.CurrentMetrics[i].External.Current.AverageValue != nil {
|
||||
current = hpa.Status.CurrentMetrics[i].External.Current.AverageValue.String()
|
||||
}
|
||||
w.Write(LEVEL_1, "%q (target average value):\t%s / %s\n", metric.External.MetricName, current, metric.External.TargetAverageValue.String())
|
||||
w.Write(LEVEL_1, "%q (target average value):\t%s / %s\n", metric.External.Metric.Name, current, metric.External.Target.AverageValue.String())
|
||||
} else {
|
||||
current := "<unknown>"
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].External != nil {
|
||||
current = hpa.Status.CurrentMetrics[i].External.CurrentValue.String()
|
||||
current = hpa.Status.CurrentMetrics[i].External.Current.Value.String()
|
||||
}
|
||||
w.Write(LEVEL_1, "%q (target value):\t%s / %s\n", metric.External.MetricName, current, metric.External.TargetValue.String())
|
||||
w.Write(LEVEL_1, "%q (target value):\t%s / %s\n", metric.External.Metric.Name, current, metric.External.Target.Value.String())
|
||||
|
||||
}
|
||||
case autoscaling.PodsMetricSourceType:
|
||||
current := "<unknown>"
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Pods != nil {
|
||||
current = hpa.Status.CurrentMetrics[i].Pods.CurrentAverageValue.String()
|
||||
current = hpa.Status.CurrentMetrics[i].Pods.Current.AverageValue.String()
|
||||
}
|
||||
w.Write(LEVEL_1, "%q on pods:\t%s / %s\n", metric.Pods.MetricName, current, metric.Pods.TargetAverageValue.String())
|
||||
w.Write(LEVEL_1, "%q on pods:\t%s / %s\n", metric.Pods.Metric.Name, current, metric.Pods.Target.AverageValue.String())
|
||||
case autoscaling.ObjectMetricSourceType:
|
||||
current := "<unknown>"
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Object != nil {
|
||||
current = hpa.Status.CurrentMetrics[i].Object.CurrentValue.String()
|
||||
current = hpa.Status.CurrentMetrics[i].Object.Current.Value.String()
|
||||
}
|
||||
w.Write(LEVEL_1, "%q on %s/%s:\t%s / %s\n", metric.Object.MetricName, metric.Object.Target.Kind, metric.Object.Target.Name, current, metric.Object.TargetValue.String())
|
||||
w.Write(LEVEL_1, "%q on %s/%s:\t%s / %s\n", metric.Object.Metric.Name, metric.Object.DescribedObject.Kind, metric.Object.DescribedObject.Name, current, metric.Object.Target.Value.String())
|
||||
case autoscaling.ResourceMetricSourceType:
|
||||
w.Write(LEVEL_1, "resource %s on pods", string(metric.Resource.Name))
|
||||
if metric.Resource.TargetAverageValue != nil {
|
||||
if metric.Resource.Target.AverageValue != nil {
|
||||
current := "<unknown>"
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Resource != nil {
|
||||
current = hpa.Status.CurrentMetrics[i].Resource.CurrentAverageValue.String()
|
||||
current = hpa.Status.CurrentMetrics[i].Resource.Current.AverageValue.String()
|
||||
}
|
||||
w.Write(LEVEL_0, ":\t%s / %s\n", current, metric.Resource.TargetAverageValue.String())
|
||||
w.Write(LEVEL_0, ":\t%s / %s\n", current, metric.Resource.Target.AverageValue.String())
|
||||
} else {
|
||||
current := "<unknown>"
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Resource != nil && hpa.Status.CurrentMetrics[i].Resource.CurrentAverageUtilization != nil {
|
||||
current = fmt.Sprintf("%d%% (%s)", *hpa.Status.CurrentMetrics[i].Resource.CurrentAverageUtilization, hpa.Status.CurrentMetrics[i].Resource.CurrentAverageValue.String())
|
||||
if len(hpa.Status.CurrentMetrics) > i && hpa.Status.CurrentMetrics[i].Resource != nil && hpa.Status.CurrentMetrics[i].Resource.Current.AverageUtilization != nil {
|
||||
current = fmt.Sprintf("%d%% (%s)", *hpa.Status.CurrentMetrics[i].Resource.Current.AverageUtilization, hpa.Status.CurrentMetrics[i].Resource.Current.AverageValue.String())
|
||||
}
|
||||
|
||||
target := "<auto>"
|
||||
if metric.Resource.TargetAverageUtilization != nil {
|
||||
target = fmt.Sprintf("%d%%", *metric.Resource.TargetAverageUtilization)
|
||||
if metric.Resource.Target.AverageUtilization != nil {
|
||||
target = fmt.Sprintf("%d%%", *metric.Resource.Target.AverageUtilization)
|
||||
}
|
||||
w.Write(LEVEL_1, "(as a percentage of request):\t%s / %s\n", current, target)
|
||||
}
|
||||
@@ -3073,9 +3182,9 @@ func DescribeEvents(el *api.EventList, w PrefixWriter) {
|
||||
for _, e := range el.Items {
|
||||
var interval string
|
||||
if e.Count > 1 {
|
||||
interval = fmt.Sprintf("%s (x%d over %s)", translateTimestamp(e.LastTimestamp), e.Count, translateTimestamp(e.FirstTimestamp))
|
||||
interval = fmt.Sprintf("%s (x%d over %s)", translateTimestampSince(e.LastTimestamp), e.Count, translateTimestampSince(e.FirstTimestamp))
|
||||
} else {
|
||||
interval = translateTimestamp(e.FirstTimestamp)
|
||||
interval = translateTimestampSince(e.FirstTimestamp)
|
||||
}
|
||||
w.Write(LEVEL_1, "%v\t%v\t%s\t%v\t%v\n",
|
||||
e.Type,
|
||||
@@ -3407,6 +3516,9 @@ func describeStorageClass(sc *storage.StorageClass, events *api.EventList) (stri
|
||||
if sc.VolumeBindingMode != nil {
|
||||
w.Write(LEVEL_0, "VolumeBindingMode:\t%s\n", *sc.VolumeBindingMode)
|
||||
}
|
||||
if sc.AllowedTopologies != nil {
|
||||
printAllowedTopologies(w, sc.AllowedTopologies)
|
||||
}
|
||||
if events != nil {
|
||||
DescribeEvents(events, w)
|
||||
}
|
||||
@@ -3415,6 +3527,38 @@ func describeStorageClass(sc *storage.StorageClass, events *api.EventList) (stri
|
||||
})
|
||||
}
|
||||
|
||||
func printAllowedTopologies(w PrefixWriter, topologies []api.TopologySelectorTerm) {
|
||||
w.Write(LEVEL_0, "AllowedTopologies:\t")
|
||||
if len(topologies) == 0 {
|
||||
w.WriteLine("<none>")
|
||||
return
|
||||
}
|
||||
w.WriteLine("")
|
||||
for i, term := range topologies {
|
||||
printTopologySelectorTermsMultilineWithIndent(w, LEVEL_1, fmt.Sprintf("Term %d", i), "\t", term.MatchLabelExpressions)
|
||||
}
|
||||
}
|
||||
|
||||
func printTopologySelectorTermsMultilineWithIndent(w PrefixWriter, indentLevel int, title, innerIndent string, reqs []api.TopologySelectorLabelRequirement) {
|
||||
w.Write(indentLevel, "%s:%s", title, innerIndent)
|
||||
|
||||
if len(reqs) == 0 {
|
||||
w.WriteLine("<none>")
|
||||
return
|
||||
}
|
||||
|
||||
for i, req := range reqs {
|
||||
if i != 0 {
|
||||
w.Write(indentLevel, "%s", innerIndent)
|
||||
}
|
||||
exprStr := fmt.Sprintf("%s %s", req.Key, "in")
|
||||
if len(req.Values) > 0 {
|
||||
exprStr = fmt.Sprintf("%s [%s]", exprStr, strings.Join(req.Values, ", "))
|
||||
}
|
||||
w.Write(LEVEL_0, "%s\n", exprStr)
|
||||
}
|
||||
}
|
||||
|
||||
type PodDisruptionBudgetDescriber struct {
|
||||
clientset.Interface
|
||||
}
|
||||
@@ -3530,6 +3674,12 @@ func describePodSecurityPolicy(psp *policy.PodSecurityPolicy) (string, error) {
|
||||
if len(psp.Spec.AllowedFlexVolumes) > 0 {
|
||||
w.Write(LEVEL_1, "Allowed FlexVolume Types:\t%s\n", flexVolumesToString(psp.Spec.AllowedFlexVolumes))
|
||||
}
|
||||
if len(psp.Spec.AllowedUnsafeSysctls) > 0 {
|
||||
w.Write(LEVEL_1, "Allowed Unsafe Sysctls:\t%s\n", sysctlsToString(psp.Spec.AllowedUnsafeSysctls))
|
||||
}
|
||||
if len(psp.Spec.ForbiddenSysctls) > 0 {
|
||||
w.Write(LEVEL_1, "Forbidden Sysctls:\t%s\n", sysctlsToString(psp.Spec.ForbiddenSysctls))
|
||||
}
|
||||
w.Write(LEVEL_1, "Allow Host Network:\t%t\n", psp.Spec.HostNetwork)
|
||||
w.Write(LEVEL_1, "Allow Host Ports:\t%s\n", hostPortRangeToString(psp.Spec.HostPorts))
|
||||
w.Write(LEVEL_1, "Allow Host PID:\t%t\n", psp.Spec.HostPID)
|
||||
@@ -3589,6 +3739,10 @@ func flexVolumesToString(flexVolumes []policy.AllowedFlexVolume) string {
|
||||
return stringOrDefaultValue(strings.Join(volumes, ","), "<all>")
|
||||
}
|
||||
|
||||
func sysctlsToString(sysctls []string) string {
|
||||
return stringOrNone(strings.Join(sysctls, ","))
|
||||
}
|
||||
|
||||
func hostPortRangeToString(ranges []policy.HostPortRange) string {
|
||||
formattedString := ""
|
||||
if ranges != nil {
|
||||
@@ -3847,6 +4001,37 @@ func printTaintsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerI
|
||||
}
|
||||
}
|
||||
|
||||
// printPodsMultiline prints multiple pods with a proper alignment.
|
||||
func printPodsMultiline(w PrefixWriter, title string, pods []api.Pod) {
|
||||
printPodsMultilineWithIndent(w, "", title, "\t", pods)
|
||||
}
|
||||
|
||||
// printPodsMultilineWithIndent prints multiple pods with a user-defined alignment.
|
||||
func printPodsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, pods []api.Pod) {
|
||||
w.Write(LEVEL_0, "%s%s:%s", initialIndent, title, innerIndent)
|
||||
|
||||
if pods == nil || len(pods) == 0 {
|
||||
w.WriteLine("<none>")
|
||||
return
|
||||
}
|
||||
|
||||
// to print pods in the sorted order
|
||||
sort.Slice(pods, func(i, j int) bool {
|
||||
cmpKey := func(pod api.Pod) string {
|
||||
return pod.Name
|
||||
}
|
||||
return cmpKey(pods[i]) < cmpKey(pods[j])
|
||||
})
|
||||
|
||||
for i, pod := range pods {
|
||||
if i != 0 {
|
||||
w.Write(LEVEL_0, "%s", initialIndent)
|
||||
w.Write(LEVEL_0, "%s", innerIndent)
|
||||
}
|
||||
w.Write(LEVEL_0, "%s\n", pod.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// printPodTolerationsMultiline prints multiple tolerations with a proper alignment.
|
||||
func printPodTolerationsMultiline(w PrefixWriter, title string, tolerations []api.Toleration) {
|
||||
printTolerationsMultilineWithIndent(w, "", title, "\t", tolerations)
|
||||
@@ -3970,7 +4155,7 @@ func (list SortableVolumeDevices) Less(i, j int) bool {
|
||||
return list[i].DevicePath < list[j].DevicePath
|
||||
}
|
||||
|
||||
var maxAnnotationLen = 200
|
||||
var maxAnnotationLen = 140
|
||||
|
||||
// printAnnotationsMultilineWithFilter prints filtered multiple annotations with a proper alignment.
|
||||
func printAnnotationsMultilineWithFilter(w PrefixWriter, title string, annotations map[string]string, skip sets.String) {
|
||||
@@ -4006,18 +4191,27 @@ func printAnnotationsMultilineWithIndent(w PrefixWriter, initialIndent, title, i
|
||||
return
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
indent := initialIndent + innerIndent
|
||||
for i, key := range keys {
|
||||
if i != 0 {
|
||||
w.Write(LEVEL_0, initialIndent)
|
||||
w.Write(LEVEL_0, innerIndent)
|
||||
w.Write(LEVEL_0, indent)
|
||||
}
|
||||
line := fmt.Sprintf("%s=%s", key, annotations[key])
|
||||
if len(line) > maxAnnotationLen {
|
||||
w.Write(LEVEL_0, "%s...\n", line[:maxAnnotationLen])
|
||||
value := strings.TrimSuffix(annotations[key], "\n")
|
||||
if (len(value)+len(key)+2) > maxAnnotationLen || strings.Contains(value, "\n") {
|
||||
w.Write(LEVEL_0, "%s:\n", key)
|
||||
for _, s := range strings.Split(value, "\n") {
|
||||
w.Write(LEVEL_0, "%s %s\n", indent, shorten(s, maxAnnotationLen-2))
|
||||
}
|
||||
} else {
|
||||
w.Write(LEVEL_0, "%s\n", line)
|
||||
w.Write(LEVEL_0, "%s: %s\n", key, value)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func shorten(s string, maxLength int) string {
|
||||
if len(s) > maxLength {
|
||||
return s[:maxLength] + "..."
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
435
vendor/k8s.io/kubernetes/pkg/printers/internalversion/describe_test.go
generated
vendored
435
vendor/k8s.io/kubernetes/pkg/printers/internalversion/describe_test.go
generated
vendored
@@ -43,7 +43,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
type describeClient struct {
|
||||
@@ -54,10 +54,14 @@ type describeClient struct {
|
||||
}
|
||||
|
||||
func TestDescribePod(t *testing.T) {
|
||||
deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0)}
|
||||
gracePeriod := int64(1234)
|
||||
fake := fake.NewSimpleClientset(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
DeletionTimestamp: &deletionTimestamp,
|
||||
DeletionGracePeriodSeconds: &gracePeriod,
|
||||
},
|
||||
})
|
||||
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
||||
@@ -69,6 +73,9 @@ func TestDescribePod(t *testing.T) {
|
||||
if !strings.Contains(out, "bar") || !strings.Contains(out, "Status:") {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
if !strings.Contains(out, "Terminating (lasts 10y)") || !strings.Contains(out, "1234s") {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribePodNode(t *testing.T) {
|
||||
@@ -458,14 +465,12 @@ func VerifyDatesInOrder(
|
||||
func TestDescribeContainers(t *testing.T) {
|
||||
trueVal := true
|
||||
testCases := []struct {
|
||||
name string
|
||||
container api.Container
|
||||
status api.ContainerStatus
|
||||
expectedElements []string
|
||||
}{
|
||||
// Running state.
|
||||
{
|
||||
name: "test1",
|
||||
container: api.Container{Name: "test", Image: "image"},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -481,7 +486,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// Waiting state.
|
||||
{
|
||||
name: "test2",
|
||||
container: api.Container{Name: "test", Image: "image"},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -497,7 +501,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// Terminated state.
|
||||
{
|
||||
name: "test3",
|
||||
container: api.Container{Name: "test", Image: "image"},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -516,7 +519,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// Last Terminated
|
||||
{
|
||||
name: "test4",
|
||||
container: api.Container{Name: "test", Image: "image"},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -540,7 +542,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// No state defaults to waiting.
|
||||
{
|
||||
name: "test5",
|
||||
container: api.Container{Name: "test", Image: "image"},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -551,7 +552,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// Env
|
||||
{
|
||||
name: "test6",
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{ConfigMapRef: &api.ConfigMapEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -561,7 +561,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap\tOptional: false"},
|
||||
},
|
||||
{
|
||||
name: "test7",
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{Prefix: "p_", ConfigMapRef: &api.ConfigMapEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -571,7 +570,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap with prefix 'p_'\tOptional: false"},
|
||||
},
|
||||
{
|
||||
name: "test8",
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{ConfigMapRef: &api.ConfigMapEnvSource{Optional: &trueVal, LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -581,7 +579,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap\tOptional: true"},
|
||||
},
|
||||
{
|
||||
name: "test9",
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}, Optional: &trueVal}}}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -591,7 +588,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret\tOptional: true"},
|
||||
},
|
||||
{
|
||||
name: "test10",
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{Prefix: "p_", SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -602,7 +598,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// Command
|
||||
{
|
||||
name: "test11",
|
||||
container: api.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000"}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -611,9 +606,18 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "sleep", "1000"},
|
||||
},
|
||||
// Command with newline
|
||||
{
|
||||
container: api.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000\n2000"}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
Ready: true,
|
||||
RestartCount: 7,
|
||||
},
|
||||
expectedElements: []string{"1000\n 2000"},
|
||||
},
|
||||
// Args
|
||||
{
|
||||
name: "test12",
|
||||
container: api.Container{Name: "test", Image: "image", Args: []string{"time", "1000"}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
@@ -622,9 +626,18 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "time", "1000"},
|
||||
},
|
||||
// Args with newline
|
||||
{
|
||||
container: api.Container{Name: "test", Image: "image", Args: []string{"time", "1000\n2000"}},
|
||||
status: api.ContainerStatus{
|
||||
Name: "test",
|
||||
Ready: true,
|
||||
RestartCount: 7,
|
||||
},
|
||||
expectedElements: []string{"1000\n 2000"},
|
||||
},
|
||||
// Using limits.
|
||||
{
|
||||
name: "test13",
|
||||
container: api.Container{
|
||||
Name: "test",
|
||||
Image: "image",
|
||||
@@ -645,7 +658,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// Using requests.
|
||||
{
|
||||
name: "test14",
|
||||
container: api.Container{
|
||||
Name: "test",
|
||||
Image: "image",
|
||||
@@ -661,7 +673,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// volumeMounts read/write
|
||||
{
|
||||
name: "test15",
|
||||
container: api.Container{
|
||||
Name: "test",
|
||||
Image: "image",
|
||||
@@ -676,7 +687,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
},
|
||||
// volumeMounts readonly
|
||||
{
|
||||
name: "test16",
|
||||
container: api.Container{
|
||||
Name: "test",
|
||||
Image: "image",
|
||||
@@ -693,7 +703,6 @@ func TestDescribeContainers(t *testing.T) {
|
||||
|
||||
// volumeDevices
|
||||
{
|
||||
name: "test17",
|
||||
container: api.Container{
|
||||
Name: "test",
|
||||
Image: "image",
|
||||
@@ -709,7 +718,7 @@ func TestDescribeContainers(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
out := new(bytes.Buffer)
|
||||
pod := api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
@@ -892,6 +901,7 @@ func TestGetPodsTotalRequests(t *testing.T) {
|
||||
func TestPersistentVolumeDescriber(t *testing.T) {
|
||||
block := api.PersistentVolumeBlock
|
||||
file := api.PersistentVolumeFilesystem
|
||||
deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0)}
|
||||
testCases := []struct {
|
||||
name string
|
||||
plugin string
|
||||
@@ -1137,6 +1147,96 @@ func TestPersistentVolumeDescriber(t *testing.T) {
|
||||
"foo in [val1, val2]",
|
||||
"foo exists"},
|
||||
},
|
||||
{
|
||||
name: "test15",
|
||||
plugin: "local",
|
||||
pv: &api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
DeletionTimestamp: &deletionTimestamp,
|
||||
},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
Local: &api.LocalVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedElements: []string{"Terminating (lasts 10y)"},
|
||||
},
|
||||
{
|
||||
name: "test16",
|
||||
plugin: "local",
|
||||
pv: &api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
GenerateName: "test-GenerateName",
|
||||
UID: "test-UID",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now()},
|
||||
DeletionTimestamp: &metav1.Time{Time: time.Now()},
|
||||
DeletionGracePeriodSeconds: new(int64),
|
||||
Labels: map[string]string{"label1": "label1", "label2": "label2", "label3": "label3"},
|
||||
Annotations: map[string]string{"annotation1": "annotation1", "annotation2": "annotation2", "annotation3": "annotation3"},
|
||||
},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
Local: &api.LocalVolumeSource{},
|
||||
},
|
||||
NodeAffinity: &api.VolumeNodeAffinity{
|
||||
Required: &api.NodeSelector{
|
||||
NodeSelectorTerms: []api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "In",
|
||||
Values: []string{"val1", "val2"},
|
||||
},
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedElements: []string{"Node Affinity", "Required Terms", "Term 0",
|
||||
"foo in [val1, val2]",
|
||||
"foo exists"},
|
||||
},
|
||||
{
|
||||
name: "test17",
|
||||
plugin: "local",
|
||||
pv: &api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
GenerateName: "test-GenerateName",
|
||||
UID: "test-UID",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now()},
|
||||
DeletionTimestamp: &metav1.Time{Time: time.Now()},
|
||||
DeletionGracePeriodSeconds: new(int64),
|
||||
Labels: map[string]string{"label1": "label1", "label2": "label2", "label3": "label3"},
|
||||
Annotations: map[string]string{"annotation1": "annotation1", "annotation2": "annotation2", "annotation3": "annotation3"},
|
||||
},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
CSI: &api.CSIPersistentVolumeSource{
|
||||
Driver: "drive",
|
||||
VolumeHandle: "handler",
|
||||
ReadOnly: true,
|
||||
VolumeAttributes: map[string]string{
|
||||
"Attribute1": "Value1",
|
||||
"Attribute2": "Value2",
|
||||
"Attribute3": "Value3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedElements: []string{"Driver", "VolumeHandle", "ReadOnly", "VolumeAttributes"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -1169,6 +1269,7 @@ func TestPersistentVolumeClaimDescriber(t *testing.T) {
|
||||
file := api.PersistentVolumeFilesystem
|
||||
goldClassName := "gold"
|
||||
now := time.Now()
|
||||
deletionTimestamp := metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0)}
|
||||
testCases := []struct {
|
||||
name string
|
||||
pvc *api.PersistentVolumeClaim
|
||||
@@ -1316,6 +1417,22 @@ func TestPersistentVolumeClaimDescriber(t *testing.T) {
|
||||
},
|
||||
expectedElements: []string{"Conditions", "Message", "User request resize"},
|
||||
},
|
||||
{
|
||||
name: "deletion-timestamp",
|
||||
pvc: &api.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "foo",
|
||||
Name: "bar",
|
||||
DeletionTimestamp: &deletionTimestamp,
|
||||
},
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume10",
|
||||
StorageClassName: &goldClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{},
|
||||
},
|
||||
expectedElements: []string{"Terminating (lasts 10y)"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -1390,6 +1507,32 @@ func TestDescribeStorageClass(t *testing.T) {
|
||||
},
|
||||
ReclaimPolicy: &reclaimPolicy,
|
||||
VolumeBindingMode: &bindingMode,
|
||||
AllowedTopologies: []api.TopologySelectorTerm{
|
||||
{
|
||||
MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
|
||||
{
|
||||
Key: "failure-domain.beta.kubernetes.io/zone",
|
||||
Values: []string{"zone1"},
|
||||
},
|
||||
{
|
||||
Key: "kubernetes.io/hostname",
|
||||
Values: []string{"node1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
|
||||
{
|
||||
Key: "failure-domain.beta.kubernetes.io/zone",
|
||||
Values: []string{"zone2"},
|
||||
},
|
||||
{
|
||||
Key: "kubernetes.io/hostname",
|
||||
Values: []string{"node2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
s := StorageClassDescriber{f}
|
||||
out, err := s.Describe("", "foo", printers.DescriberSettings{ShowEvents: true})
|
||||
@@ -1403,7 +1546,13 @@ func TestDescribeStorageClass(t *testing.T) {
|
||||
!strings.Contains(out, "value1") ||
|
||||
!strings.Contains(out, "value2") ||
|
||||
!strings.Contains(out, "Retain") ||
|
||||
!strings.Contains(out, "bindingmode") {
|
||||
!strings.Contains(out, "bindingmode") ||
|
||||
!strings.Contains(out, "failure-domain.beta.kubernetes.io/zone") ||
|
||||
!strings.Contains(out, "zone1") ||
|
||||
!strings.Contains(out, "kubernetes.io/hostname") ||
|
||||
!strings.Contains(out, "node1") ||
|
||||
!strings.Contains(out, "zone2") ||
|
||||
!strings.Contains(out, "node2") {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
}
|
||||
@@ -1440,6 +1589,10 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
minReplicasVal := int32(2)
|
||||
targetUtilizationVal := int32(80)
|
||||
currentUtilizationVal := int32(50)
|
||||
metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
|
||||
if err != nil {
|
||||
t.Errorf("unable to parse label selector: %v", err)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
hpa autoscaling.HorizontalPodAutoscaler
|
||||
@@ -1474,13 +1627,14 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1505,13 +1659,14 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1523,13 +1678,13 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricStatus{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
CurrentAverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1550,13 +1705,14 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1581,13 +1737,14 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1599,13 +1756,13 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricStatus{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
Value: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
CurrentValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1626,8 +1783,13 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "some-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1652,8 +1814,13 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "some-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1665,8 +1832,12 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricStatus{
|
||||
MetricName: "some-pods-metric",
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1687,12 +1858,17 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ObjectMetricSourceType,
|
||||
Object: &autoscaling.ObjectMetricSource{
|
||||
Target: autoscaling.CrossVersionObjectReference{
|
||||
DescribedObject: autoscaling.CrossVersionObjectReference{
|
||||
Name: "some-service",
|
||||
Kind: "Service",
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
TargetValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1717,12 +1893,17 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ObjectMetricSourceType,
|
||||
Object: &autoscaling.ObjectMetricSource{
|
||||
Target: autoscaling.CrossVersionObjectReference{
|
||||
DescribedObject: autoscaling.CrossVersionObjectReference{
|
||||
Name: "some-service",
|
||||
Kind: "Service",
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
TargetValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1734,12 +1915,16 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ObjectMetricSourceType,
|
||||
Object: &autoscaling.ObjectMetricStatus{
|
||||
Target: autoscaling.CrossVersionObjectReference{
|
||||
DescribedObject: autoscaling.CrossVersionObjectReference{
|
||||
Name: "some-service",
|
||||
Kind: "Service",
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
CurrentValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
Value: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1760,8 +1945,11 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Name: api.ResourceCPU,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1786,8 +1974,11 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Name: api.ResourceCPU,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1799,8 +1990,10 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricStatus{
|
||||
Name: api.ResourceCPU,
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Name: api.ResourceCPU,
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1822,7 +2015,10 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &targetUtilizationVal,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.UtilizationMetricType,
|
||||
AverageUtilization: &targetUtilizationVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1848,7 +2044,10 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &targetUtilizationVal,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.UtilizationMetricType,
|
||||
AverageUtilization: &targetUtilizationVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1861,8 +2060,10 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricStatus{
|
||||
Name: api.ResourceCPU,
|
||||
CurrentAverageUtilization: ¤tUtilizationVal,
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageUtilization: ¤tUtilizationVal,
|
||||
AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1883,22 +2084,35 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "some-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &targetUtilizationVal,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.UtilizationMetricType,
|
||||
AverageUtilization: &targetUtilizationVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "other-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(400, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "other-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(400, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1910,16 +2124,22 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricStatus{
|
||||
MetricName: "some-pods-metric",
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricStatus{
|
||||
Name: api.ResourceCPU,
|
||||
CurrentAverageUtilization: ¤tUtilizationVal,
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageUtilization: ¤tUtilizationVal,
|
||||
AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2101,22 +2321,31 @@ func TestDescribeEvents(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPrintLabelsMultiline(t *testing.T) {
|
||||
var maxLenAnnotationStr string = "MaxLenAnnotation=Multicast addressing can be used in the link layer (Layer 2 in the OSI model), such as Ethernet multicast, and at the internet layer (Layer 3 for OSI) for Internet Protocol Version 4 "
|
||||
key := "MaxLenAnnotation"
|
||||
value := strings.Repeat("a", maxAnnotationLen-len(key)-2)
|
||||
testCases := []struct {
|
||||
annotations map[string]string
|
||||
expectPrint string
|
||||
}{
|
||||
{
|
||||
annotations: map[string]string{"col1": "asd", "COL2": "zxc"},
|
||||
expectPrint: "Annotations:\tCOL2=zxc\n\tcol1=asd\n",
|
||||
expectPrint: "Annotations:\tCOL2: zxc\n\tcol1: asd\n",
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{"MaxLenAnnotation": maxLenAnnotationStr[17:]},
|
||||
expectPrint: "Annotations:\t" + maxLenAnnotationStr + "\n",
|
||||
annotations: map[string]string{"MaxLenAnnotation": value},
|
||||
expectPrint: fmt.Sprintf("Annotations:\t%s: %s\n", key, value),
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{"MaxLenAnnotation": maxLenAnnotationStr[17:] + "1"},
|
||||
expectPrint: "Annotations:\t" + maxLenAnnotationStr + "...\n",
|
||||
annotations: map[string]string{"MaxLenAnnotation": value + "1"},
|
||||
expectPrint: fmt.Sprintf("Annotations:\t%s:\n\t %s\n", key, value+"1"),
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{"MaxLenAnnotation": value + value},
|
||||
expectPrint: fmt.Sprintf("Annotations:\t%s:\n\t %s\n", key, strings.Repeat("a", maxAnnotationLen-2)+"..."),
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{"key": "value\nwith\nnewlines\n"},
|
||||
expectPrint: "Annotations:\tkey:\n\t value\n\t with\n\t newlines\n",
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{},
|
||||
@@ -2124,13 +2353,13 @@ func TestPrintLabelsMultiline(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
t.Run(testCase.expectPrint, func(t *testing.T) {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
out := new(bytes.Buffer)
|
||||
writer := NewPrefixWriter(out)
|
||||
printAnnotationsMultiline(writer, "Annotations", testCase.annotations)
|
||||
output := out.String()
|
||||
if output != testCase.expectPrint {
|
||||
t.Errorf("Test case %d: expected to find %q in output: %q", i, testCase.expectPrint, output)
|
||||
t.Errorf("Test case %d: expected to match:\n%q\nin output:\n%q", i, testCase.expectPrint, output)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -2228,6 +2457,8 @@ func TestDescribePodSecurityPolicy(t *testing.T) {
|
||||
"Required Drop Capabilities:\\s*<none>",
|
||||
"Allowed Capabilities:\\s*<none>",
|
||||
"Allowed Volume Types:\\s*<none>",
|
||||
"Allowed Unsafe Sysctls:\\s*kernel\\.\\*,net\\.ipv4.ip_local_port_range",
|
||||
"Forbidden Sysctls:\\s*net\\.ipv4\\.ip_default_ttl",
|
||||
"Allow Host Network:\\s*false",
|
||||
"Allow Host Ports:\\s*<none>",
|
||||
"Allow Host PID:\\s*false",
|
||||
@@ -2248,6 +2479,8 @@ func TestDescribePodSecurityPolicy(t *testing.T) {
|
||||
Name: "mypsp",
|
||||
},
|
||||
Spec: policy.PodSecurityPolicySpec{
|
||||
AllowedUnsafeSysctls: []string{"kernel.*", "net.ipv4.ip_local_port_range"},
|
||||
ForbiddenSysctls: []string{"net.ipv4.ip_default_ttl"},
|
||||
SELinux: policy.SELinuxStrategyOptions{
|
||||
Rule: policy.SELinuxStrategyRunAsAny,
|
||||
},
|
||||
|
262
vendor/k8s.io/kubernetes/pkg/printers/internalversion/printers.go
generated
vendored
262
vendor/k8s.io/kubernetes/pkg/printers/internalversion/printers.go
generated
vendored
@@ -21,7 +21,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -31,6 +30,7 @@ import (
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
batchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
|
||||
coordinationv1beta1 "k8s.io/api/coordination/v1beta1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
@@ -43,11 +43,11 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/duration"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/api/events"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||
"k8s.io/kubernetes/pkg/apis/coordination"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
@@ -82,6 +82,7 @@ func AddHandlers(h printers.PrintHandler) {
|
||||
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
||||
{Name: "IP", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["podIP"]},
|
||||
{Name: "Node", Type: "string", Priority: 1, Description: apiv1.PodSpec{}.SwaggerDoc()["nodeName"]},
|
||||
{Name: "Nominated Node", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["nominatedNodeName"]},
|
||||
}
|
||||
h.TableHandler(podColumnDefinitions, printPodList)
|
||||
h.TableHandler(podColumnDefinitions, printPod)
|
||||
@@ -149,8 +150,8 @@ func AddHandlers(h printers.PrintHandler) {
|
||||
|
||||
jobColumnDefinitions := []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||
{Name: "Desired", Type: "integer", Description: batchv1.JobSpec{}.SwaggerDoc()["completions"]},
|
||||
{Name: "Successful", Type: "integer", Description: batchv1.JobStatus{}.SwaggerDoc()["succeeded"]},
|
||||
{Name: "Completions", Type: "string", Description: batchv1.JobStatus{}.SwaggerDoc()["succeeded"]},
|
||||
{Name: "Duration", Type: "string", Description: "Time required to complete the job."},
|
||||
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
||||
{Name: "Containers", Type: "string", Priority: 1, Description: "Names of each container in the template."},
|
||||
{Name: "Images", Type: "string", Priority: 1, Description: "Images referenced by each container in the template."},
|
||||
@@ -233,15 +234,15 @@ func AddHandlers(h printers.PrintHandler) {
|
||||
|
||||
eventColumnDefinitions := []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Last Seen", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["lastTimestamp"]},
|
||||
{Name: "First Seen", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["firstTimestamp"]},
|
||||
{Name: "Count", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["count"]},
|
||||
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||
{Name: "Kind", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["kind"]},
|
||||
{Name: "Subobject", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["fieldPath"]},
|
||||
{Name: "Type", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["type"]},
|
||||
{Name: "Reason", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["reason"]},
|
||||
{Name: "Source", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["source"]},
|
||||
{Name: "Kind", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["kind"]},
|
||||
{Name: "Source", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["source"]},
|
||||
{Name: "Message", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["message"]},
|
||||
{Name: "Subobject", Type: "string", Priority: 1, Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["fieldPath"]},
|
||||
{Name: "First Seen", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["firstTimestamp"]},
|
||||
{Name: "Count", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["count"]},
|
||||
{Name: "Name", Type: "string", Priority: 1, Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||
}
|
||||
h.TableHandler(eventColumnDefinitions, printEvent)
|
||||
h.TableHandler(eventColumnDefinitions, printEventList)
|
||||
@@ -393,6 +394,14 @@ func AddHandlers(h printers.PrintHandler) {
|
||||
h.TableHandler(certificateSigningRequestColumnDefinitions, printCertificateSigningRequest)
|
||||
h.TableHandler(certificateSigningRequestColumnDefinitions, printCertificateSigningRequestList)
|
||||
|
||||
leaseColumnDefinitions := []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||
{Name: "Holder", Type: "string", Description: coordinationv1beta1.LeaseSpec{}.SwaggerDoc()["holderIdentity"]},
|
||||
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
||||
}
|
||||
h.TableHandler(leaseColumnDefinitions, printLease)
|
||||
h.TableHandler(leaseColumnDefinitions, printLeaseList)
|
||||
|
||||
storageClassColumnDefinitions := []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||
{Name: "Provisioner", Type: "string", Description: storagev1.StorageClass{}.SwaggerDoc()["provisioner"]},
|
||||
@@ -457,7 +466,7 @@ func printObjectMeta(obj runtime.Object, options printers.PrintOptions) ([]metav
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, m.GetName(), translateTimestamp(m.GetCreationTimestamp()))
|
||||
row.Cells = append(row.Cells, m.GetName(), translateTimestampSince(m.GetCreationTimestamp()))
|
||||
rows = append(rows, row)
|
||||
return rows, nil
|
||||
}
|
||||
@@ -473,19 +482,33 @@ func formatEndpoints(endpoints *api.Endpoints, ports sets.String) string {
|
||||
count := 0
|
||||
for i := range endpoints.Subsets {
|
||||
ss := &endpoints.Subsets[i]
|
||||
for i := range ss.Ports {
|
||||
port := &ss.Ports[i]
|
||||
if ports == nil || ports.Has(port.Name) {
|
||||
for i := range ss.Addresses {
|
||||
if len(list) == max {
|
||||
more = true
|
||||
if len(ss.Ports) == 0 {
|
||||
// It's possible to have headless services with no ports.
|
||||
for i := range ss.Addresses {
|
||||
if len(list) == max {
|
||||
more = true
|
||||
}
|
||||
if !more {
|
||||
list = append(list, ss.Addresses[i].IP)
|
||||
}
|
||||
count++
|
||||
}
|
||||
} else {
|
||||
// "Normal" services with ports defined.
|
||||
for i := range ss.Ports {
|
||||
port := &ss.Ports[i]
|
||||
if ports == nil || ports.Has(port.Name) {
|
||||
for i := range ss.Addresses {
|
||||
if len(list) == max {
|
||||
more = true
|
||||
}
|
||||
addr := &ss.Addresses[i]
|
||||
if !more {
|
||||
hostPort := net.JoinHostPort(addr.IP, strconv.Itoa(int(port.Port)))
|
||||
list = append(list, hostPort)
|
||||
}
|
||||
count++
|
||||
}
|
||||
addr := &ss.Addresses[i]
|
||||
if !more {
|
||||
hostPort := net.JoinHostPort(addr.IP, strconv.Itoa(int(port.Port)))
|
||||
list = append(list, hostPort)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -497,14 +520,24 @@ func formatEndpoints(endpoints *api.Endpoints, ports sets.String) string {
|
||||
return ret
|
||||
}
|
||||
|
||||
// translateTimestamp returns the elapsed time since timestamp in
|
||||
// translateTimestampSince returns the elapsed time since timestamp in
|
||||
// human-readable approximation.
|
||||
func translateTimestamp(timestamp metav1.Time) string {
|
||||
func translateTimestampSince(timestamp metav1.Time) string {
|
||||
if timestamp.IsZero() {
|
||||
return "<unknown>"
|
||||
}
|
||||
|
||||
return duration.ShortHumanDuration(time.Since(timestamp.Time))
|
||||
return duration.HumanDuration(time.Since(timestamp.Time))
|
||||
}
|
||||
|
||||
// translateTimestampUntil returns the elapsed time until timestamp in
|
||||
// human-readable approximation.
|
||||
func translateTimestampUntil(timestamp metav1.Time) string {
|
||||
if timestamp.IsZero() {
|
||||
return "<unknown>"
|
||||
}
|
||||
|
||||
return duration.HumanDuration(time.Until(timestamp.Time))
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -608,10 +641,11 @@ func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1beta1.TableR
|
||||
reason = "Terminating"
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, pod.Name, fmt.Sprintf("%d/%d", readyContainers, totalContainers), reason, int64(restarts), translateTimestamp(pod.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, pod.Name, fmt.Sprintf("%d/%d", readyContainers, totalContainers), reason, int64(restarts), translateTimestampSince(pod.CreationTimestamp))
|
||||
|
||||
if options.Wide {
|
||||
nodeName := pod.Spec.NodeName
|
||||
nominatedNodeName := pod.Status.NominatedNodeName
|
||||
podIP := pod.Status.PodIP
|
||||
if podIP == "" {
|
||||
podIP = "<none>"
|
||||
@@ -619,10 +653,10 @@ func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1beta1.TableR
|
||||
if nodeName == "" {
|
||||
nodeName = "<none>"
|
||||
}
|
||||
row.Cells = append(row.Cells, podIP, nodeName)
|
||||
if len(pod.Status.NominatedNodeName) > 0 {
|
||||
row.Cells = append(row.Cells, pod.Status.NominatedNodeName)
|
||||
if nominatedNodeName == "" {
|
||||
nominatedNodeName = "<none>"
|
||||
}
|
||||
row.Cells = append(row.Cells, podIP, nodeName, nominatedNodeName)
|
||||
}
|
||||
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
@@ -668,7 +702,7 @@ func printPodDisruptionBudget(obj *policy.PodDisruptionBudget, options printers.
|
||||
maxUnavailable = "N/A"
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, minAvailable, maxUnavailable, int64(obj.Status.PodDisruptionsAllowed), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, minAvailable, maxUnavailable, int64(obj.Status.PodDisruptionsAllowed), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -694,7 +728,7 @@ func printReplicationController(obj *api.ReplicationController, options printers
|
||||
currentReplicas := obj.Status.Replicas
|
||||
readyReplicas := obj.Status.ReadyReplicas
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredReplicas), int64(currentReplicas), int64(readyReplicas), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredReplicas), int64(currentReplicas), int64(readyReplicas), translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
|
||||
row.Cells = append(row.Cells, names, images, labels.FormatLabels(obj.Spec.Selector))
|
||||
@@ -723,7 +757,7 @@ func printReplicaSet(obj *extensions.ReplicaSet, options printers.PrintOptions)
|
||||
currentReplicas := obj.Status.Replicas
|
||||
readyReplicas := obj.Status.ReadyReplicas
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredReplicas), int64(currentReplicas), int64(readyReplicas), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredReplicas), int64(currentReplicas), int64(readyReplicas), translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
|
||||
row.Cells = append(row.Cells, names, images, metav1.FormatLabelSelector(obj.Spec.Selector))
|
||||
@@ -750,12 +784,28 @@ func printJob(obj *batch.Job, options printers.PrintOptions) ([]metav1beta1.Tabl
|
||||
|
||||
var completions string
|
||||
if obj.Spec.Completions != nil {
|
||||
completions = strconv.Itoa(int(*obj.Spec.Completions))
|
||||
completions = fmt.Sprintf("%d/%d", obj.Status.Succeeded, *obj.Spec.Completions)
|
||||
} else {
|
||||
completions = "<none>"
|
||||
parallelism := int32(0)
|
||||
if obj.Spec.Parallelism != nil {
|
||||
parallelism = *obj.Spec.Parallelism
|
||||
}
|
||||
if parallelism > 1 {
|
||||
completions = fmt.Sprintf("%d/1 of %d", obj.Status.Succeeded, parallelism)
|
||||
} else {
|
||||
completions = fmt.Sprintf("%d/1", obj.Status.Succeeded)
|
||||
}
|
||||
}
|
||||
var jobDuration string
|
||||
switch {
|
||||
case obj.Status.StartTime == nil:
|
||||
case obj.Status.CompletionTime == nil:
|
||||
jobDuration = duration.HumanDuration(time.Now().Sub(obj.Status.StartTime.Time))
|
||||
default:
|
||||
jobDuration = duration.HumanDuration(obj.Status.CompletionTime.Sub(obj.Status.StartTime.Time))
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, completions, int64(obj.Status.Succeeded), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, completions, jobDuration, translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
|
||||
row.Cells = append(row.Cells, names, images, metav1.FormatLabelSelector(obj.Spec.Selector))
|
||||
@@ -782,10 +832,10 @@ func printCronJob(obj *batch.CronJob, options printers.PrintOptions) ([]metav1be
|
||||
|
||||
lastScheduleTime := "<none>"
|
||||
if obj.Status.LastScheduleTime != nil {
|
||||
lastScheduleTime = translateTimestamp(*obj.Status.LastScheduleTime)
|
||||
lastScheduleTime = translateTimestampSince(*obj.Status.LastScheduleTime)
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, obj.Spec.Schedule, printBoolPtr(obj.Spec.Suspend), int64(len(obj.Status.Active)), lastScheduleTime, translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, obj.Spec.Schedule, printBoolPtr(obj.Spec.Suspend), int64(len(obj.Status.Active)), lastScheduleTime, translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
names, images := layoutContainerCells(obj.Spec.JobTemplate.Spec.Template.Spec.Containers)
|
||||
row.Cells = append(row.Cells, names, images, metav1.FormatLabelSelector(obj.Spec.JobTemplate.Spec.Selector))
|
||||
@@ -884,7 +934,7 @@ func printService(obj *api.Service, options printers.PrintOptions) ([]metav1beta
|
||||
svcPorts = "<none>"
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, string(svcType), internalIP, externalIP, svcPorts, translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, string(svcType), internalIP, externalIP, svcPorts, translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
row.Cells = append(row.Cells, labels.FormatLabels(obj.Spec.Selector))
|
||||
}
|
||||
@@ -948,7 +998,7 @@ func printIngress(obj *extensions.Ingress, options printers.PrintOptions) ([]met
|
||||
hosts := formatHosts(obj.Spec.Rules)
|
||||
address := loadBalancerStatusStringer(obj.Status.LoadBalancer, options.Wide)
|
||||
ports := formatPorts(obj.Spec.TLS)
|
||||
createTime := translateTimestamp(obj.CreationTimestamp)
|
||||
createTime := translateTimestampSince(obj.CreationTimestamp)
|
||||
row.Cells = append(row.Cells, obj.Name, hosts, address, ports, createTime)
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
@@ -971,7 +1021,7 @@ func printStatefulSet(obj *apps.StatefulSet, options printers.PrintOptions) ([]m
|
||||
}
|
||||
desiredReplicas := obj.Spec.Replicas
|
||||
currentReplicas := obj.Status.Replicas
|
||||
createTime := translateTimestamp(obj.CreationTimestamp)
|
||||
createTime := translateTimestampSince(obj.CreationTimestamp)
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredReplicas), int64(currentReplicas), createTime)
|
||||
if options.Wide {
|
||||
names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
|
||||
@@ -1003,7 +1053,7 @@ func printDaemonSet(obj *extensions.DaemonSet, options printers.PrintOptions) ([
|
||||
numberUpdated := obj.Status.UpdatedNumberScheduled
|
||||
numberAvailable := obj.Status.NumberAvailable
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredScheduled), int64(currentScheduled), int64(numberReady), int64(numberUpdated), int64(numberAvailable), labels.FormatLabels(obj.Spec.Template.Spec.NodeSelector), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, int64(desiredScheduled), int64(currentScheduled), int64(numberReady), int64(numberUpdated), int64(numberAvailable), labels.FormatLabels(obj.Spec.Template.Spec.NodeSelector), translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
|
||||
row.Cells = append(row.Cells, names, images, metav1.FormatLabelSelector(obj.Spec.Selector))
|
||||
@@ -1027,7 +1077,7 @@ func printEndpoints(obj *api.Endpoints, options printers.PrintOptions) ([]metav1
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, formatEndpoints(obj, nil), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, formatEndpoints(obj, nil), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1047,7 +1097,7 @@ func printNamespace(obj *api.Namespace, options printers.PrintOptions) ([]metav1
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, string(obj.Status.Phase), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, string(obj.Status.Phase), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1067,7 +1117,7 @@ func printSecret(obj *api.Secret, options printers.PrintOptions) ([]metav1beta1.
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, string(obj.Type), int64(len(obj.Data)), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, string(obj.Type), int64(len(obj.Data)), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1087,7 +1137,7 @@ func printServiceAccount(obj *api.ServiceAccount, options printers.PrintOptions)
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, int64(len(obj.Secrets)), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, int64(len(obj.Secrets)), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1136,7 +1186,7 @@ func printNode(obj *api.Node, options printers.PrintOptions) ([]metav1beta1.Tabl
|
||||
roles = "<none>"
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, strings.Join(status, ","), roles, translateTimestamp(obj.CreationTimestamp), obj.Status.NodeInfo.KubeletVersion)
|
||||
row.Cells = append(row.Cells, obj.Name, strings.Join(status, ","), roles, translateTimestampSince(obj.CreationTimestamp), obj.Status.NodeInfo.KubeletVersion)
|
||||
if options.Wide {
|
||||
osImage, kernelVersion, crVersion := obj.Status.NodeInfo.OSImage, obj.Status.NodeInfo.KernelVersion, obj.Status.NodeInfo.ContainerRuntimeVersion
|
||||
if osImage == "" {
|
||||
@@ -1234,7 +1284,7 @@ func printPersistentVolume(obj *api.PersistentVolume, options printers.PrintOpti
|
||||
row.Cells = append(row.Cells, obj.Name, aSize, modesStr, reclaimPolicyStr,
|
||||
string(phase), claimRefUID, helper.GetPersistentVolumeClass(obj),
|
||||
obj.Status.Reason,
|
||||
translateTimestamp(obj.CreationTimestamp))
|
||||
translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1269,7 +1319,7 @@ func printPersistentVolumeClaim(obj *api.PersistentVolumeClaim, options printers
|
||||
capacity = storage.String()
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, string(phase), obj.Spec.VolumeName, capacity, accessModes, helper.GetPersistentVolumeClaimClass(obj), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, string(phase), obj.Spec.VolumeName, capacity, accessModes, helper.GetPersistentVolumeClaimClass(obj), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1290,25 +1340,42 @@ func printEvent(obj *api.Event, options printers.PrintOptions) ([]metav1beta1.Ta
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
// While watching event, we should print absolute time.
|
||||
var FirstTimestamp, LastTimestamp string
|
||||
var firstTimestamp, lastTimestamp string
|
||||
if options.AbsoluteTimestamps {
|
||||
FirstTimestamp = obj.FirstTimestamp.String()
|
||||
LastTimestamp = obj.LastTimestamp.String()
|
||||
firstTimestamp = obj.FirstTimestamp.String()
|
||||
lastTimestamp = obj.LastTimestamp.String()
|
||||
} else {
|
||||
FirstTimestamp = translateTimestamp(obj.FirstTimestamp)
|
||||
LastTimestamp = translateTimestamp(obj.LastTimestamp)
|
||||
firstTimestamp = translateTimestampSince(obj.FirstTimestamp)
|
||||
lastTimestamp = translateTimestampSince(obj.LastTimestamp)
|
||||
}
|
||||
if options.Wide {
|
||||
row.Cells = append(row.Cells,
|
||||
lastTimestamp,
|
||||
obj.Type,
|
||||
obj.Reason,
|
||||
obj.InvolvedObject.Kind,
|
||||
formatEventSource(obj.Source),
|
||||
strings.TrimSpace(obj.Message),
|
||||
obj.InvolvedObject.FieldPath,
|
||||
firstTimestamp,
|
||||
int64(obj.Count),
|
||||
obj.Name,
|
||||
)
|
||||
} else {
|
||||
row.Cells = append(row.Cells,
|
||||
lastTimestamp,
|
||||
obj.Type,
|
||||
obj.Reason,
|
||||
obj.InvolvedObject.Kind,
|
||||
strings.TrimSpace(obj.Message),
|
||||
)
|
||||
}
|
||||
row.Cells = append(row.Cells, LastTimestamp, FirstTimestamp,
|
||||
int64(obj.Count), obj.Name, obj.InvolvedObject.Kind,
|
||||
obj.InvolvedObject.FieldPath, obj.Type, obj.Reason,
|
||||
formatEventSource(obj.Source), obj.Message)
|
||||
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
// Sorts and prints the EventList in a human-friendly format.
|
||||
func printEventList(list *api.EventList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
sort.Sort(events.SortableEvents(list.Items))
|
||||
rows := make([]metav1beta1.TableRow, 0, len(list.Items))
|
||||
for i := range list.Items {
|
||||
r, err := printEvent(&list.Items[i], options)
|
||||
@@ -1325,7 +1392,7 @@ func printRoleBinding(obj *rbac.RoleBinding, options printers.PrintOptions) ([]m
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
roleRef := fmt.Sprintf("%s/%s", obj.RoleRef.Kind, obj.RoleRef.Name)
|
||||
users, groups, sas, _ := rbac.SubjectsStrings(obj.Subjects)
|
||||
@@ -1352,7 +1419,7 @@ func printClusterRoleBinding(obj *rbac.ClusterRoleBinding, options printers.Prin
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, obj.Name, translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, translateTimestampSince(obj.CreationTimestamp))
|
||||
if options.Wide {
|
||||
roleRef := fmt.Sprintf("%s/%s", obj.RoleRef.Kind, obj.RoleRef.Name)
|
||||
users, groups, sas, _ := rbac.SubjectsStrings(obj.Subjects)
|
||||
@@ -1382,7 +1449,7 @@ func printCertificateSigningRequest(obj *certificates.CertificateSigningRequest,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, translateTimestamp(obj.CreationTimestamp), obj.Spec.Username, status)
|
||||
row.Cells = append(row.Cells, obj.Name, translateTimestampSince(obj.CreationTimestamp), obj.Spec.Username, status)
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1475,7 +1542,7 @@ func printDeployment(obj *extensions.Deployment, options printers.PrintOptions)
|
||||
currentReplicas := obj.Status.Replicas
|
||||
updatedReplicas := obj.Status.UpdatedReplicas
|
||||
availableReplicas := obj.Status.AvailableReplicas
|
||||
age := translateTimestamp(obj.CreationTimestamp)
|
||||
age := translateTimestampSince(obj.CreationTimestamp)
|
||||
containers := obj.Spec.Template.Spec.Containers
|
||||
selector, err := metav1.LabelSelectorAsSelector(obj.Spec.Selector)
|
||||
if err != nil {
|
||||
@@ -1513,47 +1580,47 @@ func formatHPAMetrics(specs []autoscaling.MetricSpec, statuses []autoscaling.Met
|
||||
for i, spec := range specs {
|
||||
switch spec.Type {
|
||||
case autoscaling.ExternalMetricSourceType:
|
||||
if spec.External.TargetAverageValue != nil {
|
||||
if spec.External.Target.AverageValue != nil {
|
||||
current := "<unknown>"
|
||||
if len(statuses) > i && statuses[i].External != nil && statuses[i].External.CurrentAverageValue != nil {
|
||||
current = statuses[i].External.CurrentAverageValue.String()
|
||||
if len(statuses) > i && statuses[i].External != nil && &statuses[i].External.Current.AverageValue != nil {
|
||||
current = statuses[i].External.Current.AverageValue.String()
|
||||
}
|
||||
list = append(list, fmt.Sprintf("%s/%s (avg)", current, spec.External.TargetAverageValue.String()))
|
||||
list = append(list, fmt.Sprintf("%s/%s (avg)", current, spec.External.Target.AverageValue.String()))
|
||||
} else {
|
||||
current := "<unknown>"
|
||||
if len(statuses) > i && statuses[i].External != nil {
|
||||
current = statuses[i].External.CurrentValue.String()
|
||||
current = statuses[i].External.Current.Value.String()
|
||||
}
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.External.TargetValue.String()))
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.External.Target.Value.String()))
|
||||
}
|
||||
case autoscaling.PodsMetricSourceType:
|
||||
current := "<unknown>"
|
||||
if len(statuses) > i && statuses[i].Pods != nil {
|
||||
current = statuses[i].Pods.CurrentAverageValue.String()
|
||||
current = statuses[i].Pods.Current.AverageValue.String()
|
||||
}
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.Pods.TargetAverageValue.String()))
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.Pods.Target.AverageValue.String()))
|
||||
case autoscaling.ObjectMetricSourceType:
|
||||
current := "<unknown>"
|
||||
if len(statuses) > i && statuses[i].Object != nil {
|
||||
current = statuses[i].Object.CurrentValue.String()
|
||||
current = statuses[i].Object.Current.Value.String()
|
||||
}
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.Object.TargetValue.String()))
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.Object.Target.Value.String()))
|
||||
case autoscaling.ResourceMetricSourceType:
|
||||
if spec.Resource.TargetAverageValue != nil {
|
||||
if spec.Resource.Target.AverageValue != nil {
|
||||
current := "<unknown>"
|
||||
if len(statuses) > i && statuses[i].Resource != nil {
|
||||
current = statuses[i].Resource.CurrentAverageValue.String()
|
||||
current = statuses[i].Resource.Current.AverageValue.String()
|
||||
}
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.Resource.TargetAverageValue.String()))
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, spec.Resource.Target.AverageValue.String()))
|
||||
} else {
|
||||
current := "<unknown>"
|
||||
if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.CurrentAverageUtilization != nil {
|
||||
current = fmt.Sprintf("%d%%", *statuses[i].Resource.CurrentAverageUtilization)
|
||||
if len(statuses) > i && statuses[i].Resource != nil && statuses[i].Resource.Current.AverageUtilization != nil {
|
||||
current = fmt.Sprintf("%d%%", *statuses[i].Resource.Current.AverageUtilization)
|
||||
}
|
||||
|
||||
target := "<auto>"
|
||||
if spec.Resource.TargetAverageUtilization != nil {
|
||||
target = fmt.Sprintf("%d%%", *spec.Resource.TargetAverageUtilization)
|
||||
if spec.Resource.Target.AverageUtilization != nil {
|
||||
target = fmt.Sprintf("%d%%", *spec.Resource.Target.AverageUtilization)
|
||||
}
|
||||
list = append(list, fmt.Sprintf("%s/%s", current, target))
|
||||
}
|
||||
@@ -1591,7 +1658,7 @@ func printHorizontalPodAutoscaler(obj *autoscaling.HorizontalPodAutoscaler, opti
|
||||
}
|
||||
maxPods := obj.Spec.MaxReplicas
|
||||
currentReplicas := obj.Status.CurrentReplicas
|
||||
row.Cells = append(row.Cells, obj.Name, reference, metrics, minPods, int64(maxPods), int64(currentReplicas), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, reference, metrics, minPods, int64(maxPods), int64(currentReplicas), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1611,7 +1678,7 @@ func printConfigMap(obj *api.ConfigMap, options printers.PrintOptions) ([]metav1
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, int64(len(obj.Data)), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, int64(len(obj.Data)), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1664,7 +1731,7 @@ func printNetworkPolicy(obj *networking.NetworkPolicy, options printers.PrintOpt
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, metav1.FormatLabelSelector(&obj.Spec.PodSelector), translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, obj.Name, metav1.FormatLabelSelector(&obj.Spec.PodSelector), translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
@@ -1690,7 +1757,7 @@ func printStorageClass(obj *storage.StorageClass, options printers.PrintOptions)
|
||||
name += " (default)"
|
||||
}
|
||||
provtype := obj.Provisioner
|
||||
row.Cells = append(row.Cells, name, provtype, translateTimestamp(obj.CreationTimestamp))
|
||||
row.Cells = append(row.Cells, name, provtype, translateTimestampSince(obj.CreationTimestamp))
|
||||
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
@@ -1707,6 +1774,31 @@ func printStorageClassList(list *storage.StorageClassList, options printers.Prin
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func printLease(obj *coordination.Lease, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
|
||||
var holderIdentity string
|
||||
if obj.Spec.HolderIdentity != nil {
|
||||
holderIdentity = *obj.Spec.HolderIdentity
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, holderIdentity, translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
func printLeaseList(list *coordination.LeaseList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
rows := make([]metav1beta1.TableRow, 0, len(list.Items))
|
||||
for i := range list.Items {
|
||||
r, err := printLease(&list.Items[i], options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows = append(rows, r...)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func printStatus(obj *metav1.Status, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
@@ -1776,7 +1868,7 @@ func printControllerRevision(obj *apps.ControllerRevision, options printers.Prin
|
||||
controllerName = printers.FormatResourceName(gvk.GroupKind(), controllerRef.Name, withKind)
|
||||
}
|
||||
revision := obj.Revision
|
||||
age := translateTimestamp(obj.CreationTimestamp)
|
||||
age := translateTimestampSince(obj.CreationTimestamp)
|
||||
row.Cells = append(row.Cells, obj.Name, controllerName, revision, age)
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
362
vendor/k8s.io/kubernetes/pkg/printers/internalversion/printers_test.go
generated
vendored
362
vendor/k8s.io/kubernetes/pkg/printers/internalversion/printers_test.go
generated
vendored
@@ -41,17 +41,18 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
genericprinters "k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/coordination"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
genericprinters "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
@@ -337,7 +338,7 @@ func TestUnknownTypePrinting(t *testing.T) {
|
||||
|
||||
func TestTemplatePanic(t *testing.T) {
|
||||
tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
|
||||
printer, err := printers.NewGoTemplatePrinter([]byte(tmpl))
|
||||
printer, err := genericprinters.NewGoTemplatePrinter([]byte(tmpl))
|
||||
if err != nil {
|
||||
t.Fatalf("tmpl fail: %v", err)
|
||||
}
|
||||
@@ -502,7 +503,7 @@ func TestTemplateStrings(t *testing.T) {
|
||||
}
|
||||
// The point of this test is to verify that the below template works.
|
||||
tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}`
|
||||
printer, err := printers.NewGoTemplatePrinter([]byte(tmpl))
|
||||
printer, err := genericprinters.NewGoTemplatePrinter([]byte(tmpl))
|
||||
if err != nil {
|
||||
t.Fatalf("tmpl fail: %v", err)
|
||||
}
|
||||
@@ -534,17 +535,17 @@ func TestPrinters(t *testing.T) {
|
||||
jsonpathPrinter printers.ResourcePrinter
|
||||
)
|
||||
|
||||
templatePrinter, err = printers.NewGoTemplatePrinter([]byte("{{.name}}"))
|
||||
templatePrinter, err = genericprinters.NewGoTemplatePrinter([]byte("{{.name}}"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
templatePrinter2, err = printers.NewGoTemplatePrinter([]byte("{{len .items}}"))
|
||||
templatePrinter2, err = genericprinters.NewGoTemplatePrinter([]byte("{{len .items}}"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
jsonpathPrinter, err = printers.NewJSONPathPrinter("{.metadata.name}")
|
||||
jsonpathPrinter, err = genericprinters.NewJSONPathPrinter("{.metadata.name}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1151,6 +1152,10 @@ func TestPrintHumanReadableService(t *testing.T) {
|
||||
Port: 8000,
|
||||
Protocol: "TCP",
|
||||
},
|
||||
{
|
||||
Port: 7777,
|
||||
Protocol: "SCTP",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1685,7 +1690,7 @@ func TestPrintPodwide(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
[]metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", int64(6), "<unknown>", "<none>", "<none>"}}},
|
||||
[]metav1beta1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", int64(6), "<unknown>", "<none>", "<none>", "<none>"}}},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1918,18 +1923,43 @@ type stringTestList []struct {
|
||||
name, got, exp string
|
||||
}
|
||||
|
||||
func TestTranslateTimestamp(t *testing.T) {
|
||||
func TestTranslateTimestampSince(t *testing.T) {
|
||||
tl := stringTestList{
|
||||
{"a while from now", translateTimestamp(metav1.Time{Time: time.Now().Add(2.1e9)}), "<invalid>"},
|
||||
{"almost now", translateTimestamp(metav1.Time{Time: time.Now().Add(1.9e9)}), "0s"},
|
||||
{"now", translateTimestamp(metav1.Time{Time: time.Now()}), "0s"},
|
||||
{"unknown", translateTimestamp(metav1.Time{}), "<unknown>"},
|
||||
{"30 seconds ago", translateTimestamp(metav1.Time{Time: time.Now().Add(-3e10)}), "30s"},
|
||||
{"5 minutes ago", translateTimestamp(metav1.Time{Time: time.Now().Add(-3e11)}), "5m"},
|
||||
{"an hour ago", translateTimestamp(metav1.Time{Time: time.Now().Add(-6e12)}), "1h"},
|
||||
{"2 days ago", translateTimestamp(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)}), "2d"},
|
||||
{"months ago", translateTimestamp(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -90)}), "90d"},
|
||||
{"10 years ago", translateTimestamp(metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}), "10y"},
|
||||
{"a while from now", translateTimestampSince(metav1.Time{Time: time.Now().Add(2.1e9)}), "<invalid>"},
|
||||
{"almost now", translateTimestampSince(metav1.Time{Time: time.Now().Add(1.9e9)}), "0s"},
|
||||
{"now", translateTimestampSince(metav1.Time{Time: time.Now()}), "0s"},
|
||||
{"unknown", translateTimestampSince(metav1.Time{}), "<unknown>"},
|
||||
{"30 seconds ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e10)}), "30s"},
|
||||
{"5 minutes ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e11)}), "5m"},
|
||||
{"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "100m"},
|
||||
{"2 days ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)}), "2d"},
|
||||
{"months ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -90)}), "90d"},
|
||||
{"10 years ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}), "10y"},
|
||||
}
|
||||
for _, test := range tl {
|
||||
if test.got != test.exp {
|
||||
t.Errorf("On %v, expected '%v', but got '%v'",
|
||||
test.name, test.exp, test.got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateTimestampUntil(t *testing.T) {
|
||||
// Since this method compares the time with time.Now() internally,
|
||||
// small buffers of 0.1 seconds are added on comparing times to consider method call overhead.
|
||||
// Otherwise, the output strings become shorter than expected.
|
||||
const buf = 1e8
|
||||
tl := stringTestList{
|
||||
{"a while ago", translateTimestampUntil(metav1.Time{Time: time.Now().Add(-2.1e9)}), "<invalid>"},
|
||||
{"almost now", translateTimestampUntil(metav1.Time{Time: time.Now().Add(-1.9e9)}), "0s"},
|
||||
{"now", translateTimestampUntil(metav1.Time{Time: time.Now()}), "0s"},
|
||||
{"unknown", translateTimestampUntil(metav1.Time{}), "<unknown>"},
|
||||
{"in 30 seconds", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e10 + buf)}), "30s"},
|
||||
{"in 5 minutes", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e11 + buf)}), "5m"},
|
||||
{"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "100m"},
|
||||
{"in 2 days", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 2).Add(buf)}), "2d"},
|
||||
{"in months", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 90).Add(buf)}), "90d"},
|
||||
{"in 10 years", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0).Add(buf)}), "10y"},
|
||||
}
|
||||
for _, test := range tl {
|
||||
if test.got != test.exp {
|
||||
@@ -2054,6 +2084,7 @@ func TestPrintDaemonSet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPrintJob(t *testing.T) {
|
||||
now := time.Now()
|
||||
completions := int32(2)
|
||||
tests := []struct {
|
||||
job batch.Job
|
||||
@@ -2072,7 +2103,7 @@ func TestPrintJob(t *testing.T) {
|
||||
Succeeded: 1,
|
||||
},
|
||||
},
|
||||
"job1\t2\t1\t0s\n",
|
||||
"job1\t1/2\t\t0s\n",
|
||||
},
|
||||
{
|
||||
batch.Job{
|
||||
@@ -2087,7 +2118,40 @@ func TestPrintJob(t *testing.T) {
|
||||
Succeeded: 0,
|
||||
},
|
||||
},
|
||||
"job2\t<none>\t0\t10y\n",
|
||||
"job2\t0/1\t\t10y\n",
|
||||
},
|
||||
{
|
||||
batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "job3",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: batch.JobSpec{
|
||||
Completions: nil,
|
||||
},
|
||||
Status: batch.JobStatus{
|
||||
Succeeded: 0,
|
||||
StartTime: &metav1.Time{Time: now.Add(time.Minute)},
|
||||
CompletionTime: &metav1.Time{Time: now.Add(31 * time.Minute)},
|
||||
},
|
||||
},
|
||||
"job3\t0/1\t30m\t10y\n",
|
||||
},
|
||||
{
|
||||
batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "job4",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: batch.JobSpec{
|
||||
Completions: nil,
|
||||
},
|
||||
Status: batch.JobStatus{
|
||||
Succeeded: 0,
|
||||
StartTime: &metav1.Time{Time: time.Now().Add(-20 * time.Minute)},
|
||||
},
|
||||
},
|
||||
"job4\t0/1\t20m\t10y\n",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2112,6 +2176,10 @@ func TestPrintHPA(t *testing.T) {
|
||||
minReplicasVal := int32(2)
|
||||
targetUtilizationVal := int32(80)
|
||||
currentUtilizationVal := int32(50)
|
||||
metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
|
||||
if err != nil {
|
||||
t.Errorf("unable to parse label selector: %v", err)
|
||||
}
|
||||
tests := []struct {
|
||||
hpa autoscaling.HorizontalPodAutoscaler
|
||||
expected string
|
||||
@@ -2149,13 +2217,14 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2182,13 +2251,14 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2200,13 +2270,13 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricStatus{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
CurrentAverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2229,13 +2299,14 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
TargetValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2262,13 +2333,14 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricSource{
|
||||
MetricSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
Selector: metricLabelSelector,
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
MetricName: "some-external-metric",
|
||||
TargetValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2280,8 +2352,12 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ExternalMetricSourceType,
|
||||
External: &autoscaling.ExternalMetricStatus{
|
||||
MetricName: "some-external-metric",
|
||||
CurrentValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-external-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
Value: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2304,8 +2380,13 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "some-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2332,8 +2413,13 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "some-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2345,8 +2431,12 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricStatus{
|
||||
MetricName: "some-pods-metric",
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2369,12 +2459,17 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ObjectMetricSourceType,
|
||||
Object: &autoscaling.ObjectMetricSource{
|
||||
Target: autoscaling.CrossVersionObjectReference{
|
||||
DescribedObject: autoscaling.CrossVersionObjectReference{
|
||||
Name: "some-service",
|
||||
Kind: "Service",
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
TargetValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2401,12 +2496,17 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ObjectMetricSourceType,
|
||||
Object: &autoscaling.ObjectMetricSource{
|
||||
Target: autoscaling.CrossVersionObjectReference{
|
||||
DescribedObject: autoscaling.CrossVersionObjectReference{
|
||||
Name: "some-service",
|
||||
Kind: "Service",
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
TargetValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.ValueMetricType,
|
||||
Value: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2418,12 +2518,16 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ObjectMetricSourceType,
|
||||
Object: &autoscaling.ObjectMetricStatus{
|
||||
Target: autoscaling.CrossVersionObjectReference{
|
||||
DescribedObject: autoscaling.CrossVersionObjectReference{
|
||||
Name: "some-service",
|
||||
Kind: "Service",
|
||||
},
|
||||
MetricName: "some-service-metric",
|
||||
CurrentValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-service-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
Value: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2446,8 +2550,11 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Name: api.ResourceCPU,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2474,8 +2581,11 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Name: api.ResourceCPU,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2487,8 +2597,10 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricStatus{
|
||||
Name: api.ResourceCPU,
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Name: api.ResourceCPU,
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2512,7 +2624,10 @@ func TestPrintHPA(t *testing.T) {
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &targetUtilizationVal,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.UtilizationMetricType,
|
||||
AverageUtilization: &targetUtilizationVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2540,7 +2655,10 @@ func TestPrintHPA(t *testing.T) {
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &targetUtilizationVal,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.UtilizationMetricType,
|
||||
AverageUtilization: &targetUtilizationVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2553,8 +2671,10 @@ func TestPrintHPA(t *testing.T) {
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricStatus{
|
||||
Name: api.ResourceCPU,
|
||||
CurrentAverageUtilization: ¤tUtilizationVal,
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageUtilization: ¤tUtilizationVal,
|
||||
AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2577,22 +2697,35 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "some-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &targetUtilizationVal,
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.UtilizationMetricType,
|
||||
AverageUtilization: &targetUtilizationVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricSource{
|
||||
MetricName: "other-pods-metric",
|
||||
TargetAverageValue: *resource.NewMilliQuantity(400, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "other-pods-metric",
|
||||
},
|
||||
Target: autoscaling.MetricTarget{
|
||||
Type: autoscaling.AverageValueMetricType,
|
||||
AverageValue: resource.NewMilliQuantity(400, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2604,16 +2737,22 @@ func TestPrintHPA(t *testing.T) {
|
||||
{
|
||||
Type: autoscaling.PodsMetricSourceType,
|
||||
Pods: &autoscaling.PodsMetricStatus{
|
||||
MetricName: "some-pods-metric",
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
Metric: autoscaling.MetricIdentifier{
|
||||
Name: "some-pods-metric",
|
||||
},
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageValue: resource.NewMilliQuantity(50, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricStatus{
|
||||
Name: api.ResourceCPU,
|
||||
CurrentAverageUtilization: ¤tUtilizationVal,
|
||||
CurrentAverageValue: *resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
Current: autoscaling.MetricValueStatus{
|
||||
AverageUtilization: ¤tUtilizationVal,
|
||||
AverageValue: resource.NewMilliQuantity(40, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3299,6 +3438,55 @@ func TestPrintStorageClass(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintLease(t *testing.T) {
|
||||
holder1 := "holder1"
|
||||
holder2 := "holder2"
|
||||
tests := []struct {
|
||||
sc coordination.Lease
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
coordination.Lease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "lease1",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
|
||||
},
|
||||
Spec: coordination.LeaseSpec{
|
||||
HolderIdentity: &holder1,
|
||||
},
|
||||
},
|
||||
"lease1\tholder1\t0s\n",
|
||||
},
|
||||
{
|
||||
coordination.Lease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "lease2",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().Add(-3e11)},
|
||||
},
|
||||
Spec: coordination.LeaseSpec{
|
||||
HolderIdentity: &holder2,
|
||||
},
|
||||
},
|
||||
"lease2\tholder2\t5m\n",
|
||||
},
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
for _, test := range tests {
|
||||
table, err := printers.NewTablePrinter().With(AddHandlers).PrintTable(&test.sc, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := printers.PrintTable(table, buf, printers.PrintOptions{NoHeaders: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if buf.String() != test.expect {
|
||||
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func verifyTable(t *testing.T, table *metav1beta1.Table) {
|
||||
var panicErr interface{}
|
||||
func() {
|
||||
|
159
vendor/k8s.io/kubernetes/pkg/printers/jsonpath.go
generated
vendored
159
vendor/k8s.io/kubernetes/pkg/printers/jsonpath.go
generated
vendored
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/util/jsonpath"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
)
|
||||
|
||||
// exists returns true if it would be possible to call the index function
|
||||
// with these arguments.
|
||||
//
|
||||
// TODO: how to document this for users?
|
||||
//
|
||||
// index returns the result of indexing its first argument by the following
|
||||
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
|
||||
// indexed item must be a map, slice, or array.
|
||||
func exists(item interface{}, indices ...interface{}) bool {
|
||||
v := reflect.ValueOf(item)
|
||||
for _, i := range indices {
|
||||
index := reflect.ValueOf(i)
|
||||
var isNil bool
|
||||
if v, isNil = indirect(v); isNil {
|
||||
return false
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
var x int64
|
||||
switch index.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
x = index.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
x = int64(index.Uint())
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if x < 0 || x >= int64(v.Len()) {
|
||||
return false
|
||||
}
|
||||
v = v.Index(int(x))
|
||||
case reflect.Map:
|
||||
if !index.IsValid() {
|
||||
index = reflect.Zero(v.Type().Key())
|
||||
}
|
||||
if !index.Type().AssignableTo(v.Type().Key()) {
|
||||
return false
|
||||
}
|
||||
if x := v.MapIndex(index); x.IsValid() {
|
||||
v = x
|
||||
} else {
|
||||
v = reflect.Zero(v.Type().Elem())
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
if _, isNil := indirect(v); isNil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// stolen from text/template
|
||||
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
|
||||
// We indirect through pointers and empty interfaces (only) because
|
||||
// non-empty interfaces have methods we might need.
|
||||
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
|
||||
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
|
||||
if v.IsNil() {
|
||||
return v, true
|
||||
}
|
||||
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return v, false
|
||||
}
|
||||
|
||||
// JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression.
|
||||
type JSONPathPrinter struct {
|
||||
rawTemplate string
|
||||
*jsonpath.JSONPath
|
||||
}
|
||||
|
||||
func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
|
||||
j := jsonpath.New("out")
|
||||
if err := j.Parse(tmpl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &JSONPathPrinter{
|
||||
rawTemplate: tmpl,
|
||||
JSONPath: j,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PrintObj formats the obj with the JSONPath Template.
|
||||
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
// we use reflect.Indirect here in order to obtain the actual value from a pointer.
|
||||
// we need an actual value in order to retrieve the package path for an object.
|
||||
// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
|
||||
if printers.InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||
return fmt.Errorf(printers.InternalObjectPrinterErr)
|
||||
}
|
||||
|
||||
var queryObj interface{} = obj
|
||||
if meta.IsListType(obj) {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
queryObj = map[string]interface{}{}
|
||||
if err := json.Unmarshal(data, &queryObj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if unknown, ok := obj.(*runtime.Unknown); ok {
|
||||
data, err := json.Marshal(unknown)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
queryObj = map[string]interface{}{}
|
||||
if err := json.Unmarshal(data, &queryObj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if unstructured, ok := obj.(runtime.Unstructured); ok {
|
||||
queryObj = unstructured.UnstructuredContent()
|
||||
}
|
||||
|
||||
if err := j.JSONPath.Execute(w, queryObj); err != nil {
|
||||
fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
|
||||
fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", j.rawTemplate)
|
||||
fmt.Fprintf(w, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj)
|
||||
return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, err)
|
||||
}
|
||||
return nil
|
||||
}
|
128
vendor/k8s.io/kubernetes/pkg/printers/jsonpath_flags.go
generated
vendored
128
vendor/k8s.io/kubernetes/pkg/printers/jsonpath_flags.go
generated
vendored
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
// templates are logically optional for specifying a format.
|
||||
// this allows a user to specify a template format value
|
||||
// as --output=jsonpath=
|
||||
var jsonFormats = map[string]bool{
|
||||
"jsonpath": true,
|
||||
"jsonpath-file": true,
|
||||
}
|
||||
|
||||
// JSONPathPrintFlags provides default flags necessary for template printing.
|
||||
// Given the following flag values, a printer can be requested that knows
|
||||
// how to handle printing based on these values.
|
||||
type JSONPathPrintFlags struct {
|
||||
// indicates if it is OK to ignore missing keys for rendering
|
||||
// an output template.
|
||||
AllowMissingKeys *bool
|
||||
TemplateArgument *string
|
||||
}
|
||||
|
||||
func (f *JSONPathPrintFlags) AllowedFormats() []string {
|
||||
formats := make([]string, 0, len(jsonFormats))
|
||||
for format := range jsonFormats {
|
||||
formats = append(formats, format)
|
||||
}
|
||||
return formats
|
||||
}
|
||||
|
||||
// ToPrinter receives an templateFormat and returns a printer capable of
|
||||
// handling --template format printing.
|
||||
// Returns false if the specified templateFormat does not match a template format.
|
||||
func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, error) {
|
||||
if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
|
||||
return nil, genericclioptions.NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
|
||||
}
|
||||
|
||||
templateValue := ""
|
||||
|
||||
if f.TemplateArgument == nil || len(*f.TemplateArgument) == 0 {
|
||||
for format := range jsonFormats {
|
||||
format = format + "="
|
||||
if strings.HasPrefix(templateFormat, format) {
|
||||
templateValue = templateFormat[len(format):]
|
||||
templateFormat = format[:len(format)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
templateValue = *f.TemplateArgument
|
||||
}
|
||||
|
||||
if _, supportedFormat := jsonFormats[templateFormat]; !supportedFormat {
|
||||
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &templateFormat, AllowedFormats: f.AllowedFormats()}
|
||||
}
|
||||
|
||||
if len(templateValue) == 0 {
|
||||
return nil, fmt.Errorf("template format specified but no template given")
|
||||
}
|
||||
|
||||
if templateFormat == "jsonpath-file" {
|
||||
data, err := ioutil.ReadFile(templateValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading --template %s, %v\n", templateValue, err)
|
||||
}
|
||||
|
||||
templateValue = string(data)
|
||||
}
|
||||
|
||||
p, err := NewJSONPathPrinter(templateValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing jsonpath %s, %v\n", templateValue, err)
|
||||
}
|
||||
|
||||
allowMissingKeys := true
|
||||
if f.AllowMissingKeys != nil {
|
||||
allowMissingKeys = *f.AllowMissingKeys
|
||||
}
|
||||
|
||||
p.AllowMissingKeys(allowMissingKeys)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// AddFlags receives a *cobra.Command reference and binds
|
||||
// flags related to template printing to it
|
||||
func (f *JSONPathPrintFlags) AddFlags(c *cobra.Command) {
|
||||
if f.TemplateArgument != nil {
|
||||
c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when --output=jsonpath, --output=jsonpath-file.")
|
||||
c.MarkFlagFilename("template")
|
||||
}
|
||||
if f.AllowMissingKeys != nil {
|
||||
c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
|
||||
}
|
||||
}
|
||||
|
||||
// NewJSONPathPrintFlags returns flags associated with
|
||||
// --template printing, with default values set.
|
||||
func NewJSONPathPrintFlags(templateValue string, allowMissingKeys bool) *JSONPathPrintFlags {
|
||||
return &JSONPathPrintFlags{
|
||||
TemplateArgument: &templateValue,
|
||||
AllowMissingKeys: &allowMissingKeys,
|
||||
}
|
||||
}
|
212
vendor/k8s.io/kubernetes/pkg/printers/jsonpath_flags_test.go
generated
vendored
212
vendor/k8s.io/kubernetes/pkg/printers/jsonpath_flags_test.go
generated
vendored
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
func TestPrinterSupportsExpectedJSONPathFormats(t *testing.T) {
|
||||
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
|
||||
jsonpathFile, err := ioutil.TempFile("", "printers_jsonpath_flags")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer func(tempFile *os.File) {
|
||||
tempFile.Close()
|
||||
os.Remove(tempFile.Name())
|
||||
}(jsonpathFile)
|
||||
|
||||
fmt.Fprintf(jsonpathFile, "{ .metadata.name }\n")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
outputFormat string
|
||||
templateArg string
|
||||
expectedError string
|
||||
expectedParseError string
|
||||
expectedOutput string
|
||||
expectNoMatch bool
|
||||
}{
|
||||
{
|
||||
name: "valid output format also containing the jsonpath argument succeeds",
|
||||
outputFormat: "jsonpath={ .metadata.name }",
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "valid output format and no --template argument results in an error",
|
||||
outputFormat: "jsonpath",
|
||||
expectedError: "template format specified but no template given",
|
||||
},
|
||||
{
|
||||
name: "valid output format and --template argument succeeds",
|
||||
outputFormat: "jsonpath",
|
||||
templateArg: "{ .metadata.name }",
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "jsonpath template file should match, and successfully return correct value",
|
||||
outputFormat: "jsonpath-file",
|
||||
templateArg: jsonpathFile.Name(),
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "valid output format and invalid --template argument results in a parsing from the printer",
|
||||
outputFormat: "jsonpath",
|
||||
templateArg: "{invalid}",
|
||||
expectedParseError: "unrecognized identifier invalid",
|
||||
},
|
||||
{
|
||||
name: "no printer is matched on an invalid outputFormat",
|
||||
outputFormat: "invalid",
|
||||
expectNoMatch: true,
|
||||
},
|
||||
{
|
||||
name: "jsonpath printer should not match on any other format supported by another printer",
|
||||
outputFormat: "go-template",
|
||||
expectNoMatch: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
templateArg := &tc.templateArg
|
||||
if len(tc.templateArg) == 0 {
|
||||
templateArg = nil
|
||||
}
|
||||
|
||||
printFlags := JSONPathPrintFlags{
|
||||
TemplateArgument: templateArg,
|
||||
}
|
||||
|
||||
p, err := printFlags.ToPrinter(tc.outputFormat)
|
||||
if tc.expectNoMatch {
|
||||
if !genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
|
||||
}
|
||||
return
|
||||
}
|
||||
if genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
|
||||
}
|
||||
|
||||
if len(tc.expectedError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
err = p.PrintObj(testObject, out)
|
||||
if len(tc.expectedParseError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedParseError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(out.String(), tc.expectedOutput) {
|
||||
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONPathPrinterDefaultsAllowMissingKeysToTrue(t *testing.T) {
|
||||
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
|
||||
allowMissingKeys := false
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
templateArg string
|
||||
expectedOutput string
|
||||
expectedError string
|
||||
allowMissingKeys *bool
|
||||
}{
|
||||
{
|
||||
name: "existing field does not error and returns expected value",
|
||||
templateArg: "{ .metadata.name }",
|
||||
expectedOutput: "foo",
|
||||
allowMissingKeys: &allowMissingKeys,
|
||||
},
|
||||
{
|
||||
name: "missing field does not error and returns an empty string since missing keys are allowed by default",
|
||||
templateArg: "{ .metadata.missing }",
|
||||
expectedOutput: "",
|
||||
allowMissingKeys: nil,
|
||||
},
|
||||
{
|
||||
name: "missing field returns expected error if field is missing and allowMissingKeys is explicitly set to false",
|
||||
templateArg: "{ .metadata.missing }",
|
||||
expectedError: "error executing jsonpath",
|
||||
allowMissingKeys: &allowMissingKeys,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
printFlags := JSONPathPrintFlags{
|
||||
TemplateArgument: &tc.templateArg,
|
||||
AllowMissingKeys: tc.allowMissingKeys,
|
||||
}
|
||||
|
||||
outputFormat := "jsonpath"
|
||||
p, err := printFlags.ToPrinter(outputFormat)
|
||||
if genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected to match template printer for output format %q", outputFormat)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
err = p.PrintObj(testObject, out)
|
||||
|
||||
if len(tc.expectedError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(out.String()) != len(tc.expectedOutput) {
|
||||
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
78
vendor/k8s.io/kubernetes/pkg/printers/kube_template_flags.go
generated
vendored
78
vendor/k8s.io/kubernetes/pkg/printers/kube_template_flags.go
generated
vendored
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
// KubeTemplatePrintFlags composes print flags that provide both a JSONPath and a go-template printer.
|
||||
// This is necessary if dealing with cases that require support both both printers, since both sets of flags
|
||||
// require overlapping flags.
|
||||
type KubeTemplatePrintFlags struct {
|
||||
*GoTemplatePrintFlags
|
||||
*JSONPathPrintFlags
|
||||
|
||||
AllowMissingKeys *bool
|
||||
TemplateArgument *string
|
||||
}
|
||||
|
||||
func (f *KubeTemplatePrintFlags) AllowedFormats() []string {
|
||||
return append(f.GoTemplatePrintFlags.AllowedFormats(), f.JSONPathPrintFlags.AllowedFormats()...)
|
||||
}
|
||||
|
||||
func (f *KubeTemplatePrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, error) {
|
||||
if p, err := f.JSONPathPrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
return p, err
|
||||
}
|
||||
return f.GoTemplatePrintFlags.ToPrinter(outputFormat)
|
||||
}
|
||||
|
||||
// AddFlags receives a *cobra.Command reference and binds
|
||||
// flags related to template printing to it
|
||||
func (f *KubeTemplatePrintFlags) AddFlags(c *cobra.Command) {
|
||||
if f.TemplateArgument != nil {
|
||||
c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
|
||||
c.MarkFlagFilename("template")
|
||||
}
|
||||
if f.AllowMissingKeys != nil {
|
||||
c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
|
||||
}
|
||||
}
|
||||
|
||||
// NewKubeTemplatePrintFlags returns flags associated with
|
||||
// --template printing, with default values set.
|
||||
func NewKubeTemplatePrintFlags() *KubeTemplatePrintFlags {
|
||||
allowMissingKeysPtr := true
|
||||
templateArgPtr := ""
|
||||
|
||||
return &KubeTemplatePrintFlags{
|
||||
GoTemplatePrintFlags: &GoTemplatePrintFlags{
|
||||
TemplateArgument: &templateArgPtr,
|
||||
AllowMissingKeys: &allowMissingKeysPtr,
|
||||
},
|
||||
JSONPathPrintFlags: &JSONPathPrintFlags{
|
||||
TemplateArgument: &templateArgPtr,
|
||||
AllowMissingKeys: &allowMissingKeysPtr,
|
||||
},
|
||||
|
||||
TemplateArgument: &templateArgPtr,
|
||||
AllowMissingKeys: &allowMissingKeysPtr,
|
||||
}
|
||||
}
|
4
vendor/k8s.io/kubernetes/pkg/printers/storage/BUILD
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/printers/storage/BUILD
generated
vendored
@@ -11,8 +11,8 @@ go_library(
|
||||
importpath = "k8s.io/kubernetes/pkg/printers/storage",
|
||||
deps = [
|
||||
"//pkg/printers:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/printers/tabwriter.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/printers/tabwriter.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tabwriterMinWidth = 10
|
||||
tabwriterMinWidth = 6
|
||||
tabwriterWidth = 4
|
||||
tabwriterPadding = 3
|
||||
tabwriterPadChar = ' '
|
||||
|
119
vendor/k8s.io/kubernetes/pkg/printers/template.go
generated
vendored
119
vendor/k8s.io/kubernetes/pkg/printers/template.go
generated
vendored
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"text/template"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
)
|
||||
|
||||
// GoTemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
|
||||
type GoTemplatePrinter struct {
|
||||
rawTemplate string
|
||||
template *template.Template
|
||||
}
|
||||
|
||||
func NewGoTemplatePrinter(tmpl []byte) (*GoTemplatePrinter, error) {
|
||||
t, err := template.New("output").
|
||||
Funcs(template.FuncMap{
|
||||
"exists": exists,
|
||||
"base64decode": base64decode,
|
||||
}).
|
||||
Parse(string(tmpl))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &GoTemplatePrinter{
|
||||
rawTemplate: string(tmpl),
|
||||
template: t,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AllowMissingKeys tells the template engine if missing keys are allowed.
|
||||
func (p *GoTemplatePrinter) AllowMissingKeys(allow bool) {
|
||||
if allow {
|
||||
p.template.Option("missingkey=default")
|
||||
} else {
|
||||
p.template.Option("missingkey=error")
|
||||
}
|
||||
}
|
||||
|
||||
// PrintObj formats the obj with the Go Template.
|
||||
func (p *GoTemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
if printers.InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||
return fmt.Errorf(printers.InternalObjectPrinterErr)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
var err error
|
||||
data, err = json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out := map[string]interface{}{}
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = p.safeExecute(w, out); err != nil {
|
||||
// It is way easier to debug this stuff when it shows up in
|
||||
// stdout instead of just stdin. So in addition to returning
|
||||
// a nice error, also print useful stuff with the writer.
|
||||
fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
|
||||
fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", p.rawTemplate)
|
||||
fmt.Fprintf(w, "\traw data was:\n\t\t%v\n", string(data))
|
||||
fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n", out)
|
||||
return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// safeExecute tries to execute the template, but catches panics and returns an error
|
||||
// should the template engine panic.
|
||||
func (p *GoTemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
|
||||
var panicErr error
|
||||
// Sorry for the double anonymous function. There's probably a clever way
|
||||
// to do this that has the defer'd func setting the value to be returned, but
|
||||
// that would be even less obvious.
|
||||
retErr := func() error {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
panicErr = fmt.Errorf("caught panic: %+v", x)
|
||||
}
|
||||
}()
|
||||
return p.template.Execute(w, obj)
|
||||
}()
|
||||
if panicErr != nil {
|
||||
return panicErr
|
||||
}
|
||||
return retErr
|
||||
}
|
||||
|
||||
func base64decode(v string) (string, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("base64 decode failed: %v", err)
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
133
vendor/k8s.io/kubernetes/pkg/printers/template_flags.go
generated
vendored
133
vendor/k8s.io/kubernetes/pkg/printers/template_flags.go
generated
vendored
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
// templates are logically optional for specifying a format.
|
||||
// this allows a user to specify a template format value
|
||||
// as --output=go-template=
|
||||
var templateFormats = map[string]bool{
|
||||
"template": true,
|
||||
"go-template": true,
|
||||
"go-template-file": true,
|
||||
"templatefile": true,
|
||||
}
|
||||
|
||||
// GoTemplatePrintFlags provides default flags necessary for template printing.
|
||||
// Given the following flag values, a printer can be requested that knows
|
||||
// how to handle printing based on these values.
|
||||
type GoTemplatePrintFlags struct {
|
||||
// indicates if it is OK to ignore missing keys for rendering
|
||||
// an output template.
|
||||
AllowMissingKeys *bool
|
||||
TemplateArgument *string
|
||||
}
|
||||
|
||||
func (f *GoTemplatePrintFlags) AllowedFormats() []string {
|
||||
formats := make([]string, 0, len(templateFormats))
|
||||
for format := range templateFormats {
|
||||
formats = append(formats, format)
|
||||
}
|
||||
return formats
|
||||
}
|
||||
|
||||
// ToPrinter receives an templateFormat and returns a printer capable of
|
||||
// handling --template format printing.
|
||||
// Returns false if the specified templateFormat does not match a template format.
|
||||
func (f *GoTemplatePrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, error) {
|
||||
if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
|
||||
return nil, genericclioptions.NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
|
||||
}
|
||||
|
||||
templateValue := ""
|
||||
|
||||
if f.TemplateArgument == nil || len(*f.TemplateArgument) == 0 {
|
||||
for format := range templateFormats {
|
||||
format = format + "="
|
||||
if strings.HasPrefix(templateFormat, format) {
|
||||
templateValue = templateFormat[len(format):]
|
||||
templateFormat = format[:len(format)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
templateValue = *f.TemplateArgument
|
||||
}
|
||||
|
||||
if _, supportedFormat := templateFormats[templateFormat]; !supportedFormat {
|
||||
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &templateFormat, AllowedFormats: f.AllowedFormats()}
|
||||
}
|
||||
|
||||
if len(templateValue) == 0 {
|
||||
return nil, fmt.Errorf("template format specified but no template given")
|
||||
}
|
||||
|
||||
if templateFormat == "templatefile" || templateFormat == "go-template-file" {
|
||||
data, err := ioutil.ReadFile(templateValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading --template %s, %v\n", templateValue, err)
|
||||
}
|
||||
|
||||
templateValue = string(data)
|
||||
}
|
||||
|
||||
p, err := NewGoTemplatePrinter([]byte(templateValue))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing template %s, %v\n", templateValue, err)
|
||||
}
|
||||
|
||||
allowMissingKeys := true
|
||||
if f.AllowMissingKeys != nil {
|
||||
allowMissingKeys = *f.AllowMissingKeys
|
||||
}
|
||||
|
||||
p.AllowMissingKeys(allowMissingKeys)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// AddFlags receives a *cobra.Command reference and binds
|
||||
// flags related to template printing to it
|
||||
func (f *GoTemplatePrintFlags) AddFlags(c *cobra.Command) {
|
||||
if f.TemplateArgument != nil {
|
||||
c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
|
||||
c.MarkFlagFilename("template")
|
||||
}
|
||||
if f.AllowMissingKeys != nil {
|
||||
c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
|
||||
}
|
||||
}
|
||||
|
||||
// NewGoTemplatePrintFlags returns flags associated with
|
||||
// --template printing, with default values set.
|
||||
func NewGoTemplatePrintFlags() *GoTemplatePrintFlags {
|
||||
allowMissingKeysPtr := true
|
||||
templateValuePtr := ""
|
||||
|
||||
return &GoTemplatePrintFlags{
|
||||
TemplateArgument: &templateValuePtr,
|
||||
AllowMissingKeys: &allowMissingKeysPtr,
|
||||
}
|
||||
}
|
206
vendor/k8s.io/kubernetes/pkg/printers/template_flags_test.go
generated
vendored
206
vendor/k8s.io/kubernetes/pkg/printers/template_flags_test.go
generated
vendored
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
func TestPrinterSupportsExpectedTemplateFormats(t *testing.T) {
|
||||
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
|
||||
templateFile, err := ioutil.TempFile("", "printers_jsonpath_flags")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer func(tempFile *os.File) {
|
||||
tempFile.Close()
|
||||
os.Remove(tempFile.Name())
|
||||
}(templateFile)
|
||||
|
||||
fmt.Fprintf(templateFile, "{{ .metadata.name }}")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
outputFormat string
|
||||
templateArg string
|
||||
expectedError string
|
||||
expectedParseError string
|
||||
expectedOutput string
|
||||
expectNoMatch bool
|
||||
}{
|
||||
{
|
||||
name: "valid output format also containing the template argument succeeds",
|
||||
outputFormat: "go-template={{ .metadata.name }}",
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "valid output format and no template argument results in an error",
|
||||
outputFormat: "template",
|
||||
expectedError: "template format specified but no template given",
|
||||
},
|
||||
{
|
||||
name: "valid output format and template argument succeeds",
|
||||
outputFormat: "go-template",
|
||||
templateArg: "{{ .metadata.name }}",
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "Go-template file should match, and successfully return correct value",
|
||||
outputFormat: "go-template-file",
|
||||
templateArg: templateFile.Name(),
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "valid output format and invalid template argument results in the templateArg contents as the output",
|
||||
outputFormat: "go-template",
|
||||
templateArg: "invalid",
|
||||
expectedOutput: "invalid",
|
||||
},
|
||||
{
|
||||
name: "no printer is matched on an invalid outputFormat",
|
||||
outputFormat: "invalid",
|
||||
expectNoMatch: true,
|
||||
},
|
||||
{
|
||||
name: "go-template printer should not match on any other format supported by another printer",
|
||||
outputFormat: "jsonpath",
|
||||
expectNoMatch: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
templateArg := &tc.templateArg
|
||||
if len(tc.templateArg) == 0 {
|
||||
templateArg = nil
|
||||
}
|
||||
|
||||
printFlags := GoTemplatePrintFlags{
|
||||
TemplateArgument: templateArg,
|
||||
}
|
||||
|
||||
p, err := printFlags.ToPrinter(tc.outputFormat)
|
||||
if tc.expectNoMatch {
|
||||
if !genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
|
||||
}
|
||||
return
|
||||
}
|
||||
if genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
|
||||
}
|
||||
|
||||
if len(tc.expectedError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
err = p.PrintObj(testObject, out)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(out.String()) != len(tc.expectedOutput) {
|
||||
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplatePrinterDefaultsAllowMissingKeysToTrue(t *testing.T) {
|
||||
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
|
||||
allowMissingKeys := false
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
templateArg string
|
||||
expectedOutput string
|
||||
expectedError string
|
||||
allowMissingKeys *bool
|
||||
}{
|
||||
{
|
||||
name: "existing field does not error and returns expected value",
|
||||
templateArg: "{{ .metadata.name }}",
|
||||
expectedOutput: "foo",
|
||||
allowMissingKeys: &allowMissingKeys,
|
||||
},
|
||||
{
|
||||
name: "missing field does not error and returns no value since missing keys are allowed by default",
|
||||
templateArg: "{{ .metadata.missing }}",
|
||||
expectedOutput: "<no value>",
|
||||
allowMissingKeys: nil,
|
||||
},
|
||||
{
|
||||
name: "missing field returns expected error if field is missing and allowMissingKeys is explicitly set to false",
|
||||
templateArg: "{{ .metadata.missing }}",
|
||||
expectedError: "error executing template",
|
||||
allowMissingKeys: &allowMissingKeys,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
printFlags := GoTemplatePrintFlags{
|
||||
TemplateArgument: &tc.templateArg,
|
||||
AllowMissingKeys: tc.allowMissingKeys,
|
||||
}
|
||||
|
||||
outputFormat := "template"
|
||||
p, err := printFlags.ToPrinter(outputFormat)
|
||||
if genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected to match template printer for output format %q", outputFormat)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
err = p.PrintObj(testObject, out)
|
||||
|
||||
if len(tc.expectedError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(out.String()) != len(tc.expectedOutput) {
|
||||
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
102
vendor/k8s.io/kubernetes/pkg/printers/template_test.go
generated
vendored
102
vendor/k8s.io/kubernetes/pkg/printers/template_test.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestTemplate(t *testing.T) {
|
||||
testCase := []struct {
|
||||
name string
|
||||
template string
|
||||
obj runtime.Object
|
||||
expectOut string
|
||||
expectErr func(error) (string, bool)
|
||||
}{
|
||||
{
|
||||
name: "support base64 decoding of secret data",
|
||||
template: "{{ .data.username | base64decode }}",
|
||||
obj: &v1.Secret{
|
||||
Data: map[string][]byte{
|
||||
"username": []byte("hunter"),
|
||||
},
|
||||
},
|
||||
expectOut: "hunter",
|
||||
},
|
||||
{
|
||||
name: "test error path for base64 decoding",
|
||||
template: "{{ .data.username | base64decode }}",
|
||||
obj: &badlyMarshaledSecret{},
|
||||
expectErr: func(err error) (string, bool) {
|
||||
matched := strings.Contains(err.Error(), "base64 decode")
|
||||
return "a base64 decode error", matched
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range testCase {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
p, err := NewGoTemplatePrinter([]byte(test.template))
|
||||
if err != nil {
|
||||
if test.expectErr == nil {
|
||||
t.Errorf("[%s]expected success but got:\n %v\n", test.name, err)
|
||||
return
|
||||
}
|
||||
if expected, ok := test.expectErr(err); !ok {
|
||||
t.Errorf("[%s]expect:\n %v\n but got:\n %v\n", test.name, expected, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = p.PrintObj(test.obj, buffer)
|
||||
if err != nil {
|
||||
if test.expectErr == nil {
|
||||
t.Errorf("[%s]expected success but got:\n %v\n", test.name, err)
|
||||
return
|
||||
}
|
||||
if expected, ok := test.expectErr(err); !ok {
|
||||
t.Errorf("[%s]expect:\n %v\n but got:\n %v\n", test.name, expected, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if test.expectErr != nil {
|
||||
t.Errorf("[%s]expect:\n error\n but got:\n no error\n", test.name)
|
||||
return
|
||||
}
|
||||
|
||||
if test.expectOut != buffer.String() {
|
||||
t.Errorf("[%s]expect:\n %v\n but got:\n %v\n", test.name, test.expectOut, buffer.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type badlyMarshaledSecret struct {
|
||||
v1.Secret
|
||||
}
|
||||
|
||||
func (a badlyMarshaledSecret) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`{"apiVersion":"v1","data":{"username":"--THIS IS NOT BASE64--"},"kind":"Secret"}`), nil
|
||||
}
|
Reference in New Issue
Block a user