diff --git a/pkg/cmd/apply/apply.go b/pkg/cmd/apply/apply.go index 103eb244..5a81e0a5 100644 --- a/pkg/cmd/apply/apply.go +++ b/pkg/cmd/apply/apply.go @@ -27,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -207,10 +206,20 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions // Complete verifies if ApplyOptions are valid and without conflicts. func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { + var err error o.ServerSideApply = cmdutil.GetServerSideApplyFlag(cmd) o.ForceConflicts = cmdutil.GetForceConflictsFlag(cmd) o.FieldManager = cmdutil.GetFieldManagerFlag(cmd) o.DryRun = cmdutil.GetDryRunFlag(cmd) + o.DynamicClient, err = f.DynamicClient() + if err != nil { + return err + } + + o.DiscoveryClient, err = f.ToDiscoveryClient() + if err != nil { + return err + } if o.ForceConflicts && !o.ServerSideApply { return fmt.Errorf("--force-conflicts only works with --server-side") @@ -236,23 +245,13 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return o.PrintFlags.ToPrinter() } - var err error o.RecordFlags.Complete(cmd) o.Recorder, err = o.RecordFlags.ToRecorder() if err != nil { return err } - o.DiscoveryClient, err = f.ToDiscoveryClient() - if err != nil { - return err - } - - dynamicClient, err := f.DynamicClient() - if err != nil { - return err - } - o.DeleteOptions = o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams) + o.DeleteOptions = o.DeleteFlags.ToOptions(o.DynamicClient, o.IOStreams) err = o.DeleteOptions.FilenameOptions.RequireFilenameOrKustomize() if err != nil { return err @@ -269,11 +268,6 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - o.DynamicClient, err = f.DynamicClient() - if err != nil { - return err - } - o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err @@ -360,11 +354,6 @@ func (o *ApplyOptions) SetObjects(infos []*resource.Info) { // Run executes the `apply` command. func (o *ApplyOptions) Run() error { - dryRunVerifier := &DryRunVerifier{ - Finder: cmdutil.NewCRDFinder(cmdutil.CRDFromDynamic(o.DynamicClient)), - OpenAPIGetter: o.DiscoveryClient, - } - if o.PreProcessorFn != nil { klog.V(4).Infof("Running apply pre-processor function") if err := o.PreProcessorFn(); err != nil { @@ -388,13 +377,6 @@ func (o *ApplyOptions) Run() error { } for _, info := range infos { - // If server-dry-run is requested but the type doesn't support it, fail right away. - if o.ServerDryRun { - if err := dryRunVerifier.HasSupport(info.Mapping.GroupVersionKind); err != nil { - return err - } - } - o.MarkNamespaceVisited(info) if err := o.Recorder.Record(info.Object); err != nil { @@ -412,11 +394,15 @@ func (o *ApplyOptions) Run() error { Force: &o.ForceConflicts, FieldManager: o.FieldManager, } - if o.ServerDryRun { - options.DryRun = []string{metav1.DryRunAll} - } - obj, err := resource.NewHelper(info.Client, info.Mapping).Patch( + helper := resource.NewHelper(info.Client, info.Mapping) + if o.ServerDryRun { + if err := resource.VerifyDryRun(info.Mapping.GroupVersionKind, o.DynamicClient, o.DiscoveryClient); err != nil { + return err + } + helper.DryRun(o.ServerDryRun) + } + obj, err := helper.Patch( info.Namespace, info.Name, types.ApplyPatchType, @@ -486,11 +472,14 @@ See http://k8s.io/docs/reference/using-api/api-concepts/#conflicts`, err) if !o.DryRun { // Then create the resource and skip the three-way merge - options := metav1.CreateOptions{} + helper := resource.NewHelper(info.Client, info.Mapping) if o.ServerDryRun { - options.DryRun = []string{metav1.DryRunAll} + if err := resource.VerifyDryRun(info.Mapping.GroupVersionKind, o.DynamicClient, o.DiscoveryClient); err != nil { + return cmdutil.AddSourceToErr("creating", info.Source, err) + } + helper.DryRun(o.ServerDryRun) } - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, &options) + obj, err := helper.Create(info.Namespace, true, info.Object) if err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } @@ -526,7 +515,10 @@ See http://k8s.io/docs/reference/using-api/api-concepts/#conflicts`, err) fmt.Fprintf(o.ErrOut, warningNoLastAppliedConfigAnnotation, o.cmdBaseName) } - patcher := newPatcher(o, info) + patcher, err := newPatcher(o, info) + if err != nil { + return err + } patchBytes, patchedObject, err := patcher.Patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err) @@ -663,42 +655,3 @@ func (o *ApplyOptions) PrintAndPrunePostProcessor() func() error { return nil } } - -// DryRunVerifier verifies if a given group-version-kind supports DryRun -// against the current server. Sending dryRun requests to apiserver that -// don't support it will result in objects being unwillingly persisted. -// -// It reads the OpenAPI to see if the given GVK supports dryRun. If the -// GVK can not be found, we assume that CRDs will have the same level of -// support as "namespaces", and non-CRDs will not be supported. We -// delay the check for CRDs as much as possible though, since it -// requires an extra round-trip to the server. -type DryRunVerifier struct { - Finder cmdutil.CRDFinder - OpenAPIGetter discovery.OpenAPISchemaInterface -} - -// HasSupport verifies if the given gvk supports DryRun. An error is -// returned if it doesn't. -func (v *DryRunVerifier) HasSupport(gvk schema.GroupVersionKind) error { - oapi, err := v.OpenAPIGetter.OpenAPISchema() - if err != nil { - return fmt.Errorf("failed to download openapi: %v", err) - } - supports, err := openapi.SupportsDryRun(oapi, gvk) - if err != nil { - // We assume that we couldn't find the type, then check for namespace: - supports, _ = openapi.SupportsDryRun(oapi, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}) - // If namespace supports dryRun, then we will support dryRun for CRDs only. - if supports { - supports, err = v.Finder.HasCRD(gvk.GroupKind()) - if err != nil { - return fmt.Errorf("failed to check CRD: %v", err) - } - } - } - if !supports { - return fmt.Errorf("%v doesn't support dry-run", gvk) - } - return nil -} diff --git a/pkg/cmd/apply/apply_test.go b/pkg/cmd/apply/apply_test.go index 9e814fb9..15c34fa7 100644 --- a/pkg/cmd/apply/apply_test.go +++ b/pkg/cmd/apply/apply_test.go @@ -29,7 +29,6 @@ import ( "strings" "testing" - "github.com/googleapis/gnostic/OpenAPIv2" "github.com/spf13/cobra" appsv1 "k8s.io/api/apps/v1" @@ -1389,75 +1388,3 @@ func TestForceApply(t *testing.T) { }) } } - -func TestDryRunVerifier(t *testing.T) { - dryRunVerifier := DryRunVerifier{ - Finder: cmdutil.NewCRDFinder(func() ([]schema.GroupKind, error) { - return []schema.GroupKind{ - { - Group: "crd.com", - Kind: "MyCRD", - }, - { - Group: "crd.com", - Kind: "MyNewCRD", - }, - }, nil - }), - OpenAPIGetter: &fakeSchema, - } - - err := dryRunVerifier.HasSupport(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "NodeProxyOptions"}) - if err == nil { - t.Fatalf("NodeProxyOptions doesn't support dry-run, yet no error found") - } - - err = dryRunVerifier.HasSupport(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}) - if err != nil { - t.Fatalf("Pod should support dry-run: %v", err) - } - - err = dryRunVerifier.HasSupport(schema.GroupVersionKind{Group: "crd.com", Version: "v1", Kind: "MyCRD"}) - if err != nil { - t.Fatalf("MyCRD should support dry-run: %v", err) - } - - err = dryRunVerifier.HasSupport(schema.GroupVersionKind{Group: "crd.com", Version: "v1", Kind: "Random"}) - if err == nil { - t.Fatalf("Random doesn't support dry-run, yet no error found") - } -} - -type EmptyOpenAPI struct{} - -func (EmptyOpenAPI) OpenAPISchema() (*openapi_v2.Document, error) { - return &openapi_v2.Document{}, nil -} - -func TestDryRunVerifierNoOpenAPI(t *testing.T) { - dryRunVerifier := DryRunVerifier{ - Finder: cmdutil.NewCRDFinder(func() ([]schema.GroupKind, error) { - return []schema.GroupKind{ - { - Group: "crd.com", - Kind: "MyCRD", - }, - { - Group: "crd.com", - Kind: "MyNewCRD", - }, - }, nil - }), - OpenAPIGetter: EmptyOpenAPI{}, - } - - err := dryRunVerifier.HasSupport(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}) - if err == nil { - t.Fatalf("Pod doesn't support dry-run, yet no error found") - } - - err = dryRunVerifier.HasSupport(schema.GroupVersionKind{Group: "crd.com", Version: "v1", Kind: "MyCRD"}) - if err == nil { - t.Fatalf("MyCRD doesn't support dry-run, yet no error found") - } -} diff --git a/pkg/cmd/apply/patcher.go b/pkg/cmd/apply/patcher.go index d606e1de..c2d44d06 100644 --- a/pkg/cmd/apply/patcher.go +++ b/pkg/cmd/apply/patcher.go @@ -25,7 +25,6 @@ import ( "github.com/jonboulle/clockwork" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -75,13 +74,19 @@ type Patcher struct { OpenapiSchema openapi.Resources } -func newPatcher(o *ApplyOptions, info *resource.Info) *Patcher { +func newPatcher(o *ApplyOptions, info *resource.Info) (*Patcher, error) { var openapiSchema openapi.Resources if o.OpenAPIPatch { openapiSchema = o.OpenAPISchema } helper := resource.NewHelper(info.Client, info.Mapping) + if o.ServerDryRun { + if err := resource.VerifyDryRun(info.Mapping.GroupVersionKind, o.DynamicClient, o.DiscoveryClient); err != nil { + return nil, err + } + helper.DryRun(o.ServerDryRun) + } return &Patcher{ Mapping: info.Mapping, Helper: helper, @@ -95,7 +100,7 @@ func newPatcher(o *ApplyOptions, info *resource.Info) *Patcher { ServerDryRun: o.ServerDryRun, OpenapiSchema: openapiSchema, Retries: maxPatchRetry, - } + }, nil } func (p *Patcher) delete(namespace, name string) error { @@ -180,12 +185,7 @@ func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, source, names } } - options := metav1.PatchOptions{} - if p.ServerDryRun { - options.DryRun = []string{metav1.DryRunAll} - } - - patchedObj, err := p.Helper.Patch(namespace, name, patchType, patch, &options) + patchedObj, err := p.Helper.Patch(namespace, name, patchType, patch, nil) return patch, patchedObj, err } @@ -230,15 +230,11 @@ func (p *Patcher) deleteAndCreate(original runtime.Object, modified []byte, name if err != nil { return modified, nil, err } - options := metav1.CreateOptions{} - if p.ServerDryRun { - options.DryRun = []string{metav1.DryRunAll} - } - createdObject, err := p.Helper.Create(namespace, true, versionedObject, &options) + createdObject, err := p.Helper.Create(namespace, true, versionedObject) if err != nil { // restore the original object if we fail to create the new one // but still propagate and advertise error to user - recreated, recreateErr := p.Helper.Create(namespace, true, original, &options) + recreated, recreateErr := p.Helper.Create(namespace, true, original) if recreateErr != nil { err = fmt.Errorf("An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v", err, recreateErr) } else { diff --git a/pkg/cmd/create/create.go b/pkg/cmd/create/create.go index 19c88c9f..74e7ac9c 100644 --- a/pkg/cmd/create/create.go +++ b/pkg/cmd/create/create.go @@ -299,7 +299,7 @@ func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags // createAndRefresh creates an object from input info and refreshes info with that object func createAndRefresh(info *resource.Info) error { - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil) + obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) if err != nil { return err } diff --git a/pkg/cmd/diff/diff.go b/pkg/cmd/diff/diff.go index 15179d58..ac762a71 100644 --- a/pkg/cmd/diff/diff.go +++ b/pkg/cmd/diff/diff.go @@ -77,7 +77,7 @@ type DiffOptions struct { OpenAPISchema openapi.Resources DiscoveryClient discovery.DiscoveryInterface DynamicClient dynamic.Interface - DryRunVerifier *apply.DryRunVerifier + DryRunVerifier *resource.DryRunVerifier CmdNamespace string EnforceNamespace bool Builder *resource.Builder @@ -295,7 +295,7 @@ func (obj InfoObject) Merged() (runtime.Object, error) { // Build the patcher, and then apply the patch with dry-run, unless the object doesn't exist, in which case we need to create it. if obj.Live() == nil { // Dry-run create if the object doesn't exist. - return resource.NewHelper(obj.Info.Client, obj.Info.Mapping).Create( + return resource.NewHelper(obj.Info.Client, obj.Info.Mapping).CreateWithOptions( obj.Info.Namespace, true, obj.LocalObj, @@ -427,10 +427,7 @@ func (o *DiffOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - o.DryRunVerifier = &apply.DryRunVerifier{ - Finder: cmdutil.NewCRDFinder(cmdutil.CRDFromDynamic(o.DynamicClient)), - OpenAPIGetter: o.DiscoveryClient, - } + o.DryRunVerifier = resource.NewDryRunVerifier(o.DynamicClient, o.DiscoveryClient) o.CmdNamespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { diff --git a/pkg/cmd/replace/replace.go b/pkg/cmd/replace/replace.go index f5980013..e5d28811 100644 --- a/pkg/cmd/replace/replace.go +++ b/pkg/cmd/replace/replace.go @@ -355,7 +355,7 @@ func (o *ReplaceOptions) forceReplace() error { klog.V(4).Infof("error recording current command: %v", err) } - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil) + obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) if err != nil { return err } diff --git a/pkg/cmd/run/run.go b/pkg/cmd/run/run.go index 4f782374..b6d9b5a9 100644 --- a/pkg/cmd/run/run.go +++ b/pkg/cmd/run/run.go @@ -708,7 +708,7 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command if err != nil { return nil, err } - actualObj, err = resource.NewHelper(client, mapping).Create(namespace, false, obj, nil) + actualObj, err = resource.NewHelper(client, mapping).Create(namespace, false, obj) if err != nil { return nil, err } diff --git a/pkg/cmd/util/crdfinder.go b/pkg/cmd/util/crdfinder.go deleted file mode 100644 index aaf309dc..00000000 --- a/pkg/cmd/util/crdfinder.go +++ /dev/null @@ -1,109 +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 util - -import ( - "fmt" - "reflect" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" -) - -// CRDGetter is a function that can download the list of GVK for all -// CRDs. -type CRDGetter func() ([]schema.GroupKind, error) - -func CRDFromDynamic(client dynamic.Interface) CRDGetter { - return func() ([]schema.GroupKind, error) { - list, err := client.Resource(schema.GroupVersionResource{ - Group: "apiextensions.k8s.io", - Version: "v1beta1", - Resource: "customresourcedefinitions", - }).List(metav1.ListOptions{}) - if err != nil { - return nil, fmt.Errorf("failed to list CRDs: %v", err) - } - if list == nil { - return nil, nil - } - - gks := []schema.GroupKind{} - - // We need to parse the list to get the gvk, I guess that's fine. - for _, crd := range (*list).Items { - // Look for group, version, and kind - group, _, _ := unstructured.NestedString(crd.Object, "spec", "group") - kind, _, _ := unstructured.NestedString(crd.Object, "spec", "names", "kind") - - gks = append(gks, schema.GroupKind{ - Group: group, - Kind: kind, - }) - } - - return gks, nil - } -} - -// CRDFinder keeps a cache of known CRDs and finds a given GVK in the -// list. -type CRDFinder interface { - HasCRD(gvk schema.GroupKind) (bool, error) -} - -func NewCRDFinder(getter CRDGetter) CRDFinder { - return &crdFinder{ - getter: getter, - } -} - -type crdFinder struct { - getter CRDGetter - cache *[]schema.GroupKind -} - -func (f *crdFinder) cacheCRDs() error { - if f.cache != nil { - return nil - } - - list, err := f.getter() - if err != nil { - return err - } - f.cache = &list - return nil -} - -func (f *crdFinder) findCRD(gvk schema.GroupKind) bool { - for _, crd := range *f.cache { - if reflect.DeepEqual(gvk, crd) { - return true - } - } - return false -} - -func (f *crdFinder) HasCRD(gvk schema.GroupKind) (bool, error) { - if err := f.cacheCRDs(); err != nil { - return false, err - } - return f.findCRD(gvk), nil -} diff --git a/pkg/cmd/util/crdfinder_test.go b/pkg/cmd/util/crdfinder_test.go deleted file mode 100644 index 084a3f6e..00000000 --- a/pkg/cmd/util/crdfinder_test.go +++ /dev/null @@ -1,89 +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 util_test - -import ( - "errors" - "testing" - - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubectl/pkg/cmd/util" -) - -func TestCacheCRDFinder(t *testing.T) { - called := 0 - getter := func() ([]schema.GroupKind, error) { - called += 1 - return nil, nil - } - finder := util.NewCRDFinder(getter) - if called != 0 { - t.Fatalf("Creating the finder shouldn't call the getter, has called = %v", called) - } - _, err := finder.HasCRD(schema.GroupKind{Group: "", Kind: "Pod"}) - if err != nil { - t.Fatalf("Failed to call HasCRD: %v", err) - } - if called != 1 { - t.Fatalf("First call should call the getter, has called = %v", called) - } - - _, err = finder.HasCRD(schema.GroupKind{Group: "", Kind: "Pod"}) - if err != nil { - t.Fatalf("Failed to call HasCRD: %v", err) - } - if called != 1 { - t.Fatalf("Second call should NOT call the getter, has called = %v", called) - } -} - -func TestCRDFinderErrors(t *testing.T) { - getter := func() ([]schema.GroupKind, error) { - return nil, errors.New("not working") - } - finder := util.NewCRDFinder(getter) - found, err := finder.HasCRD(schema.GroupKind{Group: "", Kind: "Pod"}) - if found == true { - t.Fatalf("Found the CRD with non-working getter function") - } - if err == nil { - t.Fatalf("Error in getter should be reported") - } -} - -func TestCRDFinder(t *testing.T) { - getter := func() ([]schema.GroupKind, error) { - return []schema.GroupKind{ - { - Group: "crd.com", - Kind: "MyCRD", - }, - { - Group: "crd.com", - Kind: "MyNewCRD", - }, - }, nil - } - finder := util.NewCRDFinder(getter) - - if found, _ := finder.HasCRD(schema.GroupKind{Group: "crd.com", Kind: "MyCRD"}); !found { - t.Fatalf("Failed to find CRD MyCRD") - } - if found, _ := finder.HasCRD(schema.GroupKind{Group: "crd.com", Kind: "Random"}); found { - t.Fatalf("Found crd Random that doesn't exist") - } -} diff --git a/pkg/util/openapi/dryrun.go b/pkg/util/openapi/dryrun.go deleted file mode 100644 index 33cf9e9e..00000000 --- a/pkg/util/openapi/dryrun.go +++ /dev/null @@ -1,65 +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 openapi - -import ( - "errors" - - openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" - yaml "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool { - for _, extension := range extensions { - if extension.GetValue().GetYaml() == "" || - extension.GetName() != "x-kubernetes-group-version-kind" { - continue - } - var value map[string]string - err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value) - if err != nil { - continue - } - - if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version { - return true - } - return false - } - return false -} - -// SupportsDryRun is a method that let's us look in the OpenAPI if the -// specific group-version-kind supports the dryRun query parameter for -// the PATCH end-point. -func SupportsDryRun(doc *openapi_v2.Document, gvk schema.GroupVersionKind) (bool, error) { - for _, path := range doc.GetPaths().GetPath() { - // Is this describing the gvk we're looking for? - if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) { - continue - } - for _, param := range path.GetValue().GetPatch().GetParameters() { - if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == "dryRun" { - return true, nil - } - } - return false, nil - } - - return false, errors.New("couldn't find GVK in openapi") -} diff --git a/pkg/util/openapi/dryrun_test.go b/pkg/util/openapi/dryrun_test.go deleted file mode 100644 index 4a299d13..00000000 --- a/pkg/util/openapi/dryrun_test.go +++ /dev/null @@ -1,80 +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 openapi_test - -import ( - "testing" - - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubectl/pkg/util/openapi" -) - -func TestSupportsDryRun(t *testing.T) { - doc, err := fakeSchema.OpenAPISchema() - if err != nil { - t.Fatalf("Failed to get OpenAPI Schema: %v", err) - } - - tests := []struct { - gvk schema.GroupVersionKind - success bool - supports bool - }{ - { - gvk: schema.GroupVersionKind{ - Group: "", - Version: "v1", - Kind: "Pod", - }, - success: true, - supports: true, - }, - { - gvk: schema.GroupVersionKind{ - Group: "", - Version: "v1", - Kind: "UnknownKind", - }, - success: false, - supports: false, - }, - { - gvk: schema.GroupVersionKind{ - Group: "", - Version: "v1", - Kind: "NodeProxyOptions", - }, - success: true, - supports: false, - }, - } - - for _, test := range tests { - supports, err := openapi.SupportsDryRun(doc, test.gvk) - if supports != test.supports || ((err == nil) != test.success) { - errStr := "nil" - if test.success == false { - errStr = "err" - } - t.Errorf("SupportsDryRun(doc, %v) = (%v, %v), expected (%v, %v)", - test.gvk, - supports, err, - test.supports, errStr, - ) - } - } -}