diff --git a/pkg/kubectl/cmd/util/openapi/BUILD b/pkg/kubectl/cmd/util/openapi/BUILD index 398af66eec..5958bec309 100644 --- a/pkg/kubectl/cmd/util/openapi/BUILD +++ b/pkg/kubectl/cmd/util/openapi/BUILD @@ -10,6 +10,7 @@ go_library( name = "go_default_library", srcs = [ "doc.go", + "dryrun.go", "extensions.go", "openapi.go", "openapi_getter.go", @@ -20,6 +21,7 @@ go_library( "//staging/src/k8s.io/client-go/discovery:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", + "//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library", ], ) @@ -28,6 +30,7 @@ go_test( name = "go_default_test", size = "small", srcs = [ + "dryrun_test.go", "openapi_getter_test.go", "openapi_suite_test.go", "openapi_test.go", diff --git a/pkg/kubectl/cmd/util/openapi/dryrun.go b/pkg/kubectl/cmd/util/openapi/dryrun.go new file mode 100644 index 0000000000..33cf9e9e5c --- /dev/null +++ b/pkg/kubectl/cmd/util/openapi/dryrun.go @@ -0,0 +1,65 @@ +/* +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/kubectl/cmd/util/openapi/dryrun_test.go b/pkg/kubectl/cmd/util/openapi/dryrun_test.go new file mode 100644 index 0000000000..e9398945b3 --- /dev/null +++ b/pkg/kubectl/cmd/util/openapi/dryrun_test.go @@ -0,0 +1,80 @@ +/* +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/kubernetes/pkg/kubectl/cmd/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, + ) + } + } +}