mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
add KUBECTL_EXPLAIN_OPENAPIV3
envar to gate new explain implementation
update
This commit is contained in:
parent
a9c093b165
commit
2f16cd4aea
@ -18,14 +18,17 @@ package explain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
"k8s.io/client-go/discovery"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"k8s.io/kubectl/pkg/explain"
|
"k8s.io/kubectl/pkg/explain"
|
||||||
|
explainv2 "k8s.io/kubectl/pkg/explain/v2"
|
||||||
"k8s.io/kubectl/pkg/util/i18n"
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
"k8s.io/kubectl/pkg/util/openapi"
|
"k8s.io/kubectl/pkg/util/openapi"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
@ -62,12 +65,25 @@ type ExplainOptions struct {
|
|||||||
|
|
||||||
Mapper meta.RESTMapper
|
Mapper meta.RESTMapper
|
||||||
Schema openapi.Resources
|
Schema openapi.Resources
|
||||||
|
|
||||||
|
// Toggles whether the OpenAPI v3 template-based renderer should be used to show
|
||||||
|
// output.
|
||||||
|
EnableOpenAPIV3 bool
|
||||||
|
|
||||||
|
// Name of the template to use with the openapiv3 template renderer. If
|
||||||
|
// `EnableOpenAPIV3` is disabled, this does nothing
|
||||||
|
OutputFormat string
|
||||||
|
|
||||||
|
// Client capable of fetching openapi documents from the user's cluster
|
||||||
|
DiscoveryClient discovery.DiscoveryInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExplainOptions(parent string, streams genericclioptions.IOStreams) *ExplainOptions {
|
func NewExplainOptions(parent string, streams genericclioptions.IOStreams) *ExplainOptions {
|
||||||
return &ExplainOptions{
|
return &ExplainOptions{
|
||||||
IOStreams: streams,
|
IOStreams: streams,
|
||||||
CmdParent: parent,
|
CmdParent: parent,
|
||||||
|
EnableOpenAPIV3: os.Getenv("KUBECTL_EXPLAIN_OPENAPIV3") == "true",
|
||||||
|
OutputFormat: "plaintext",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +105,12 @@ func NewCmdExplain(parent string, f cmdutil.Factory, streams genericclioptions.I
|
|||||||
}
|
}
|
||||||
cmd.Flags().BoolVar(&o.Recursive, "recursive", o.Recursive, "Print the fields of fields (Currently only 1 level deep)")
|
cmd.Flags().BoolVar(&o.Recursive, "recursive", o.Recursive, "Print the fields of fields (Currently only 1 level deep)")
|
||||||
cmd.Flags().StringVar(&o.APIVersion, "api-version", o.APIVersion, "Get different explanations for particular API version (API group/version)")
|
cmd.Flags().StringVar(&o.APIVersion, "api-version", o.APIVersion, "Get different explanations for particular API version (API group/version)")
|
||||||
|
|
||||||
|
// Only enable --output as a valid flag if the feature is enabled
|
||||||
|
if o.EnableOpenAPIV3 {
|
||||||
|
cmd.Flags().StringVar(&o.OutputFormat, "output", o.OutputFormat, "Format in which to render the schema")
|
||||||
|
}
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +126,15 @@ func (o *ExplainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only openapi v3 needs the discovery client.
|
||||||
|
if o.EnableOpenAPIV3 {
|
||||||
|
clientset, err := f.KubernetesClientSet()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.DiscoveryClient = clientset.DiscoveryClient
|
||||||
|
}
|
||||||
|
|
||||||
o.args = args
|
o.args = args
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -142,6 +173,17 @@ func (o *ExplainOptions) Run() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.EnableOpenAPIV3 {
|
||||||
|
return explainv2.PrintModelDescription(
|
||||||
|
fieldsPath,
|
||||||
|
o.Out,
|
||||||
|
o.DiscoveryClient.OpenAPIV3(),
|
||||||
|
fullySpecifiedGVR,
|
||||||
|
recursive,
|
||||||
|
o.OutputFormat,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
gvk, _ := o.Mapper.KindFor(fullySpecifiedGVR)
|
gvk, _ := o.Mapper.KindFor(fullySpecifiedGVR)
|
||||||
if gvk.Empty() {
|
if gvk.Empty() {
|
||||||
gvk, err = o.Mapper.KindFor(fullySpecifiedGVR.GroupResource().WithVersion(""))
|
gvk, err = o.Mapper.KindFor(fullySpecifiedGVR.GroupResource().WithVersion(""))
|
||||||
|
84
staging/src/k8s.io/kubectl/pkg/explain/v2/explain.go
Normal file
84
staging/src/k8s.io/kubectl/pkg/explain/v2/explain.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 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 v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/openapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrintModelDescription prints the description of a specific model or dot path.
|
||||||
|
// If recursive, all components nested within the fields of the schema will be
|
||||||
|
// printed.
|
||||||
|
func PrintModelDescription(
|
||||||
|
fieldsPath []string,
|
||||||
|
w io.Writer,
|
||||||
|
client openapi.Client,
|
||||||
|
gvr schema.GroupVersionResource,
|
||||||
|
recursive bool,
|
||||||
|
outputFormat string,
|
||||||
|
) error {
|
||||||
|
return printModelDescriptionWithGenerator(
|
||||||
|
NewGenerator(), fieldsPath, w, client, gvr, recursive, outputFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factored out for testability
|
||||||
|
func printModelDescriptionWithGenerator(
|
||||||
|
generator *generator,
|
||||||
|
fieldsPath []string,
|
||||||
|
w io.Writer,
|
||||||
|
client openapi.Client,
|
||||||
|
gvr schema.GroupVersionResource,
|
||||||
|
recursive bool,
|
||||||
|
outputFormat string,
|
||||||
|
) error {
|
||||||
|
paths, err := client.Paths()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch list of groupVersions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourcePath string
|
||||||
|
if len(gvr.Group) == 0 {
|
||||||
|
resourcePath = fmt.Sprintf("api/%s", gvr.Version)
|
||||||
|
} else {
|
||||||
|
resourcePath = fmt.Sprintf("apis/%s/%s", gvr.Group, gvr.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
gv, exists := paths[resourcePath]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("could not locate schema for %s", resourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
openAPISchemaBytes, err := gv.Schema(runtime.ContentTypeJSON)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch openapi schema for %s: %w", resourcePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedV3Schema map[string]interface{}
|
||||||
|
if err := json.Unmarshal(openAPISchemaBytes, &parsedV3Schema); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse openapi schema for %s: %w", resourcePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generator.Render(outputFormat, parsedV3Schema, gvr, fieldsPath, recursive, w)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user