kubectl explain should work for both cluster and namespace resources and without a GET method
Kubernetes-commit: 7f4c187ab26fc8b96a091068ee9c84fa447b9291
This commit is contained in:
parent
156d5a9a97
commit
138a1cc87a
|
@ -185,6 +185,9 @@ func WithBuiltinTemplateFuncs(tmpl *template.Template) *template.Template {
|
|||
|
||||
return copyDict, nil
|
||||
},
|
||||
"list": func(values ...any) ([]any, error) {
|
||||
return values, nil
|
||||
},
|
||||
"add": func(value, operand int) int {
|
||||
return value + operand
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,13 +2,33 @@
|
|||
{{- $prefix := (ternary "/api" (join "" "/apis/" $.GVR.Group) (not $.GVR.Group)) -}}
|
||||
|
||||
{{- /* Search both cluster-scoped and namespaced-scoped paths for the GVR to find its GVK */ -}}
|
||||
{{- /* Looks for path /apis/<group>/<version>/<resource> or /apis/<group>/<version>/<version>/namespaces/{namespace}/<resource> */ -}}
|
||||
{{- /* Also search for paths with {name} component in case the list path is missing */ -}}
|
||||
{{- /* Looks for the following paths: */ -}}
|
||||
{{- /* /apis/<group>/<version>/<resource> */ -}}
|
||||
{{- /* /apis/<group>/<version>/<resource>/{name} */ -}}
|
||||
{{- /* /apis/<group>/<version>/namespaces/{namespace}/<resource> */ -}}
|
||||
{{- /* /apis/<group>/<version>/namespaces/{namespace}/<resource>/{name} */ -}}
|
||||
{{- /* Also search for get verb paths in case list verb is missing */ -}}
|
||||
{{- $clusterScopedSearchPath := join "/" $prefix $.GVR.Version $.GVR.Resource -}}
|
||||
{{- $namespaceScopedSearchPath := join "/" $prefix $.GVR.Version "namespaces" "\\{namespace\\}" $.GVR.Resource -}}
|
||||
{{- $path := or (index $.Document "paths" $clusterScopedSearchPath) (index $.Document "paths" $clusterScopedSearchPath) -}}
|
||||
{{- $clusterScopedNameSearchPath := join "/" $prefix $.GVR.Version $.GVR.Resource "{name}" -}}
|
||||
{{- $namespaceScopedSearchPath := join "/" $prefix $.GVR.Version "namespaces" "{namespace}" $.GVR.Resource -}}
|
||||
{{- $namespaceScopedNameSearchPath := join "/" $prefix $.GVR.Version "namespaces" "{namespace}" $.GVR.Resource "{name}" -}}
|
||||
{{- $gvk := "" -}}
|
||||
|
||||
{{- /* Pull GVK from operation */ -}}
|
||||
{{- with $gvk := and $path (index $path "get" "x-kubernetes-group-version-kind") -}}
|
||||
{{- range $index, $searchPath := (list $clusterScopedSearchPath $clusterScopedNameSearchPath $namespaceScopedSearchPath $namespaceScopedNameSearchPath) -}}
|
||||
{{- with $resourcePathElement := index $.Document "paths" $searchPath -}}
|
||||
{{- range $methodIndex, $method := (list "get" "post" "put" "patch" "delete") -}}
|
||||
{{- with $resourceMethodPathElement := index $resourcePathElement $method -}}
|
||||
{{- with $gvk = index $resourceMethodPathElement "x-kubernetes-group-version-kind" -}}
|
||||
{{- break -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- with $gvk -}}
|
||||
{{- if $gvk.group -}}
|
||||
GROUP: {{ $gvk.group }}{{"\n" -}}
|
||||
{{- end -}}
|
||||
|
|
|
@ -41,17 +41,46 @@ var (
|
|||
//go:embed apiextensions.k8s.io_v1.json
|
||||
apiextensionsJSON string
|
||||
|
||||
//go:embed batch.k8s.io_v1.json
|
||||
batchJSON string
|
||||
|
||||
apiExtensionsV1OpenAPI map[string]interface{} = func() map[string]interface{} {
|
||||
var res map[string]interface{}
|
||||
utilruntime.Must(json.Unmarshal([]byte(apiextensionsJSON), &res))
|
||||
return res
|
||||
}()
|
||||
|
||||
apiExtensionsV1OpenAPIWithoutListVerb map[string]interface{} = func() map[string]interface{} {
|
||||
var res map[string]interface{}
|
||||
utilruntime.Must(json.Unmarshal([]byte(apiextensionsJSON), &res))
|
||||
paths := res["paths"].(map[string]interface{})
|
||||
delete(paths, "/apis/apiextensions.k8s.io/v1/customresourcedefinitions")
|
||||
return res
|
||||
}()
|
||||
|
||||
apiExtensionsV1OpenAPISpec spec3.OpenAPI = func() spec3.OpenAPI {
|
||||
var res spec3.OpenAPI
|
||||
utilruntime.Must(json.Unmarshal([]byte(apiextensionsJSON), &res))
|
||||
return res
|
||||
}()
|
||||
|
||||
batchV1OpenAPI map[string]interface{} = func() map[string]interface{} {
|
||||
var res map[string]interface{}
|
||||
utilruntime.Must(json.Unmarshal([]byte(batchJSON), &res))
|
||||
return res
|
||||
}()
|
||||
|
||||
batchV1OpenAPIWithoutListVerb map[string]interface{} = func() map[string]interface{} {
|
||||
var res map[string]interface{}
|
||||
utilruntime.Must(json.Unmarshal([]byte(batchJSON), &res))
|
||||
paths := res["paths"].(map[string]interface{})
|
||||
delete(paths, "/apis/batch/v1/jobs")
|
||||
delete(paths, "/apis/batch/v1/namespaces/{namespace}/jobs")
|
||||
|
||||
delete(paths, "/apis/batch/v1/cronjobs")
|
||||
delete(paths, "/apis/batch/v1/namespaces/{namespace}/cronjobs/{name}")
|
||||
return res
|
||||
}()
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
|
@ -143,6 +172,74 @@ func TestPlaintext(t *testing.T) {
|
|||
checkContains("CustomResourceDefinition represents a resource that should be exposed"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test basic ability to find a namespaced GVR and print its description
|
||||
Name: "SchemaFoundNamespaced",
|
||||
Context: v2.TemplateContext{
|
||||
Document: batchV1OpenAPI,
|
||||
GVR: schema.GroupVersionResource{
|
||||
Group: "batch",
|
||||
Version: "v1",
|
||||
Resource: "jobs",
|
||||
},
|
||||
FieldPath: nil,
|
||||
Recursive: false,
|
||||
},
|
||||
Checks: []check{
|
||||
checkContains("Job represents the configuration of a single job"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test basic ability to find a GVR without a list verb and print its description
|
||||
Name: "SchemaFoundWithoutListVerb",
|
||||
Context: v2.TemplateContext{
|
||||
Document: apiExtensionsV1OpenAPIWithoutListVerb,
|
||||
GVR: schema.GroupVersionResource{
|
||||
Group: "apiextensions.k8s.io",
|
||||
Version: "v1",
|
||||
Resource: "customresourcedefinitions",
|
||||
},
|
||||
FieldPath: nil,
|
||||
Recursive: false,
|
||||
},
|
||||
Checks: []check{
|
||||
checkContains("CustomResourceDefinition represents a resource that should be exposed"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test basic ability to find a namespaced GVR without a list verb and print its description
|
||||
Name: "SchemaFoundNamespacedWithoutListVerb",
|
||||
Context: v2.TemplateContext{
|
||||
Document: batchV1OpenAPIWithoutListVerb,
|
||||
GVR: schema.GroupVersionResource{
|
||||
Group: "batch",
|
||||
Version: "v1",
|
||||
Resource: "jobs",
|
||||
},
|
||||
FieldPath: nil,
|
||||
Recursive: false,
|
||||
},
|
||||
Checks: []check{
|
||||
checkContains("Job represents the configuration of a single job"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test basic ability to find a namespaced GVR without a top level list verb and print its description
|
||||
Name: "SchemaFoundNamespacedWithoutTopLevelListVerb",
|
||||
Context: v2.TemplateContext{
|
||||
Document: batchV1OpenAPIWithoutListVerb,
|
||||
GVR: schema.GroupVersionResource{
|
||||
Group: "batch",
|
||||
Version: "v1",
|
||||
Resource: "cronjobs",
|
||||
},
|
||||
FieldPath: nil,
|
||||
Recursive: false,
|
||||
},
|
||||
Checks: []check{
|
||||
checkContains("CronJob represents the configuration of a single cron job"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test that shows trying to find a non-existent field path of an existing
|
||||
// schema
|
||||
|
|
Loading…
Reference in New Issue