mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-04 10:47:25 +00:00
Merge pull request #55689 from luksa/kubectl_explain_kind_version
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Make kubectl explain print the Kind and APIVersion of the resource **What this PR does / why we need it**: Kubectl explain currently doesn't print out the Kind and APIversion of the resource being explained. When running `kubectl explain hpa.spec`, for example, there is no way of knowing whether you're looking at the `autoscaling/v1` or the `autoscaling/v2beta1` version. Also, `kubectl explain` is often used as a reference when writing YAML/JSON object manifests. It allows you to look up everything except the API version. Currently, you either need to know the API Version of a resource by heart or look it up in the online API docs. This PR fixes both problems by having `kubectl explain` print out the full Kind and APIVersion of the resource it is explaining. Here are a few examples of the new output: ``` $ kubectl explain deploy KIND: Deployment VERSION: extensions/v1beta1 DESCRIPTION: ... $ kubectl explain hpa.spec KIND: HorizontalPodAutoscaler VERSION: autoscaling/v1 RESOURCE: spec <Object> DESCRIPTION: ... $ kubectl explain hpa.spec.maxReplicas KIND: HorizontalPodAutoscaler VERSION: autoscaling/v1 FIELD: maxReplicas <integer> DESCRIPTION: ... $ kubectl explain hpa.spec --recursive KIND: HorizontalPodAutoscaler VERSION: autoscaling/v1 RESOURCE: spec <Object> DESCRIPTION: behaviour of autoscaler. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status. specification of a horizontal pod autoscaler. FIELDS: maxReplicas <integer> minReplicas <integer> scaleTargetRef <Object> apiVersion <string> kind <string> name <string> targetCPUUtilizationPercentage <integer> ``` **Release note**: ```release-note Kubectl explain now prints out the Kind and API version of the resource being explained ```
This commit is contained in:
@@ -129,5 +129,5 @@ func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, ar
|
|||||||
return fmt.Errorf("Couldn't find resource for %q", gvk)
|
return fmt.Errorf("Couldn't find resource for %q", gvk)
|
||||||
}
|
}
|
||||||
|
|
||||||
return explain.PrintModelDescription(fieldsPath, out, schema, recursive)
|
return explain.PrintModelDescription(fieldsPath, out, schema, gvk, recursive)
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ go_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@@ -21,6 +21,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/kube-openapi/pkg/util/proto"
|
"k8s.io/kube-openapi/pkg/util/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (st
|
|||||||
// PrintModelDescription prints the description of a specific model or dot path.
|
// PrintModelDescription prints the description of a specific model or dot path.
|
||||||
// If recursive, all components nested within the fields of the schema will be
|
// If recursive, all components nested within the fields of the schema will be
|
||||||
// printed.
|
// printed.
|
||||||
func PrintModelDescription(fieldsPath []string, w io.Writer, schema proto.Schema, recursive bool) error {
|
func PrintModelDescription(fieldsPath []string, w io.Writer, schema proto.Schema, gvk schema.GroupVersionKind, recursive bool) error {
|
||||||
fieldName := ""
|
fieldName := ""
|
||||||
if len(fieldsPath) != 0 {
|
if len(fieldsPath) != 0 {
|
||||||
fieldName = fieldsPath[len(fieldsPath)-1]
|
fieldName = fieldsPath[len(fieldsPath)-1]
|
||||||
@@ -64,5 +65,5 @@ func PrintModelDescription(fieldsPath []string, w io.Writer, schema proto.Schema
|
|||||||
}
|
}
|
||||||
b := fieldsPrinterBuilder{Recursive: recursive}
|
b := fieldsPrinterBuilder{Recursive: recursive}
|
||||||
f := &Formatter{Writer: w, Wrap: 80}
|
f := &Formatter{Writer: w, Wrap: 80}
|
||||||
return PrintModel(fieldName, f, b, schema)
|
return PrintModel(fieldName, f, b, schema, gvk)
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,10 @@ limitations under the License.
|
|||||||
|
|
||||||
package explain
|
package explain
|
||||||
|
|
||||||
import "k8s.io/kube-openapi/pkg/util/proto"
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/kube-openapi/pkg/util/proto"
|
||||||
|
)
|
||||||
|
|
||||||
// fieldIndentLevel is the level of indentation for fields.
|
// fieldIndentLevel is the level of indentation for fields.
|
||||||
const fieldIndentLevel = 3
|
const fieldIndentLevel = 3
|
||||||
@@ -33,11 +36,19 @@ type modelPrinter struct {
|
|||||||
Descriptions []string
|
Descriptions []string
|
||||||
Writer *Formatter
|
Writer *Formatter
|
||||||
Builder fieldsPrinterBuilder
|
Builder fieldsPrinterBuilder
|
||||||
|
GVK schema.GroupVersionKind
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ proto.SchemaVisitor = &modelPrinter{}
|
var _ proto.SchemaVisitor = &modelPrinter{}
|
||||||
|
|
||||||
|
func (m *modelPrinter) PrintKindAndVersion() error {
|
||||||
|
if err := m.Writer.Write("KIND: %s", m.GVK.Kind); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return m.Writer.Write("VERSION: %s\n", m.GVK.GroupVersion())
|
||||||
|
}
|
||||||
|
|
||||||
// PrintDescription prints the description for a given schema. There
|
// PrintDescription prints the description for a given schema. There
|
||||||
// might be multiple description, since we collect descriptions when we
|
// might be multiple description, since we collect descriptions when we
|
||||||
// go through references, arrays and maps.
|
// go through references, arrays and maps.
|
||||||
@@ -73,6 +84,11 @@ func (m *modelPrinter) VisitArray(a *proto.Array) {
|
|||||||
|
|
||||||
// VisitKind prints a full resource with its fields.
|
// VisitKind prints a full resource with its fields.
|
||||||
func (m *modelPrinter) VisitKind(k *proto.Kind) {
|
func (m *modelPrinter) VisitKind(k *proto.Kind) {
|
||||||
|
if err := m.PrintKindAndVersion(); err != nil {
|
||||||
|
m.Error = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if m.Type == "" {
|
if m.Type == "" {
|
||||||
m.Type = GetTypeName(k)
|
m.Type = GetTypeName(k)
|
||||||
}
|
}
|
||||||
@@ -103,10 +119,15 @@ func (m *modelPrinter) VisitMap(om *proto.Map) {
|
|||||||
|
|
||||||
// VisitPrimitive prints a field type and its description.
|
// VisitPrimitive prints a field type and its description.
|
||||||
func (m *modelPrinter) VisitPrimitive(p *proto.Primitive) {
|
func (m *modelPrinter) VisitPrimitive(p *proto.Primitive) {
|
||||||
|
if err := m.PrintKindAndVersion(); err != nil {
|
||||||
|
m.Error = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if m.Type == "" {
|
if m.Type == "" {
|
||||||
m.Type = GetTypeName(p)
|
m.Type = GetTypeName(p)
|
||||||
}
|
}
|
||||||
if err := m.Writer.Write("FIELD: %s <%s>\n", m.Name, m.Type); err != nil {
|
if err := m.Writer.Write("FIELD: %s <%s>\n", m.Name, m.Type); err != nil {
|
||||||
m.Error = err
|
m.Error = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -120,8 +141,8 @@ func (m *modelPrinter) VisitReference(r proto.Reference) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrintModel prints the description of a schema in writer.
|
// PrintModel prints the description of a schema in writer.
|
||||||
func PrintModel(name string, writer *Formatter, builder fieldsPrinterBuilder, schema proto.Schema) error {
|
func PrintModel(name string, writer *Formatter, builder fieldsPrinterBuilder, schema proto.Schema, gvk schema.GroupVersionKind) error {
|
||||||
m := &modelPrinter{Name: name, Writer: writer, Builder: builder}
|
m := &modelPrinter{Name: name, Writer: writer, Builder: builder, GVK: gvk}
|
||||||
schema.Accept(m)
|
schema.Accept(m)
|
||||||
return m.Error
|
return m.Error
|
||||||
}
|
}
|
||||||
|
@@ -24,11 +24,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestModel(t *testing.T) {
|
func TestModel(t *testing.T) {
|
||||||
schema := resources.LookupResource(schema.GroupVersionKind{
|
gvk := schema.GroupVersionKind{
|
||||||
Group: "",
|
Group: "",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Kind: "OneKind",
|
Kind: "OneKind",
|
||||||
})
|
}
|
||||||
|
schema := resources.LookupResource(gvk)
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
t.Fatal("Couldn't find schema v1.OneKind")
|
t.Fatal("Couldn't find schema v1.OneKind")
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,10 @@ func TestModel(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
want: `DESCRIPTION:
|
want: `KIND: OneKind
|
||||||
|
VERSION: v1
|
||||||
|
|
||||||
|
DESCRIPTION:
|
||||||
OneKind has a short description
|
OneKind has a short description
|
||||||
|
|
||||||
FIELDS:
|
FIELDS:
|
||||||
@@ -58,7 +62,10 @@ FIELDS:
|
|||||||
path: []string{},
|
path: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: `RESOURCE: field1 <Object>
|
want: `KIND: OneKind
|
||||||
|
VERSION: v1
|
||||||
|
|
||||||
|
RESOURCE: field1 <Object>
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ut lacus ac
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ut lacus ac
|
||||||
@@ -90,7 +97,10 @@ FIELDS:
|
|||||||
path: []string{"field1"},
|
path: []string{"field1"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: `FIELD: string <string>
|
want: `KIND: OneKind
|
||||||
|
VERSION: v1
|
||||||
|
|
||||||
|
FIELD: string <string>
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
This string must be a string
|
This string must be a string
|
||||||
@@ -98,7 +108,10 @@ DESCRIPTION:
|
|||||||
path: []string{"field1", "string"},
|
path: []string{"field1", "string"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: `FIELD: array <[]integer>
|
want: `KIND: OneKind
|
||||||
|
VERSION: v1
|
||||||
|
|
||||||
|
FIELD: array <[]integer>
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
This array must be an array of int
|
This array must be an array of int
|
||||||
@@ -111,7 +124,7 @@ DESCRIPTION:
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
if err := PrintModelDescription(test.path, &buf, schema, false); err != nil {
|
if err := PrintModelDescription(test.path, &buf, schema, gvk, false); err != nil {
|
||||||
t.Fatalf("Failed to PrintModelDescription for path %v: %v", test.path, err)
|
t.Fatalf("Failed to PrintModelDescription for path %v: %v", test.path, err)
|
||||||
}
|
}
|
||||||
got := buf.String()
|
got := buf.String()
|
||||||
|
Reference in New Issue
Block a user