Merge pull request #108889 from kevindelgado/validation-beta-flag

ServerSideFieldValidation Beta Graduation
This commit is contained in:
Kubernetes Prow Robot 2022-03-28 14:20:12 -07:00 committed by GitHub
commit ea0dc6ed41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 27 deletions

View File

@ -993,7 +993,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
genericfeatures.OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta}, genericfeatures.OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta},
genericfeatures.CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha}, genericfeatures.CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha},
genericfeatures.OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha}, genericfeatures.OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha},
genericfeatures.ServerSideFieldValidation: {Default: false, PreRelease: featuregate.Alpha}, genericfeatures.ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta},
// features that enable backwards compatibility but are scheduled to be removed // features that enable backwards compatibility but are scheduled to be removed
// ... // ...
HPAScaleToZero: {Default: false, PreRelease: featuregate.Alpha}, HPAScaleToZero: {Default: false, PreRelease: featuregate.Alpha},

View File

@ -174,6 +174,7 @@ const (
// owner: @kevindelgado // owner: @kevindelgado
// kep: http://kep.k8s.io/2885 // kep: http://kep.k8s.io/2885
// alpha: v1.23 // alpha: v1.23
// beta: v1.24
// //
// Enables server-side field validation. // Enables server-side field validation.
ServerSideFieldValidation featuregate.Feature = "ServerSideFieldValidation" ServerSideFieldValidation featuregate.Feature = "ServerSideFieldValidation"
@ -205,5 +206,5 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta}, OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta},
CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha}, CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha},
OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha}, OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha},
ServerSideFieldValidation: {Default: false, PreRelease: featuregate.Alpha}, ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta},
} }

View File

@ -251,8 +251,8 @@
works for CRD preserving unknown fields at the schema root [Conformance]' works for CRD preserving unknown fields at the schema root [Conformance]'
description: Register a custom resource definition with x-preserve-unknown-fields description: Register a custom resource definition with x-preserve-unknown-fields
in the schema root. Attempt to create and apply a change a custom resource, via in the schema root. Attempt to create and apply a change a custom resource, via
kubectl; client-side validation MUST accept unknown properties. Attempt kubectl kubectl; kubectl validation MUST accept unknown properties. Attempt kubectl explain;
explain; the output MUST show the custom resource KIND. the output MUST show the custom resource KIND.
release: v1.16 release: v1.16
file: test/e2e/apimachinery/crd_publish_openapi.go file: test/e2e/apimachinery/crd_publish_openapi.go
- testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in embedded - testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in embedded
@ -261,7 +261,7 @@
works for CRD preserving unknown fields in an embedded object [Conformance]' works for CRD preserving unknown fields in an embedded object [Conformance]'
description: Register a custom resource definition with x-preserve-unknown-fields description: Register a custom resource definition with x-preserve-unknown-fields
in an embedded object. Attempt to create and apply a change a custom resource, in an embedded object. Attempt to create and apply a change a custom resource,
via kubectl; client-side validation MUST accept unknown properties. Attempt kubectl via kubectl; kubectl validation MUST accept unknown properties. Attempt kubectl
explain; the output MUST show that x-preserve-unknown-properties is used on the explain; the output MUST show that x-preserve-unknown-properties is used on the
nested field. nested field.
release: v1.16 release: v1.16
@ -271,12 +271,12 @@
works for CRD with validation schema [Conformance]' works for CRD with validation schema [Conformance]'
description: Register a custom resource definition with a validating schema consisting description: Register a custom resource definition with a validating schema consisting
of objects, arrays and primitives. Attempt to create and apply a change a custom of objects, arrays and primitives. Attempt to create and apply a change a custom
resource using valid properties, via kubectl; client-side validation MUST pass. resource using valid properties, via kubectl; kubectl validation MUST pass. Attempt
Attempt both operations with unknown properties and without required properties; both operations with unknown properties and without required properties; kubectl
client-side validation MUST reject the operations. Attempt kubectl explain; the validation MUST reject the operations. Attempt kubectl explain; the output MUST
output MUST explain the custom resource properties. Attempt kubectl explain on explain the custom resource properties. Attempt kubectl explain on custom resource
custom resource properties; the output MUST explain the nested custom resource properties; the output MUST explain the nested custom resource properties. All
properties. validation should be the same.
release: v1.16 release: v1.16
file: test/e2e/apimachinery/crd_publish_openapi.go file: test/e2e/apimachinery/crd_publish_openapi.go
- testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in object - testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in object
@ -284,7 +284,7 @@
works for CRD without validation schema [Conformance]' works for CRD without validation schema [Conformance]'
description: Register a custom resource definition with x-preserve-unknown-fields description: Register a custom resource definition with x-preserve-unknown-fields
in the top level object. Attempt to create and apply a change a custom resource, in the top level object. Attempt to create and apply a change a custom resource,
via kubectl; client-side validation MUST accept unknown properties. Attempt kubectl via kubectl; kubectl validation MUST accept unknown properties. Attempt kubectl
explain; the output MUST contain a valid DESCRIPTION stanza. explain; the output MUST contain a valid DESCRIPTION stanza.
release: v1.16 release: v1.16
file: test/e2e/apimachinery/crd_publish_openapi.go file: test/e2e/apimachinery/crd_publish_openapi.go

View File

@ -58,10 +58,11 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
Testname: Custom Resource OpenAPI Publish, with validation schema Testname: Custom Resource OpenAPI Publish, with validation schema
Description: Register a custom resource definition with a validating schema consisting of objects, arrays and Description: Register a custom resource definition with a validating schema consisting of objects, arrays and
primitives. Attempt to create and apply a change a custom resource using valid properties, via kubectl; primitives. Attempt to create and apply a change a custom resource using valid properties, via kubectl;
client-side validation MUST pass. Attempt both operations with unknown properties and without required kubectl validation MUST pass. Attempt both operations with unknown properties and without required
properties; client-side validation MUST reject the operations. Attempt kubectl explain; the output MUST properties; kubectl validation MUST reject the operations. Attempt kubectl explain; the output MUST
explain the custom resource properties. Attempt kubectl explain on custom resource properties; the output MUST explain the custom resource properties. Attempt kubectl explain on custom resource properties; the output MUST
explain the nested custom resource properties. explain the nested custom resource properties.
All validation should be the same.
*/ */
framework.ConformanceIt("works for CRD with validation schema", func() { framework.ConformanceIt("works for CRD with validation schema", func() {
crd, err := setupCRD(f, schemaFoo, "foo", "v1") crd, err := setupCRD(f, schemaFoo, "foo", "v1")
@ -72,7 +73,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-foo") meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-foo")
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name) ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
ginkgo.By("client-side validation (kubectl create and apply) allows request with known and required properties") ginkgo.By("kubectl validation (kubectl create and apply) allows request with known and required properties")
validCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}]}}`, meta) validCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, validCR, ns, "create", "-f", "-"); err != nil { if _, err := framework.RunKubectlInput(f.Namespace.Name, validCR, ns, "create", "-f", "-"); err != nil {
framework.Failf("failed to create valid CR %s: %v", validCR, err) framework.Failf("failed to create valid CR %s: %v", validCR, err)
@ -87,13 +88,15 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
framework.Failf("failed to delete valid CR: %v", err) framework.Failf("failed to delete valid CR: %v", err)
} }
ginkgo.By("client-side validation (kubectl create and apply) rejects request with value outside defined enum values") ginkgo.By("kubectl validation (kubectl create and apply) rejects request with value outside defined enum values")
badEnumValueCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar", "feeling":"NonExistentValue"}]}}`, meta) badEnumValueCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar", "feeling":"NonExistentValue"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, badEnumValueCR, ns, "create", "-f", "-"); err == nil || !strings.Contains(err.Error(), `Unsupported value: "NonExistentValue"`) { if _, err := framework.RunKubectlInput(f.Namespace.Name, badEnumValueCR, ns, "create", "-f", "-"); err == nil || !strings.Contains(err.Error(), `Unsupported value: "NonExistentValue"`) {
framework.Failf("unexpected no error when creating CR with unknown enum value: %v", err) framework.Failf("unexpected no error when creating CR with unknown enum value: %v", err)
} }
ginkgo.By("client-side validation (kubectl create and apply) rejects request with unknown properties when disallowed by the schema") // TODO: server-side validation and client-side validation produce slightly different error messages.
// Because server-side is default in beta but not GA yet, we will produce different behaviors in the default vs GA only conformance tests. We have made the error generic enough to pass both, but should go back and make the error more specific once server-side validation goes GA.
ginkgo.By("kubectl validation (kubectl create and apply) rejects request with unknown properties when disallowed by the schema")
unknownCR := fmt.Sprintf(`{%s,"spec":{"foo":true}}`, meta) unknownCR := fmt.Sprintf(`{%s,"spec":{"foo":true}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, unknownCR, ns, "create", "-f", "-"); err == nil || (!strings.Contains(err.Error(), `unknown field "foo"`) && !strings.Contains(err.Error(), `unknown field "spec.foo"`)) { if _, err := framework.RunKubectlInput(f.Namespace.Name, unknownCR, ns, "create", "-f", "-"); err == nil || (!strings.Contains(err.Error(), `unknown field "foo"`) && !strings.Contains(err.Error(), `unknown field "spec.foo"`)) {
framework.Failf("unexpected no error when creating CR with unknown field: %v", err) framework.Failf("unexpected no error when creating CR with unknown field: %v", err)
@ -102,7 +105,8 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
framework.Failf("unexpected no error when applying CR with unknown field: %v", err) framework.Failf("unexpected no error when applying CR with unknown field: %v", err)
} }
ginkgo.By("client-side validation (kubectl create and apply) rejects request without required properties") // TODO: see above note, we should check the value of the error once server-side validation is GA.
ginkgo.By("kubectl validation (kubectl create and apply) rejects request without required properties")
noRequireCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"age":"10"}]}}`, meta) noRequireCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"age":"10"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, noRequireCR, ns, "create", "-f", "-"); err == nil || (!strings.Contains(err.Error(), `missing required field "name"`) && !strings.Contains(err.Error(), `spec.bars[0].name: Required value`)) { if _, err := framework.RunKubectlInput(f.Namespace.Name, noRequireCR, ns, "create", "-f", "-"); err == nil || (!strings.Contains(err.Error(), `missing required field "name"`) && !strings.Contains(err.Error(), `spec.bars[0].name: Required value`)) {
framework.Failf("unexpected no error when creating CR without required field: %v", err) framework.Failf("unexpected no error when creating CR without required field: %v", err)
@ -141,7 +145,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
Release: v1.16 Release: v1.16
Testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in object Testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in object
Description: Register a custom resource definition with x-preserve-unknown-fields in the top level object. Description: Register a custom resource definition with x-preserve-unknown-fields in the top level object.
Attempt to create and apply a change a custom resource, via kubectl; client-side validation MUST accept unknown Attempt to create and apply a change a custom resource, via kubectl; kubectl validation MUST accept unknown
properties. Attempt kubectl explain; the output MUST contain a valid DESCRIPTION stanza. properties. Attempt kubectl explain; the output MUST contain a valid DESCRIPTION stanza.
*/ */
framework.ConformanceIt("works for CRD without validation schema", func() { framework.ConformanceIt("works for CRD without validation schema", func() {
@ -153,7 +157,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr") meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name) ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
ginkgo.By("client-side validation (kubectl create and apply) allows request with any unknown properties") ginkgo.By("kubectl validation (kubectl create and apply) allows request with any unknown properties")
randomCR := fmt.Sprintf(`{%s,"a":{"b":[{"c":"d"}]}}`, meta) randomCR := fmt.Sprintf(`{%s,"a":{"b":[{"c":"d"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil { if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil {
framework.Failf("failed to create random CR %s for CRD without schema: %v", randomCR, err) framework.Failf("failed to create random CR %s for CRD without schema: %v", randomCR, err)
@ -182,7 +186,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
Release: v1.16 Release: v1.16
Testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields at root Testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields at root
Description: Register a custom resource definition with x-preserve-unknown-fields in the schema root. Description: Register a custom resource definition with x-preserve-unknown-fields in the schema root.
Attempt to create and apply a change a custom resource, via kubectl; client-side validation MUST accept unknown Attempt to create and apply a change a custom resource, via kubectl; kubectl validation MUST accept unknown
properties. Attempt kubectl explain; the output MUST show the custom resource KIND. properties. Attempt kubectl explain; the output MUST show the custom resource KIND.
*/ */
framework.ConformanceIt("works for CRD preserving unknown fields at the schema root", func() { framework.ConformanceIt("works for CRD preserving unknown fields at the schema root", func() {
@ -194,7 +198,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr") meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name) ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
ginkgo.By("client-side validation (kubectl create and apply) allows request with any unknown properties") ginkgo.By("kubectl validation (kubectl create and apply) allows request with any unknown properties")
randomCR := fmt.Sprintf(`{%s,"a":{"b":[{"c":"d"}]}}`, meta) randomCR := fmt.Sprintf(`{%s,"a":{"b":[{"c":"d"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil { if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil {
framework.Failf("failed to create random CR %s for CRD that allows unknown properties at the root: %v", randomCR, err) framework.Failf("failed to create random CR %s for CRD that allows unknown properties at the root: %v", randomCR, err)
@ -223,7 +227,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
Release: v1.16 Release: v1.16
Testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in embedded object Testname: Custom Resource OpenAPI Publish, with x-preserve-unknown-fields in embedded object
Description: Register a custom resource definition with x-preserve-unknown-fields in an embedded object. Description: Register a custom resource definition with x-preserve-unknown-fields in an embedded object.
Attempt to create and apply a change a custom resource, via kubectl; client-side validation MUST accept unknown Attempt to create and apply a change a custom resource, via kubectl; kubectl validation MUST accept unknown
properties. Attempt kubectl explain; the output MUST show that x-preserve-unknown-properties is used on the properties. Attempt kubectl explain; the output MUST show that x-preserve-unknown-properties is used on the
nested field. nested field.
*/ */
@ -236,7 +240,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Privileged:ClusterAdmin]", fu
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr") meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name) ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
ginkgo.By("client-side validation (kubectl create and apply) allows request with any unknown properties") ginkgo.By("kubectl validation (kubectl create and apply) allows request with any unknown properties")
randomCR := fmt.Sprintf(`{%s,"spec":{"a":null,"b":[{"c":"d"}]}}`, meta) randomCR := fmt.Sprintf(`{%s,"spec":{"a":null,"b":[{"c":"d"}]}}`, meta)
if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil { if _, err := framework.RunKubectlInput(f.Namespace.Name, randomCR, ns, "create", "-f", "-"); err != nil {
framework.Failf("failed to create random CR %s for CRD that allows unknown properties in a nested object: %v", randomCR, err) framework.Failf("failed to create random CR %s for CRD that allows unknown properties in a nested object: %v", randomCR, err)

View File

@ -1003,7 +1003,7 @@ metadata:
return nil return nil
} }
ginkgo.Describe("Kubectl client-side validation", func() { ginkgo.Describe("Kubectl validation", func() {
ginkgo.It("should create/apply a CR with unknown fields for CRD with no validation schema", func() { ginkgo.It("should create/apply a CR with unknown fields for CRD with no validation schema", func() {
ginkgo.By("create CRD with no validation schema") ginkgo.By("create CRD with no validation schema")
crd, err := crd.CreateTestCRD(f) crd, err := crd.CreateTestCRD(f)
@ -1048,7 +1048,7 @@ metadata:
} }
}) })
ginkgo.It("should create/apply a valid CR with arbitrary-extra properties for CRD with partially-specified validation schema", func() { ginkgo.It("should create/apply an invalid/valid CR with arbitrary-extra properties for CRD with partially-specified validation schema", func() {
ginkgo.By("prepare CRD with partially-specified validation schema") ginkgo.By("prepare CRD with partially-specified validation schema")
crd, err := crd.CreateTestCRD(f, func(crd *apiextensionsv1.CustomResourceDefinition) { crd, err := crd.CreateTestCRD(f, func(crd *apiextensionsv1.CustomResourceDefinition) {
props := &apiextensionsv1.JSONSchemaProps{} props := &apiextensionsv1.JSONSchemaProps{}
@ -1074,6 +1074,15 @@ metadata:
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr") meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
// XPreserveUnknownFields is defined on the root of the schema so unknown fields within the spec
// are still considered invalid
invalidArbitraryCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}],"extraProperty":"arbitrary-value"}}`, meta)
err = createApplyCustomResource(invalidArbitraryCR, f.Namespace.Name, "test-cr", crd)
framework.ExpectError(err, "creating custom resource")
if !strings.Contains(err.Error(), `unknown field "spec.extraProperty"`) {
framework.Failf("incorrect error from createApplyCustomResource: %v", err)
}
// unknown fields on the root are considered valid // unknown fields on the root are considered valid
validArbitraryCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}]},"extraProperty":"arbitrary-value"}`, meta) validArbitraryCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}]},"extraProperty":"arbitrary-value"}`, meta)
err = createApplyCustomResource(validArbitraryCR, f.Namespace.Name, "test-cr", crd) err = createApplyCustomResource(validArbitraryCR, f.Namespace.Name, "test-cr", crd)
@ -2153,7 +2162,7 @@ func startLocalProxy() (srv *httptest.Server, logs *bytes.Buffer) {
} }
// createApplyCustomResource asserts that given CustomResource be created and applied // createApplyCustomResource asserts that given CustomResource be created and applied
// without being rejected by client-side validation // without being rejected by kubectl validation
func createApplyCustomResource(resource, namespace, name string, crd *crd.TestCrd) error { func createApplyCustomResource(resource, namespace, name string, crd *crd.TestCrd) error {
ginkgo.By("successfully create CR") ginkgo.By("successfully create CR")
if _, err := framework.RunKubectlInput(namespace, resource, "create", "--validate=true", "-f", "-"); err != nil { if _, err := framework.RunKubectlInput(namespace, resource, "create", "--validate=true", "-f", "-"); err != nil {