diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/BUILD index 64088c8a1b0..a7b948801fe 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/BUILD @@ -9,10 +9,12 @@ load( go_library( name = "go_default_library", srcs = [ + "deepcopy.go", "doc.go", "helpers.go", "register.go", "types.go", + "types_jsonschema.go", "zz_generated.deepcopy.go", ], deps = [ diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/deepcopy.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/deepcopy.go new file mode 100644 index 00000000000..dd9680c36f5 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/deepcopy.go @@ -0,0 +1,279 @@ +/* +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 apiextensions + +// TODO: Update this after a tag is created for interface fields in DeepCopy +func (in *JSONSchemaProps) DeepCopy() *JSONSchemaProps { + if in == nil { + return nil + } + out := new(JSONSchemaProps) + + *out = *in + + if in.Default != nil { + defaultJSON := JSON(deepCopyJSON(*(in.Default))) + out.Default = &(defaultJSON) + } else { + out.Default = nil + } + + if in.Example != nil { + exampleJSON := JSON(deepCopyJSON(*(in.Example))) + out.Example = &(exampleJSON) + } else { + out.Example = nil + } + + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } + + if in.Maximum != nil { + in, out := &in.Maximum, &out.Maximum + if *in == nil { + *out = nil + } else { + *out = new(float64) + **out = **in + } + } + + if in.Minimum != nil { + in, out := &in.Minimum, &out.Minimum + if *in == nil { + *out = nil + } else { + *out = new(float64) + **out = **in + } + } + + if in.MaxLength != nil { + in, out := &in.MaxLength, &out.MaxLength + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MinLength != nil { + in, out := &in.MinLength, &out.MinLength + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + if in.MaxItems != nil { + in, out := &in.MaxItems, &out.MaxItems + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MinItems != nil { + in, out := &in.MinItems, &out.MinItems + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MultipleOf != nil { + in, out := &in.MultipleOf, &out.MultipleOf + if *in == nil { + *out = nil + } else { + *out = new(float64) + **out = **in + } + } + + if in.Enum != nil { + out.Enum = make([]JSON, len(in.Enum)) + for i := range in.Enum { + out.Enum[i] = deepCopyJSON(in.Enum[i]) + } + } + + if in.MaxProperties != nil { + in, out := &in.MaxProperties, &out.MaxProperties + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MinProperties != nil { + in, out := &in.MinProperties, &out.MinProperties + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.Required != nil { + in, out := &in.Required, &out.Required + *out = make([]string, len(*in)) + copy(*out, *in) + } + + if in.Items != nil { + in, out := &in.Items, &out.Items + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaPropsOrArray) + (*in).DeepCopyInto(*out) + } + } + + if in.AllOf != nil { + in, out := &in.AllOf, &out.AllOf + *out = make([]JSONSchemaProps, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + + if in.OneOf != nil { + in, out := &in.OneOf, &out.OneOf + *out = make([]JSONSchemaProps, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AnyOf != nil { + in, out := &in.AnyOf, &out.AnyOf + *out = make([]JSONSchemaProps, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + + if in.Not != nil { + in, out := &in.Not, &out.Not + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaProps) + (*in).DeepCopyInto(*out) + } + } + + if in.Properties != nil { + in, out := &in.Properties, &out.Properties + *out = make(map[string]JSONSchemaProps, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.AdditionalProperties != nil { + in, out := &in.AdditionalProperties, &out.AdditionalProperties + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaPropsOrBool) + (*in).DeepCopyInto(*out) + } + } + + if in.PatternProperties != nil { + in, out := &in.PatternProperties, &out.PatternProperties + *out = make(map[string]JSONSchemaProps, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.Dependencies != nil { + in, out := &in.Dependencies, &out.Dependencies + *out = make(JSONSchemaDependencies, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.AdditionalItems != nil { + in, out := &in.AdditionalItems, &out.AdditionalItems + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaPropsOrBool) + (*in).DeepCopyInto(*out) + } + } + + if in.Definitions != nil { + in, out := &in.Definitions, &out.Definitions + *out = make(JSONSchemaDefinitions, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.ExternalDocs != nil { + in, out := &in.ExternalDocs, &out.ExternalDocs + if *in == nil { + *out = nil + } else { + *out = new(ExternalDocumentation) + (*in).DeepCopyInto(*out) + } + } + + return out +} + +func deepCopyJSON(x interface{}) interface{} { + switch x := x.(type) { + case map[string]interface{}: + clone := make(map[string]interface{}, len(x)) + for k, v := range x { + clone[k] = deepCopyJSON(v) + } + return clone + case []interface{}: + clone := make([]interface{}, len(x)) + for i := range x { + clone[i] = deepCopyJSON(x[i]) + } + return clone + default: + return x + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go index 705fc0bd7a1..e4a4352d485 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go @@ -17,6 +17,7 @@ limitations under the License. package fuzzer import ( + "reflect" "strings" "github.com/google/gofuzz" @@ -42,5 +43,65 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { obj.Names.ListKind = obj.Names.Kind + "List" } }, + func(obj *apiextensions.JSONSchemaProps, c fuzz.Continue) { + // we cannot use c.FuzzNoCustom because of the interface{} fields. So let's loop with reflection. + vobj := reflect.ValueOf(obj).Elem() + tobj := reflect.TypeOf(obj).Elem() + for i := 0; i < tobj.NumField(); i++ { + field := tobj.Field(i) + switch field.Name { + case "Default", "Enum", "Example": + continue + default: + isValue := true + switch field.Type.Kind() { + case reflect.Interface, reflect.Map, reflect.Slice, reflect.Ptr: + isValue = false + } + if isValue || c.Intn(5) == 0 { + c.Fuzz(vobj.Field(i).Addr().Interface()) + } + } + } + if c.RandBool() { + validJSON := apiextensions.JSON(`{"some": {"json": "test"}, "string": 42}`) + obj.Default = &validJSON + } + if c.RandBool() { + obj.Enum = []apiextensions.JSON{c.Float64(), c.RandString(), c.RandBool()} + } + if c.RandBool() { + validJSON := apiextensions.JSON(`"foobarbaz"`) + obj.Example = &validJSON + } + }, + func(obj *apiextensions.JSONSchemaPropsOrBool, c fuzz.Continue) { + if c.RandBool() { + obj.Schema = &apiextensions.JSONSchemaProps{} + c.Fuzz(obj.Schema) + } else { + obj.Allows = c.RandBool() + } + }, + func(obj *apiextensions.JSONSchemaPropsOrArray, c fuzz.Continue) { + // disallow both Schema and JSONSchemas to be nil. + if c.RandBool() { + obj.Schema = &apiextensions.JSONSchemaProps{} + c.Fuzz(obj.Schema) + } else { + obj.JSONSchemas = make([]apiextensions.JSONSchemaProps, c.Intn(3)+1) + for i := range obj.JSONSchemas { + c.Fuzz(&obj.JSONSchemas[i]) + } + } + }, + func(obj *apiextensions.JSONSchemaPropsOrStringArray, c fuzz.Continue) { + if c.RandBool() { + obj.Schema = &apiextensions.JSONSchemaProps{} + c.Fuzz(obj.Schema) + } else { + c.Fuzz(&obj.Property) + } + }, } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go index 59240fbd008..fba9c90bc26 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go @@ -26,9 +26,10 @@ type CustomResourceDefinitionSpec struct { Version string // Names are the names used to describe this custom resource Names CustomResourceDefinitionNames - // Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced Scope ResourceScope + // Validation describes the validation methods for CustomResources + Validation *CustomResourceValidation } // CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition @@ -139,3 +140,9 @@ type CustomResourceDefinitionList struct { // Items individual CustomResourceDefinitions Items []CustomResourceDefinition } + +// CustomResourceValidation is a list of validation methods for CustomResources. +type CustomResourceValidation struct { + // OpenAPIV3Schema is the OpenAPI v3 schema to be validated against. + OpenAPIV3Schema *JSONSchemaProps +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types_jsonschema.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types_jsonschema.go new file mode 100644 index 00000000000..79f34e8bf65 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types_jsonschema.go @@ -0,0 +1,96 @@ +/* +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 apiextensions + +// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/). +type JSONSchemaProps struct { + ID string + Schema JSONSchemaURL + Ref *string + Description string + Type string + Format string + Title string + Default *JSON + Maximum *float64 + ExclusiveMaximum bool + Minimum *float64 + ExclusiveMinimum bool + MaxLength *int64 + MinLength *int64 + Pattern string + MaxItems *int64 + MinItems *int64 + UniqueItems bool + MultipleOf *float64 + Enum []JSON + MaxProperties *int64 + MinProperties *int64 + Required []string + Items *JSONSchemaPropsOrArray + AllOf []JSONSchemaProps + OneOf []JSONSchemaProps + AnyOf []JSONSchemaProps + Not *JSONSchemaProps + Properties map[string]JSONSchemaProps + AdditionalProperties *JSONSchemaPropsOrBool + PatternProperties map[string]JSONSchemaProps + Dependencies JSONSchemaDependencies + AdditionalItems *JSONSchemaPropsOrBool + Definitions JSONSchemaDefinitions + ExternalDocs *ExternalDocumentation + Example *JSON +} + +// JSON represents any valid JSON value. +// These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil. +type JSON interface{} + +// JSONSchemaURL represents a schema url. +type JSONSchemaURL string + +// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps +// or an array of JSONSchemaProps. Mainly here for serialization purposes. +type JSONSchemaPropsOrArray struct { + Schema *JSONSchemaProps + JSONSchemas []JSONSchemaProps +} + +// JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value. +// Defaults to true for the boolean property. +type JSONSchemaPropsOrBool struct { + Allows bool + Schema *JSONSchemaProps +} + +// JSONSchemaDependencies represent a dependencies property. +type JSONSchemaDependencies map[string]JSONSchemaPropsOrStringArray + +// JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array. +type JSONSchemaPropsOrStringArray struct { + Schema *JSONSchemaProps + Property []string +} + +// JSONSchemaDefinitions contains the models explicitly defined in this spec. +type JSONSchemaDefinitions map[string]JSONSchemaProps + +// ExternalDocumentation allows referencing an external resource for extended documentation. +type ExternalDocumentation struct { + Description string + URL string +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/BUILD index 60b91ad1f23..421014e87c8 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/BUILD @@ -3,16 +3,21 @@ package(default_visibility = ["//visibility:public"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", + "go_test", ) go_library( name = "go_default_library", srcs = [ + "conversion.go", + "deepcopy.go", "defaults.go", "doc.go", "generated.pb.go", + "marshal.go", "register.go", "types.go", + "types_jsonschema.go", "zz_generated.conversion.go", "zz_generated.deepcopy.go", "zz_generated.defaults.go", @@ -24,6 +29,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library", ], ) @@ -45,3 +51,16 @@ filegroup( srcs = ["generated.proto"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = [ + "conversion_test.go", + "marshal_test.go", + ], + library = ":go_default_library", + deps = [ + "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + ], +) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/conversion.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/conversion.go new file mode 100644 index 00000000000..f9951009dc9 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/conversion.go @@ -0,0 +1,73 @@ +/* +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 v1beta1 + +import ( + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/json" + + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" +) + +func addConversionFuncs(scheme *runtime.Scheme) error { + // Add non-generated conversion functions + err := scheme.AddConversionFuncs( + Convert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps, + Convert_apiextensions_JSON_To_v1beta1_JSON, + Convert_v1beta1_JSON_To_apiextensions_JSON, + ) + if err != nil { + return err + } + return nil +} + +func Convert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in *apiextensions.JSONSchemaProps, out *JSONSchemaProps, s conversion.Scope) error { + if err := autoConvert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in, out, s); err != nil { + return err + } + if in.Default != nil && *(in.Default) == nil { + out.Default = nil + } + if in.Example != nil && *(in.Example) == nil { + out.Example = nil + } + return nil +} + +func Convert_apiextensions_JSON_To_v1beta1_JSON(in *apiextensions.JSON, out *JSON, s conversion.Scope) error { + raw, err := json.Marshal(*in) + if err != nil { + return err + } + out.Raw = raw + return nil +} + +func Convert_v1beta1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s conversion.Scope) error { + if in != nil { + var i interface{} + if err := json.Unmarshal(in.Raw, &i); err != nil { + return err + } + *out = i + } else { + out = nil + } + return nil +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/conversion_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/conversion_test.go new file mode 100644 index 00000000000..a697dd9e3ac --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/conversion_test.go @@ -0,0 +1,113 @@ +/* +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 v1beta1 + +import ( + "reflect" + "testing" + + "k8s.io/apimachinery/pkg/runtime" + + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" +) + +func TestJSONConversion(t *testing.T) { + nilJSON := apiextensions.JSON(nil) + nullJSON := apiextensions.JSON("null") + stringJSON := apiextensions.JSON("foo") + boolJSON := apiextensions.JSON(true) + sliceJSON := apiextensions.JSON([]string{"foo", "bar", "baz"}) + + testCases := map[string]struct { + input *apiextensions.JSONSchemaProps + expected *JSONSchemaProps + }{ + "nil": { + input: &apiextensions.JSONSchemaProps{ + Default: nil, + }, + expected: &JSONSchemaProps{}, + }, + "aliased nil": { + input: &apiextensions.JSONSchemaProps{ + Default: &nilJSON, + }, + expected: &JSONSchemaProps{}, + }, + "null": { + input: &apiextensions.JSONSchemaProps{ + Default: &nullJSON, + }, + expected: &JSONSchemaProps{ + Default: &JSON{ + Raw: []byte(`"null"`), + }, + }, + }, + "string": { + input: &apiextensions.JSONSchemaProps{ + Default: &stringJSON, + }, + expected: &JSONSchemaProps{ + Default: &JSON{ + Raw: []byte(`"foo"`), + }, + }, + }, + "bool": { + input: &apiextensions.JSONSchemaProps{ + Default: &boolJSON, + }, + expected: &JSONSchemaProps{ + Default: &JSON{ + Raw: []byte(`true`), + }, + }, + }, + "slice": { + input: &apiextensions.JSONSchemaProps{ + Default: &sliceJSON, + }, + expected: &JSONSchemaProps{ + Default: &JSON{ + Raw: []byte(`["foo","bar","baz"]`), + }, + }, + }, + } + + scheme := runtime.NewScheme() + + // add internal and external types + if err := apiextensions.AddToScheme(scheme); err != nil { + t.Fatal(err) + } + if err := AddToScheme(scheme); err != nil { + t.Fatal(err) + } + + for k, tc := range testCases { + external := &JSONSchemaProps{} + if err := scheme.Convert(tc.input, external, nil); err != nil { + t.Errorf("%s: unexpected error: %v", k, err) + } + + if !reflect.DeepEqual(external, tc.expected) { + t.Errorf("%s: expected\n\t%#v, got \n\t%#v", k, tc.expected, external) + } + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/deepcopy.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/deepcopy.go new file mode 100644 index 00000000000..903773ae213 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/deepcopy.go @@ -0,0 +1,257 @@ +/* +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 v1beta1 + +// TODO: Update this after a tag is created for interface fields in DeepCopy +func (in *JSONSchemaProps) DeepCopy() *JSONSchemaProps { + if in == nil { + return nil + } + out := new(JSONSchemaProps) + *out = *in + + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } + + if in.Maximum != nil { + in, out := &in.Maximum, &out.Maximum + if *in == nil { + *out = nil + } else { + *out = new(float64) + **out = **in + } + } + + if in.Minimum != nil { + in, out := &in.Minimum, &out.Minimum + if *in == nil { + *out = nil + } else { + *out = new(float64) + **out = **in + } + } + + if in.MaxLength != nil { + in, out := &in.MaxLength, &out.MaxLength + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MinLength != nil { + in, out := &in.MinLength, &out.MinLength + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + if in.MaxItems != nil { + in, out := &in.MaxItems, &out.MaxItems + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MinItems != nil { + in, out := &in.MinItems, &out.MinItems + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MultipleOf != nil { + in, out := &in.MultipleOf, &out.MultipleOf + if *in == nil { + *out = nil + } else { + *out = new(float64) + **out = **in + } + } + + if in.MaxProperties != nil { + in, out := &in.MaxProperties, &out.MaxProperties + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.MinProperties != nil { + in, out := &in.MinProperties, &out.MinProperties + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + + if in.Required != nil { + in, out := &in.Required, &out.Required + *out = make([]string, len(*in)) + copy(*out, *in) + } + + if in.Items != nil { + in, out := &in.Items, &out.Items + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaPropsOrArray) + (*in).DeepCopyInto(*out) + } + } + + if in.AllOf != nil { + in, out := &in.AllOf, &out.AllOf + *out = make([]JSONSchemaProps, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + + if in.OneOf != nil { + in, out := &in.OneOf, &out.OneOf + *out = make([]JSONSchemaProps, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AnyOf != nil { + in, out := &in.AnyOf, &out.AnyOf + *out = make([]JSONSchemaProps, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + + if in.Not != nil { + in, out := &in.Not, &out.Not + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaProps) + (*in).DeepCopyInto(*out) + } + } + + if in.Properties != nil { + in, out := &in.Properties, &out.Properties + *out = make(map[string]JSONSchemaProps, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.AdditionalProperties != nil { + in, out := &in.AdditionalProperties, &out.AdditionalProperties + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaPropsOrBool) + (*in).DeepCopyInto(*out) + } + } + + if in.PatternProperties != nil { + in, out := &in.PatternProperties, &out.PatternProperties + *out = make(map[string]JSONSchemaProps, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.Dependencies != nil { + in, out := &in.Dependencies, &out.Dependencies + *out = make(JSONSchemaDependencies, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.AdditionalItems != nil { + in, out := &in.AdditionalItems, &out.AdditionalItems + if *in == nil { + *out = nil + } else { + *out = new(JSONSchemaPropsOrBool) + (*in).DeepCopyInto(*out) + } + } + + if in.Definitions != nil { + in, out := &in.Definitions, &out.Definitions + *out = make(JSONSchemaDefinitions, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + + if in.ExternalDocs != nil { + in, out := &in.ExternalDocs, &out.ExternalDocs + if *in == nil { + *out = nil + } else { + *out = new(ExternalDocumentation) + (*in).DeepCopyInto(*out) + } + } + + return out +} + +func deepCopyJSON(x interface{}) interface{} { + switch x := x.(type) { + case map[string]interface{}: + clone := make(map[string]interface{}, len(x)) + for k, v := range x { + clone[k] = deepCopyJSON(v) + } + return clone + case []interface{}: + clone := make([]interface{}, len(x)) + for i := range x { + clone[i] = deepCopyJSON(x[i]) + } + return clone + default: + return x + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/marshal.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/marshal.go new file mode 100644 index 00000000000..d8f9f164e8c --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/marshal.go @@ -0,0 +1,134 @@ +/* +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 v1beta1 + +import ( + "errors" + + "k8s.io/apimachinery/pkg/util/json" +) + +var jsTrue = []byte("true") +var jsFalse = []byte("false") + +func (s JSONSchemaPropsOrBool) MarshalJSON() ([]byte, error) { + if s.Schema != nil { + return json.Marshal(s.Schema) + } + + if s.Schema == nil && !s.Allows { + return jsFalse, nil + } + return jsTrue, nil +} + +func (s *JSONSchemaPropsOrBool) UnmarshalJSON(data []byte) error { + var nw JSONSchemaPropsOrBool + switch { + case len(data) == 0: + case data[0] == '{': + var sch JSONSchemaProps + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + case len(data) == 4 && string(data) == "true": + nw.Allows = true + case len(data) == 5 && string(data) == "false": + nw.Allows = false + default: + return errors.New("boolean or JSON schema expected") + } + *s = nw + return nil +} + +func (s JSONSchemaPropsOrStringArray) MarshalJSON() ([]byte, error) { + if len(s.Property) > 0 { + return json.Marshal(s.Property) + } + if s.Schema != nil { + return json.Marshal(s.Schema) + } + return []byte("null"), nil +} + +func (s *JSONSchemaPropsOrStringArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + var nw JSONSchemaPropsOrStringArray + if first == '{' { + var sch JSONSchemaProps + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Property); err != nil { + return err + } + } + *s = nw + return nil +} + +func (s JSONSchemaPropsOrArray) MarshalJSON() ([]byte, error) { + if len(s.JSONSchemas) > 0 { + return json.Marshal(s.JSONSchemas) + } + return json.Marshal(s.Schema) +} + +func (s *JSONSchemaPropsOrArray) UnmarshalJSON(data []byte) error { + var nw JSONSchemaPropsOrArray + var first byte + if len(data) > 1 { + first = data[0] + } + if first == '{' { + var sch JSONSchemaProps + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.JSONSchemas); err != nil { + return err + } + } + *s = nw + return nil +} + +func (s JSON) MarshalJSON() ([]byte, error) { + if len(s.Raw) > 0 { + return s.Raw, nil + } + return []byte("null"), nil + +} + +func (s *JSON) UnmarshalJSON(data []byte) error { + if len(data) > 0 && string(data) != "null" { + s.Raw = data + } + return nil +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/marshal_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/marshal_test.go new file mode 100644 index 00000000000..0c6d4d4b525 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/marshal_test.go @@ -0,0 +1,150 @@ +/* +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 v1beta1 + +import ( + "encoding/json" + "reflect" + "testing" +) + +type JSONSchemaPropsOrBoolHolder struct { + JSPoB JSONSchemaPropsOrBool `json:"val1"` + JSPoBOmitEmpty *JSONSchemaPropsOrBool `json:"val2,omitempty"` +} + +func TestJSONSchemaPropsOrBoolUnmarshalJSON(t *testing.T) { + cases := []struct { + input string + result JSONSchemaPropsOrBoolHolder + }{ + {`{}`, JSONSchemaPropsOrBoolHolder{}}, + + {`{"val1": {}}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{}}}}, + {`{"val1": {"type":"string"}}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{Type: "string"}}}}, + {`{"val1": false}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{}}}, + {`{"val1": true}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Allows: true}}}, + + {`{"val2": {}}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{}}}}, + {`{"val2": {"type":"string"}}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{Type: "string"}}}}, + {`{"val2": false}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{}}}, + {`{"val2": true}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Allows: true}}}, + } + + for _, c := range cases { + var result JSONSchemaPropsOrBoolHolder + if err := json.Unmarshal([]byte(c.input), &result); err != nil { + t.Errorf("Failed to unmarshal input '%v': %v", c.input, err) + } + if !reflect.DeepEqual(result, c.result) { + t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result) + } + } +} + +func TestStringArrayOrStringMarshalJSON(t *testing.T) { + cases := []struct { + input JSONSchemaPropsOrBoolHolder + result string + }{ + {JSONSchemaPropsOrBoolHolder{}, `{"val1":false}`}, + + {JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{}}}, `{"val1":{}}`}, + {JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":{"type":"string"}}`}, + {JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{}}, `{"val1":false}`}, + {JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Allows: true}}, `{"val1":true}`}, + + {JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{}}}, `{"val1":false,"val2":{}}`}, + {JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":false,"val2":{"type":"string"}}`}, + {JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{}}, `{"val1":false,"val2":false}`}, + {JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Allows: true}}, `{"val1":false,"val2":true}`}, + } + + for _, c := range cases { + result, err := json.Marshal(&c.input) + if err != nil { + t.Errorf("Unexpected error marshaling input '%v': %v", c.input, err) + } + if string(result) != c.result { + t.Errorf("Failed to marshal input '%v': expected: %q, got %q", c.input, c.result, string(result)) + } + } +} + +type JSONSchemaPropsOrArrayHolder struct { + JSPoA JSONSchemaPropsOrArray `json:"val1"` + JSPoAOmitEmpty *JSONSchemaPropsOrArray `json:"val2,omitempty"` +} + +func TestJSONSchemaPropsOrArrayUnmarshalJSON(t *testing.T) { + cases := []struct { + input string + result JSONSchemaPropsOrArrayHolder + }{ + {`{}`, JSONSchemaPropsOrArrayHolder{}}, + + {`{"val1": {}}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}}, + {`{"val1": {"type":"string"}}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}}, + {`{"val1": [{}]}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}}, + {`{"val1": [{},{"type":"string"}]}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}}, + + {`{"val2": {}}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}}, + {`{"val2": {"type":"string"}}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}}, + {`{"val2": [{}]}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}}, + {`{"val2": [{},{"type":"string"}]}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}}, + } + + for _, c := range cases { + var result JSONSchemaPropsOrArrayHolder + if err := json.Unmarshal([]byte(c.input), &result); err != nil { + t.Errorf("Failed to unmarshal input '%v': %v", c.input, err) + } + if !reflect.DeepEqual(result, c.result) { + t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result) + } + } +} + +func TestJSONSchemaPropsOrArrayMarshalJSON(t *testing.T) { + cases := []struct { + input JSONSchemaPropsOrArrayHolder + result string + }{ + {JSONSchemaPropsOrArrayHolder{}, `{"val1":null}`}, + + {JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}, `{"val1":{}}`}, + {JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":{"type":"string"}}`}, + {JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}, `{"val1":[{}]}`}, + {JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}, `{"val1":[{},{"type":"string"}]}`}, + + {JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{}}, `{"val1":null,"val2":null}`}, + {JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}, `{"val1":null,"val2":{}}`}, + {JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":null,"val2":{"type":"string"}}`}, + {JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}, `{"val1":null,"val2":[{}]}`}, + {JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}, `{"val1":null,"val2":[{},{"type":"string"}]}`}, + } + + for i, c := range cases { + result, err := json.Marshal(&c.input) + if err != nil { + t.Errorf("%d: Unexpected error marshaling input '%v': %v", i, c.input, err) + } + if string(result) != c.result { + t.Errorf("%d: Failed to marshal input '%v': expected: %q, got %q", i, c.input, c.result, string(result)) + } + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/register.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/register.go index 8d4c341f7c6..8ced548fa96 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/register.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/register.go @@ -38,7 +38,7 @@ func Resource(resource string) schema.GroupResource { } var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs) + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs) localSchemeBuilder = &SchemeBuilder AddToScheme = localSchemeBuilder.AddToScheme ) @@ -52,3 +52,10 @@ func addKnownTypes(scheme *runtime.Scheme) error { metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil } + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs, addConversionFuncs) +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go index 75b41065072..fe61f32151c 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go @@ -26,9 +26,12 @@ type CustomResourceDefinitionSpec struct { Version string `json:"version" protobuf:"bytes,2,opt,name=version"` // Names are the names used to describe this custom resource Names CustomResourceDefinitionNames `json:"names" protobuf:"bytes,3,opt,name=names"` - // Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced Scope ResourceScope `json:"scope" protobuf:"bytes,4,opt,name=scope,casttype=ResourceScope"` + // Validation describes the validation methods for CustomResources + // This field is alpha-level and should only be sent to servers that enable the CustomResourceValidation feature. + // +optional + Validation *CustomResourceValidation `json:"validation,omitempty" protobuf:"bytes,5,opt,name=validation"` } // CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition @@ -139,3 +142,9 @@ type CustomResourceDefinitionList struct { // Items individual CustomResourceDefinitions Items []CustomResourceDefinition `json:"items" protobuf:"bytes,2,rep,name=items"` } + +// CustomResourceValidation is a list of validation methods for CustomResources. +type CustomResourceValidation struct { + // OpenAPIV3Schema is the OpenAPI v3 schema to be validated against. + OpenAPIV3Schema *JSONSchemaProps `json:"openAPIV3Schema,omitempty" protobuf:"bytes,1,opt,name=openAPIV3Schema"` +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types_jsonschema.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types_jsonschema.go new file mode 100644 index 00000000000..9edd276d84c --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types_jsonschema.go @@ -0,0 +1,98 @@ +/* +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 v1beta1 + +// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/). +type JSONSchemaProps struct { + ID string `json:"id,omitempty" protobuf:"bytes,1,opt,name=id"` + Schema JSONSchemaURL `json:"$schema,omitempty" protobuf:"bytes,2,opt,name=schema"` + Ref *string `json:"$ref,omitempty" protobuf:"bytes,3,opt,name=ref"` + Description string `json:"description,omitempty" protobuf:"bytes,4,opt,name=description"` + Type string `json:"type,omitempty" protobuf:"bytes,5,opt,name=type"` + Format string `json:"format,omitempty" protobuf:"bytes,6,opt,name=format"` + Title string `json:"title,omitempty" protobuf:"bytes,7,opt,name=title"` + Default *JSON `json:"default,omitempty" protobuf:"bytes,8,opt,name=default"` + Maximum *float64 `json:"maximum,omitempty" protobuf:"bytes,9,opt,name=maximum"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty" protobuf:"bytes,10,opt,name=exclusiveMaximum"` + Minimum *float64 `json:"minimum,omitempty" protobuf:"bytes,11,opt,name=minimum"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty" protobuf:"bytes,12,opt,name=exclusiveMinimum"` + MaxLength *int64 `json:"maxLength,omitempty" protobuf:"bytes,13,opt,name=maxLength"` + MinLength *int64 `json:"minLength,omitempty" protobuf:"bytes,14,opt,name=minLength"` + Pattern string `json:"pattern,omitempty" protobuf:"bytes,15,opt,name=pattern"` + MaxItems *int64 `json:"maxItems,omitempty" protobuf:"bytes,16,opt,name=maxItems"` + MinItems *int64 `json:"minItems,omitempty" protobuf:"bytes,17,opt,name=minItems"` + UniqueItems bool `json:"uniqueItems,omitempty" protobuf:"bytes,18,opt,name=uniqueItems"` + MultipleOf *float64 `json:"multipleOf,omitempty" protobuf:"bytes,19,opt,name=multipleOf"` + Enum []JSON `json:"enum,omitempty" protobuf:"bytes,20,rep,name=enum"` + MaxProperties *int64 `json:"maxProperties,omitempty" protobuf:"bytes,21,opt,name=maxProperties"` + MinProperties *int64 `json:"minProperties,omitempty" protobuf:"bytes,22,opt,name=minProperties"` + Required []string `json:"required,omitempty" protobuf:"bytes,23,rep,name=required"` + Items *JSONSchemaPropsOrArray `json:"items,omitempty" protobuf:"bytes,24,opt,name=items"` + AllOf []JSONSchemaProps `json:"allOf,omitempty" protobuf:"bytes,25,rep,name=allOf"` + OneOf []JSONSchemaProps `json:"oneOf,omitempty" protobuf:"bytes,26,rep,name=oneOf"` + AnyOf []JSONSchemaProps `json:"anyOf,omitempty" protobuf:"bytes,27,rep,name=anyOf"` + Not *JSONSchemaProps `json:"not,omitempty" protobuf:"bytes,28,opt,name=not"` + Properties map[string]JSONSchemaProps `json:"properties,omitempty" protobuf:"bytes,29,rep,name=properties"` + AdditionalProperties *JSONSchemaPropsOrBool `json:"additionalProperties,omitempty" protobuf:"bytes,30,opt,name=additionalProperties"` + PatternProperties map[string]JSONSchemaProps `json:"patternProperties,omitempty" protobuf:"bytes,31,rep,name=patternProperties"` + Dependencies JSONSchemaDependencies `json:"dependencies,omitempty" protobuf:"bytes,32,opt,name=dependencies"` + AdditionalItems *JSONSchemaPropsOrBool `json:"additionalItems,omitempty" protobuf:"bytes,33,opt,name=additionalItems"` + Definitions JSONSchemaDefinitions `json:"definitions,omitempty" protobuf:"bytes,34,opt,name=definitions"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" protobuf:"bytes,35,opt,name=externalDocs"` + Example *JSON `json:"example,omitempty" protobuf:"bytes,36,opt,name=example"` +} + +// JSON represents any valid JSON value. +// These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil. +type JSON struct { + Raw []byte `protobuf:"bytes,1,opt,name=raw"` +} + +// JSONSchemaURL represents a schema url. +type JSONSchemaURL string + +// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps +// or an array of JSONSchemaProps. Mainly here for serialization purposes. +type JSONSchemaPropsOrArray struct { + Schema *JSONSchemaProps `protobuf:"bytes,1,opt,name=schema"` + JSONSchemas []JSONSchemaProps `protobuf:"bytes,2,rep,name=jSONSchemas"` +} + +// JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value. +// Defaults to true for the boolean property. +type JSONSchemaPropsOrBool struct { + Allows bool `protobuf:"varint,1,opt,name=allows"` + Schema *JSONSchemaProps `protobuf:"bytes,2,opt,name=schema"` +} + +// JSONSchemaDependencies represent a dependencies property. +type JSONSchemaDependencies map[string]JSONSchemaPropsOrStringArray + +// JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array. +type JSONSchemaPropsOrStringArray struct { + Schema *JSONSchemaProps `protobuf:"bytes,1,opt,name=schema"` + Property []string `protobuf:"bytes,2,rep,name=property"` +} + +// JSONSchemaDefinitions contains the models explicitly defined in this spec. +type JSONSchemaDefinitions map[string]JSONSchemaProps + +// ExternalDocumentation allows referencing an external resource for extended documentation. +type ExternalDocumentation struct { + Description string `json:"description,omitempty" protobuf:"bytes,1,opt,name=description"` + URL string `json:"url,omitempty" protobuf:"bytes,2,opt,name=url"` +}