diff --git a/commands/commands.go b/commands/commands.go index 97589fde91..301ec7a8f9 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -19,7 +19,6 @@ import ( "strings" "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/cmd/util" ) // NormalizeCommand will modify commands to be consistent, e.g. silencing errors @@ -31,11 +30,11 @@ func NormalizeCommand(c ...*cobra.Command) { } // GetKptCommands returns the set of kpt commands to be registered -func GetKptCommands(ctx context.Context, name string, f util.Factory) []*cobra.Command { +func GetKptCommands(ctx context.Context, name, version string) []*cobra.Command { var c []*cobra.Command fnCmd := GetFnCommand(ctx, name) pkgCmd := GetPkgCommand(ctx, name) - liveCmd := GetLiveCommand(name, f) + liveCmd := GetLiveCommand(name, version) c = append(c, pkgCmd, fnCmd, liveCmd) diff --git a/commands/livecmd.go b/commands/livecmd.go index 4e67cb5e38..712bb19998 100644 --- a/commands/livecmd.go +++ b/commands/livecmd.go @@ -15,11 +15,13 @@ package commands import ( + "flag" + "fmt" "os" - "github.com/GoogleContainerTools/kpt/internal/cmdfetchk8sschema" "github.com/GoogleContainerTools/kpt/internal/cmdliveinit" "github.com/GoogleContainerTools/kpt/internal/docs/generated/livedocs" + "github.com/GoogleContainerTools/kpt/internal/util/cfgflags" "github.com/GoogleContainerTools/kpt/pkg/live" "github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/destroy" "github.com/GoogleContainerTools/kpt/thirdparty/cli-utils/diff" @@ -28,12 +30,13 @@ import ( "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/klog" - "k8s.io/kubectl/pkg/cmd/util" + cluster "k8s.io/kubectl/pkg/cmd/util" "sigs.k8s.io/cli-utils/pkg/manifestreader" "sigs.k8s.io/cli-utils/pkg/provider" + "sigs.k8s.io/cli-utils/pkg/util/factory" ) -func GetLiveCommand(name string, f util.Factory) *cobra.Command { +func GetLiveCommand(_, version string) *cobra.Command { liveCmd := &cobra.Command{ Use: "live", Short: livedocs.LiveShort, @@ -56,6 +59,8 @@ func GetLiveCommand(name string, f util.Factory) *cobra.Command { ErrOut: os.Stderr, } + f := newFactory(liveCmd, version) + // The provider handles both ConfigMap and ResourceGroup inventory objects. // If a package has both inventory objects, then an error is thrown. klog.V(2).Infoln("provider supports ResourceGroup and ConfigMap inventory") @@ -95,10 +100,8 @@ func GetLiveCommand(name string, f util.Factory) *cobra.Command { statusCmd.Long = livedocs.StatusLong statusCmd.Example = livedocs.StatusExamples - fetchOpenAPICmd := cmdfetchk8sschema.NewCommand(name, f, ioStreams) - liveCmd.AddCommand(initCmd, applyCmd, previewCmd, diffCmd, destroyCmd, - fetchOpenAPICmd, statusCmd) + statusCmd) // Add the migrate command to change from ConfigMap to ResourceGroup inventory // object. Also add the install-resource-group command. @@ -113,3 +116,21 @@ func GetLiveCommand(name string, f util.Factory) *cobra.Command { return liveCmd } + +func newFactory(cmd *cobra.Command, version string) cluster.Factory { + flags := cmd.PersistentFlags() + kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() + kubeConfigFlags.AddFlags(flags) + userAgentKubeConfigFlags := &cfgflags.UserAgentKubeConfigFlags{ + Delegate: kubeConfigFlags, + UserAgent: fmt.Sprintf("kpt/%s", version), + } + matchVersionKubeConfigFlags := cluster.NewMatchVersionFlags( + &factory.CachingRESTClientGetter{ + Delegate: userAgentKubeConfigFlags, + }, + ) + matchVersionKubeConfigFlags.AddFlags(cmd.PersistentFlags()) + cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) + return cluster.NewFactory(matchVersionKubeConfigFlags) +} diff --git a/internal/cmdfetchk8sschema/cmdfetchk8sschema.go b/internal/cmdfetchk8sschema/cmdfetchk8sschema.go deleted file mode 100644 index be499a3a26..0000000000 --- a/internal/cmdfetchk8sschema/cmdfetchk8sschema.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 cmdfetchk8sschema - -import ( - "encoding/json" - "fmt" - - "github.com/GoogleContainerTools/kpt/internal/docs/generated/livedocs" - "github.com/GoogleContainerTools/kpt/internal/util/cmdutil" - "github.com/GoogleContainerTools/kpt/internal/util/openapi" - "github.com/spf13/cobra" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/cmd/util" -) - -func NewRunner(parent string, f util.Factory, - ioStreams genericclioptions.IOStreams) *Runner { - r := &Runner{ - IOStreams: ioStreams, - Factory: f, - } - // TODO: Update description with info from the site. - c := &cobra.Command{ - Use: "fetch-k8s-schema", - Short: livedocs.FetchK8sSchemaShort, - Long: livedocs.FetchK8sSchemaShort + "\n" + livedocs.FetchK8sSchemaLong, - Example: livedocs.FetchK8sSchemaExamples, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - // Don't run the PreRun functions that read k8s-schema since - // we are doing that in the command itself. - return nil - }, - RunE: r.runE, - } - cmdutil.FixDocs("kpt", parent, c) - r.Command = c - - c.Flags().BoolVar(&r.PrettyPrint, "pretty-print", false, - `Format the schema before printing`) - - return r -} - -func NewCommand(parent string, f util.Factory, - ioStreams genericclioptions.IOStreams) *cobra.Command { - return NewRunner(parent, f, ioStreams).Command -} - -// Runner contains the run function -type Runner struct { - Command *cobra.Command - IOStreams genericclioptions.IOStreams - Factory util.Factory - - PrettyPrint bool -} - -func (r *Runner) runE(c *cobra.Command, args []string) error { - schema, err := openapi.FetchOpenAPISchemaFromCluster(r.Factory) - if err != nil { - return err - } - - if r.PrettyPrint { - var data map[string]interface{} - err = json.Unmarshal(schema, &data) - if err != nil { - return err - } - - schema, err = json.MarshalIndent(data, "", " ") - if err != nil { - return err - } - } - - fmt.Fprintf(r.IOStreams.Out, "%s\n", string(schema)) - return nil -} diff --git a/internal/util/cmdutil/cmdutil.go b/internal/util/cmdutil/cmdutil.go index 01a56d6c1b..721eb2be7c 100644 --- a/internal/util/cmdutil/cmdutil.go +++ b/internal/util/cmdutil/cmdutil.go @@ -50,14 +50,6 @@ func PrintErrorStacktrace() bool { // StackOnError if true, will print a stack trace on failure. var StackOnError bool -// K8sSchemaSource defines where we should look for the kubernetes openAPI -// schema -var K8sSchemaSource string - -// K8sSchemaPath defines the path to the openAPI schema if we are reading from -// a file -var K8sSchemaPath string - func ResolveAbsAndRelPaths(path string) (string, string, error) { cwd, err := os.Getwd() if err != nil { diff --git a/internal/util/openapi/openapi.go b/internal/util/openapi/openapi.go deleted file mode 100644 index 75c6fb5916..0000000000 --- a/internal/util/openapi/openapi.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 openapi - -import ( - "context" - "fmt" - "io/ioutil" - - "k8s.io/kubectl/pkg/cmd/util" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi" -) - -const ( - SchemaSourceBuiltin = "builtin" - SchemaSourceFile = "file" - SchemaSourceCluster = "cluster" -) - -var SchemaSources = fmt.Sprintf("{%q, %q, %q}", SchemaSourceBuiltin, SchemaSourceCluster, SchemaSourceFile) - -// ConfigureOpenAPI sets the openAPI schema in kyaml. It can either -// fetch the schema from a cluster, read it from file, or just the -// schema built into kyaml. -func ConfigureOpenAPI(factory util.Factory, k8sSchemaSource, k8sSchemaPath string) error { - switch k8sSchemaSource { - case SchemaSourceCluster: - openAPISchema, err := FetchOpenAPISchemaFromCluster(factory) - if err != nil { - return fmt.Errorf("error fetching schema from cluster: %v", err) - } - return ConfigureOpenAPISchema(openAPISchema) - case SchemaSourceFile: - openAPISchema, err := ReadOpenAPISchemaFromDisk(k8sSchemaPath) - if err != nil { - return fmt.Errorf("error reading file at path %s: %v", - k8sSchemaPath, err) - } - return ConfigureOpenAPISchema(openAPISchema) - case SchemaSourceBuiltin: - return nil - default: - return fmt.Errorf("unknown schema source %s. Must be one of %s", - k8sSchemaSource, SchemaSources) - } -} - -func FetchOpenAPISchemaFromCluster(f util.Factory) ([]byte, error) { - restClient, err := f.RESTClient() - if err != nil { - return nil, err - } - data, err := restClient.Get().AbsPath("/openapi/v2"). - SetHeader("Accept", "application/json").Do(context.Background()).Raw() - if err != nil { - return nil, err - } - return data, nil -} - -func ReadOpenAPISchemaFromDisk(path string) ([]byte, error) { - return ioutil.ReadFile(path) -} - -func ConfigureOpenAPISchema(openAPISchema []byte) error { - openapi.SuppressBuiltInSchemaUse() - - err := openapi.AddSchema(openAPISchema) - if err != nil { - return err - } - // Also add make sure the Kustomize openAPI is always added regardless - // of where we got the Kubernetes openAPI schema. - // TODO: Refactor the openapi package in kyaml so we don't need to - // know the name of the kustomize asset here. - return openapi.AddSchema(kustomizationapi.MustAsset("kustomizationapi/swagger.json")) -} diff --git a/internal/util/openapi/openapi_test.go b/internal/util/openapi/openapi_test.go deleted file mode 100644 index b6866fe4a7..0000000000 --- a/internal/util/openapi/openapi_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2020 Google LLC -// -// 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 openapi - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" - "testing" - - "github.com/go-openapi/spec" - "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/rest/fake" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - "sigs.k8s.io/kustomize/kyaml/openapi" -) - -func TestSomething(t *testing.T) { - testCases := []struct { - name string - schemaSource string - schemaPath string - response *http.Response - includesRefString string - notIncludesRefString string - expectError bool - }{ - { - name: "no schemaSource provided should lead to error", - schemaSource: "", - includesRefString: "#/definitions/io.k8s.api.core.v1.PodSpec", - expectError: true, - }, - { - name: "schemaSource cluster with successful fetch", - schemaSource: "cluster", - response: &http.Response{StatusCode: http.StatusOK, - Header: cmdtesting.DefaultHeader(), Body: getSchema(t, "clusterschema.json")}, - includesRefString: "#/definitions/io.k8s.clusterSchema", - notIncludesRefString: "#/definitions/io.k8s.api.core.v1.PodSpec", - expectError: false, - }, - { - name: "schemaSource cluster with failed fetch", - schemaSource: "cluster", - response: &http.Response{StatusCode: http.StatusNotFound, - Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, - expectError: true, - }, - { - name: "schemaSource file with valid path", - schemaSource: "file", - schemaPath: "testdata/fileschema.json", - includesRefString: "#/definitions/io.k8s.fileSchema", - notIncludesRefString: "#/definitions/io.k8s.api.core.v1.PodSpec", - expectError: false, - }, - { - name: "schemaSource file with invalid path", - schemaSource: "file", - schemaPath: "testdata/notfound.json", - expectError: true, - }, - { - name: "schemaSource builtin", - schemaSource: "builtin", - includesRefString: "#/definitions/io.k8s.api.core.v1.PodSpec", - expectError: false, - }, - { - name: "unknown schemasource", - schemaSource: "unknown", - expectError: true, - }, - } - - for i := range testCases { - test := testCases[i] - t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory().WithNamespace("test-namespace") - defer tf.Cleanup() - - tf.ClientConfigVal.GroupVersion = &schema.GroupVersion{ - Group: "", - Version: "v1", - } - tf.ClientConfigVal.NegotiatedSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer - - tf.Client = &fake.RESTClient{ - NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if req.Method == http.MethodGet && req.URL.Path == "/openapi/v2" { - return test.response, nil - } - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - }), - } - - openapi.ResetOpenAPI() - - err := ConfigureOpenAPI(tf, test.schemaSource, test.schemaPath) - if test.expectError { - assert.Error(t, err) - return - } - assert.NoError(t, err) - - ref, err := spec.NewRef(test.includesRefString) - assert.NoError(t, err) - res, err := openapi.Resolve(&ref, openapi.Schema()) - assert.NoError(t, err) - assert.NotNil(t, res) - - // If the notIncludesRefString is specified, make sure - // the schema does not include the reference. - if test.notIncludesRefString != "" { - ref2, err := spec.NewRef(test.notIncludesRefString) - assert.NoError(t, err) - res2, _ := openapi.Resolve(&ref2, openapi.Schema()) - assert.Nil(t, res2) - } - - // Verify that we have the Kustomize openAPI included. - kustomizeRef, _ := spec.NewRef("#/definitions/io.k8s.api.apps.v1.Kustomization") - kustomizeRes, err := openapi.Resolve(&kustomizeRef, openapi.Schema()) - assert.NoError(t, err) - assert.NotNil(t, kustomizeRes) - }) - } -} - -func getSchema(t *testing.T, filename string) io.ReadCloser { - b, err := ioutil.ReadFile("testdata/" + filename) - if err != nil { - t.Fatal(err) - } - return ioutil.NopCloser(bytes.NewBuffer(b)) -} diff --git a/internal/util/openapi/testdata/clusterschema.json b/internal/util/openapi/testdata/clusterschema.json deleted file mode 100644 index 5fef6f181d..0000000000 --- a/internal/util/openapi/testdata/clusterschema.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "definitions": { - "io.k8s.clusterSchema": { - "additionalProperties": false, - "type": "object" - } - } -} \ No newline at end of file diff --git a/internal/util/openapi/testdata/fileschema.json b/internal/util/openapi/testdata/fileschema.json deleted file mode 100644 index 6bb85b494d..0000000000 --- a/internal/util/openapi/testdata/fileschema.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "definitions": { - "io.k8s.fileSchema": { - "additionalProperties": false, - "type": "object" - } - } -} \ No newline at end of file diff --git a/run/run.go b/run/run.go index 17e60c3c73..1e544405b8 100644 --- a/run/run.go +++ b/run/run.go @@ -17,7 +17,6 @@ package run import ( "bytes" "context" - "flag" "fmt" "os" "os/exec" @@ -28,13 +27,8 @@ import ( "github.com/GoogleContainerTools/kpt/internal/cmdcomplete" "github.com/GoogleContainerTools/kpt/internal/docs/generated/overview" "github.com/GoogleContainerTools/kpt/internal/printer" - "github.com/GoogleContainerTools/kpt/internal/util/cfgflags" "github.com/GoogleContainerTools/kpt/internal/util/cmdutil" - kptopenapi "github.com/GoogleContainerTools/kpt/internal/util/openapi" "github.com/spf13/cobra" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/kubectl/pkg/cmd/util" - "sigs.k8s.io/cli-utils/pkg/util/factory" "sigs.k8s.io/kustomize/kyaml/commandutil" ) @@ -84,17 +78,6 @@ func GetMain(ctx context.Context) *cobra.Command { // create context with associated printer ctx = printer.WithContext(ctx, pr) - f := newFactory(cmd) - - cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - err := kptopenapi.ConfigureOpenAPI(f, cmdutil.K8sSchemaSource, cmdutil.K8sSchemaPath) - if err != nil { - return err - } - - return nil - } - cmd.Flags().BoolVar(&installComp, "install-completion", false, "Install shell completion") // this command will be invoked by the shell-completion code @@ -134,17 +117,12 @@ func GetMain(ctx context.Context) *cobra.Command { // help and documentation cmd.InitDefaultHelpCmd() - cmd.AddCommand(kptcommands.GetKptCommands(ctx, "kpt", f)...) + cmd.AddCommand(kptcommands.GetKptCommands(ctx, "kpt", version)...) // enable stack traces cmd.PersistentFlags().BoolVar(&cmdutil.StackOnError, "stack-trace", false, "Print a stack-trace on failure") - cmd.PersistentFlags().StringVar(&cmdutil.K8sSchemaSource, "k8s-schema-source", - kptopenapi.SchemaSourceBuiltin, fmt.Sprintf("Source for the kubernetes openAPI schema. Must be one of %s.", kptopenapi.SchemaSources)) - cmd.PersistentFlags().StringVar(&cmdutil.K8sSchemaPath, "k8s-schema-path", - "./openapi.json", "Path to the kubernetes openAPI schema file") - if _, err := exec.LookPath("git"); err != nil { fmt.Fprintf(os.Stderr, "kpt requires that `git` is installed and on the PATH") os.Exit(1) @@ -201,24 +179,6 @@ func newHelp(e []string, c *cobra.Command) func(command *cobra.Command, strings } } -func newFactory(cmd *cobra.Command) util.Factory { - flags := cmd.PersistentFlags() - kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() - kubeConfigFlags.AddFlags(flags) - userAgentKubeConfigFlags := &cfgflags.UserAgentKubeConfigFlags{ - Delegate: kubeConfigFlags, - UserAgent: fmt.Sprintf("kpt/%s", version), - } - matchVersionKubeConfigFlags := util.NewMatchVersionFlags( - &factory.CachingRESTClientGetter{ - Delegate: userAgentKubeConfigFlags, - }, - ) - matchVersionKubeConfigFlags.AddFlags(cmd.PersistentFlags()) - cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) - return util.NewFactory(matchVersionKubeConfigFlags) -} - var version = "unknown" var versionCmd = &cobra.Command{