Merge pull request #70211 from roycaihw/crd-multi-version-x
CRD supports multi-version Schema, Subresources and AdditionalPrintColumns
This commit is contained in:
21
api/openapi-spec/swagger.json
generated
21
api/openapi-spec/swagger.json
generated
@@ -91983,7 +91983,7 @@
|
|||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"additionalPrinterColumns": {
|
"additionalPrinterColumns": {
|
||||||
"description": "AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.",
|
"description": "AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. Optional, the global columns for all versions. Top-level and per-version columns are mutually exclusive.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition"
|
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition"
|
||||||
@@ -92006,11 +92006,11 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"subresources": {
|
"subresources": {
|
||||||
"description": "Subresources describes the subresources for CustomResources",
|
"description": "Subresources describes the subresources for CustomResource Optional, the global subresources for all versions. Top-level and per-version subresources are mutually exclusive.",
|
||||||
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources"
|
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources"
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"description": "Validation describes the validation methods for CustomResources",
|
"description": "Validation describes the validation methods for CustomResources Optional, the global validation schema for all versions. Top-level and per-version schemas are mutually exclusive.",
|
||||||
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation"
|
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation"
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
@@ -92062,10 +92062,21 @@
|
|||||||
"storage"
|
"storage"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"additionalPrinterColumns": {
|
||||||
|
"description": "AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. Top-level and per-version columns are mutually exclusive. Per-version columns must not all be set to identical values (top-level columns should be used instead) This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature. NOTE: CRDs created prior to 1.13 populated the top-level additionalPrinterColumns field by default. To apply an update that changes to per-version additionalPrinterColumns, the top-level additionalPrinterColumns field must be explicitly set to null",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition"
|
||||||
|
}
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"description": "Name is the version name, e.g. “v1”, “v2beta1”, etc.",
|
"description": "Name is the version name, e.g. “v1”, “v2beta1”, etc.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"schema": {
|
||||||
|
"description": "Schema describes the schema for CustomResource used in validation, pruning, and defaulting. Top-level and per-version schemas are mutually exclusive. Per-version schemas must not all be set to identical values (top-level validation schema should be used instead) This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.",
|
||||||
|
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation"
|
||||||
|
},
|
||||||
"served": {
|
"served": {
|
||||||
"description": "Served is a flag enabling/disabling this version from being served via REST APIs",
|
"description": "Served is a flag enabling/disabling this version from being served via REST APIs",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@@ -92073,6 +92084,10 @@
|
|||||||
"storage": {
|
"storage": {
|
||||||
"description": "Storage flags the version as storage version. There must be exactly one flagged as storage version.",
|
"description": "Storage flags the version as storage version. There must be exactly one flagged as storage version.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"subresources": {
|
||||||
|
"description": "Subresources describes the subresources for CustomResource Top-level and per-version subresources are mutually exclusive. Per-version subresources must not all be set to identical values (top-level subresources should be used instead) This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.",
|
||||||
|
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -2442,6 +2442,10 @@
|
|||||||
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
|
"ImportPath": "k8s.io/apiserver/pkg/util/feature",
|
||||||
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "k8s.io/apiserver/pkg/util/feature/testing",
|
||||||
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apiserver/pkg/util/logs",
|
"ImportPath": "k8s.io/apiserver/pkg/util/logs",
|
||||||
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
@@ -44,8 +44,14 @@ type CustomResourceDefinitionSpec struct {
|
|||||||
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
|
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
|
||||||
Scope ResourceScope
|
Scope ResourceScope
|
||||||
// Validation describes the validation methods for CustomResources
|
// Validation describes the validation methods for CustomResources
|
||||||
|
// Optional, the global validation schema for all versions.
|
||||||
|
// Top-level and per-version schemas are mutually exclusive.
|
||||||
|
// +optional
|
||||||
Validation *CustomResourceValidation
|
Validation *CustomResourceValidation
|
||||||
// Subresources describes the subresources for CustomResources
|
// Subresources describes the subresources for CustomResource
|
||||||
|
// Optional, the global subresources for all versions.
|
||||||
|
// Top-level and per-version subresources are mutually exclusive.
|
||||||
|
// +optional
|
||||||
Subresources *CustomResourceSubresources
|
Subresources *CustomResourceSubresources
|
||||||
// Versions is the list of all supported versions for this resource.
|
// Versions is the list of all supported versions for this resource.
|
||||||
// If Version field is provided, this field is optional.
|
// If Version field is provided, this field is optional.
|
||||||
@@ -60,6 +66,9 @@ type CustomResourceDefinitionSpec struct {
|
|||||||
// v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.
|
// v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.
|
||||||
Versions []CustomResourceDefinitionVersion
|
Versions []CustomResourceDefinitionVersion
|
||||||
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||||
|
// Optional, the global columns for all versions.
|
||||||
|
// Top-level and per-version columns are mutually exclusive.
|
||||||
|
// +optional
|
||||||
AdditionalPrinterColumns []CustomResourceColumnDefinition
|
AdditionalPrinterColumns []CustomResourceColumnDefinition
|
||||||
|
|
||||||
// `conversion` defines conversion settings for the CRD.
|
// `conversion` defines conversion settings for the CRD.
|
||||||
@@ -149,6 +158,27 @@ type CustomResourceDefinitionVersion struct {
|
|||||||
// Storage flags the version as storage version. There must be exactly one flagged
|
// Storage flags the version as storage version. There must be exactly one flagged
|
||||||
// as storage version.
|
// as storage version.
|
||||||
Storage bool
|
Storage bool
|
||||||
|
// Schema describes the schema for CustomResource used in validation, pruning, and defaulting.
|
||||||
|
// Top-level and per-version schemas are mutually exclusive.
|
||||||
|
// Per-version schemas must not all be set to identical values (top-level validation schema should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// +optional
|
||||||
|
Schema *CustomResourceValidation
|
||||||
|
// Subresources describes the subresources for CustomResource
|
||||||
|
// Top-level and per-version subresources are mutually exclusive.
|
||||||
|
// Per-version subresources must not all be set to identical values (top-level subresources should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// +optional
|
||||||
|
Subresources *CustomResourceSubresources
|
||||||
|
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||||
|
// Top-level and per-version columns are mutually exclusive.
|
||||||
|
// Per-version columns must not all be set to identical values (top-level columns should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// NOTE: CRDs created prior to 1.13 populated the top-level additionalPrinterColumns field by default. To apply an
|
||||||
|
// update that changes to per-version additionalPrinterColumns, the top-level additionalPrinterColumns field must
|
||||||
|
// be explicitly set to null
|
||||||
|
// +optional
|
||||||
|
AdditionalPrinterColumns []CustomResourceColumnDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomResourceColumnDefinition specifies a column for server side printing.
|
// CustomResourceColumnDefinition specifies a column for server side printing.
|
||||||
|
@@ -19,12 +19,9 @@ package v1beta1
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
|
|
||||||
|
|
||||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||||
scheme.AddTypeDefaultingFunc(&CustomResourceDefinition{}, func(obj interface{}) { SetDefaults_CustomResourceDefinition(obj.(*CustomResourceDefinition)) })
|
scheme.AddTypeDefaultingFunc(&CustomResourceDefinition{}, func(obj interface{}) { SetDefaults_CustomResourceDefinition(obj.(*CustomResourceDefinition)) })
|
||||||
// TODO figure out why I can't seem to get my defaulter generated
|
// TODO figure out why I can't seem to get my defaulter generated
|
||||||
@@ -66,14 +63,19 @@ func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec)
|
|||||||
if len(obj.Version) == 0 && len(obj.Versions) != 0 {
|
if len(obj.Version) == 0 && len(obj.Versions) != 0 {
|
||||||
obj.Version = obj.Versions[0].Name
|
obj.Version = obj.Versions[0].Name
|
||||||
}
|
}
|
||||||
if len(obj.AdditionalPrinterColumns) == 0 {
|
|
||||||
obj.AdditionalPrinterColumns = []CustomResourceColumnDefinition{
|
|
||||||
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if obj.Conversion == nil {
|
if obj.Conversion == nil {
|
||||||
obj.Conversion = &CustomResourceConversion{
|
obj.Conversion = &CustomResourceConversion{
|
||||||
Strategy: NoneConverter,
|
Strategy: NoneConverter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasPerVersionColumns returns true if a CRD uses per-version columns.
|
||||||
|
func hasPerVersionColumns(versions []CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if len(v.AdditionalPrinterColumns) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -785,6 +785,38 @@ func (m *CustomResourceDefinitionVersion) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0
|
dAtA[i] = 0
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
|
if m.Schema != nil {
|
||||||
|
dAtA[i] = 0x22
|
||||||
|
i++
|
||||||
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
||||||
|
n15, err := m.Schema.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n15
|
||||||
|
}
|
||||||
|
if m.Subresources != nil {
|
||||||
|
dAtA[i] = 0x2a
|
||||||
|
i++
|
||||||
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Subresources.Size()))
|
||||||
|
n16, err := m.Subresources.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n16
|
||||||
|
}
|
||||||
|
if len(m.AdditionalPrinterColumns) > 0 {
|
||||||
|
for _, msg := range m.AdditionalPrinterColumns {
|
||||||
|
dAtA[i] = 0x32
|
||||||
|
i++
|
||||||
|
i = encodeVarintGenerated(dAtA, i, uint64(msg.Size()))
|
||||||
|
n, err := msg.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,21 +889,21 @@ func (m *CustomResourceSubresources) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Status.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Status.Size()))
|
||||||
n15, err := m.Status.MarshalTo(dAtA[i:])
|
n17, err := m.Status.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n15
|
i += n17
|
||||||
}
|
}
|
||||||
if m.Scale != nil {
|
if m.Scale != nil {
|
||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Scale.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Scale.Size()))
|
||||||
n16, err := m.Scale.MarshalTo(dAtA[i:])
|
n18, err := m.Scale.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n16
|
i += n18
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@@ -895,11 +927,11 @@ func (m *CustomResourceValidation) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.OpenAPIV3Schema.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.OpenAPIV3Schema.Size()))
|
||||||
n17, err := m.OpenAPIV3Schema.MarshalTo(dAtA[i:])
|
n19, err := m.OpenAPIV3Schema.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n17
|
i += n19
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@@ -1003,11 +1035,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x42
|
dAtA[i] = 0x42
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Default.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Default.Size()))
|
||||||
n18, err := m.Default.MarshalTo(dAtA[i:])
|
n20, err := m.Default.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n18
|
i += n20
|
||||||
}
|
}
|
||||||
if m.Maximum != nil {
|
if m.Maximum != nil {
|
||||||
dAtA[i] = 0x49
|
dAtA[i] = 0x49
|
||||||
@@ -1134,11 +1166,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x1
|
dAtA[i] = 0x1
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Items.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Items.Size()))
|
||||||
n19, err := m.Items.MarshalTo(dAtA[i:])
|
n21, err := m.Items.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n19
|
i += n21
|
||||||
}
|
}
|
||||||
if len(m.AllOf) > 0 {
|
if len(m.AllOf) > 0 {
|
||||||
for _, msg := range m.AllOf {
|
for _, msg := range m.AllOf {
|
||||||
@@ -1188,11 +1220,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x1
|
dAtA[i] = 0x1
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Not.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Not.Size()))
|
||||||
n20, err := m.Not.MarshalTo(dAtA[i:])
|
n22, err := m.Not.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n20
|
i += n22
|
||||||
}
|
}
|
||||||
if len(m.Properties) > 0 {
|
if len(m.Properties) > 0 {
|
||||||
keysForProperties := make([]string, 0, len(m.Properties))
|
keysForProperties := make([]string, 0, len(m.Properties))
|
||||||
@@ -1220,11 +1252,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
||||||
n21, err := (&v).MarshalTo(dAtA[i:])
|
n23, err := (&v).MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n21
|
i += n23
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.AdditionalProperties != nil {
|
if m.AdditionalProperties != nil {
|
||||||
@@ -1233,11 +1265,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x1
|
dAtA[i] = 0x1
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.AdditionalProperties.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.AdditionalProperties.Size()))
|
||||||
n22, err := m.AdditionalProperties.MarshalTo(dAtA[i:])
|
n24, err := m.AdditionalProperties.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n22
|
i += n24
|
||||||
}
|
}
|
||||||
if len(m.PatternProperties) > 0 {
|
if len(m.PatternProperties) > 0 {
|
||||||
keysForPatternProperties := make([]string, 0, len(m.PatternProperties))
|
keysForPatternProperties := make([]string, 0, len(m.PatternProperties))
|
||||||
@@ -1265,11 +1297,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
||||||
n23, err := (&v).MarshalTo(dAtA[i:])
|
n25, err := (&v).MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n23
|
i += n25
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(m.Dependencies) > 0 {
|
if len(m.Dependencies) > 0 {
|
||||||
@@ -1298,11 +1330,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
||||||
n24, err := (&v).MarshalTo(dAtA[i:])
|
n26, err := (&v).MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n24
|
i += n26
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.AdditionalItems != nil {
|
if m.AdditionalItems != nil {
|
||||||
@@ -1311,11 +1343,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.AdditionalItems.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.AdditionalItems.Size()))
|
||||||
n25, err := m.AdditionalItems.MarshalTo(dAtA[i:])
|
n27, err := m.AdditionalItems.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n25
|
i += n27
|
||||||
}
|
}
|
||||||
if len(m.Definitions) > 0 {
|
if len(m.Definitions) > 0 {
|
||||||
keysForDefinitions := make([]string, 0, len(m.Definitions))
|
keysForDefinitions := make([]string, 0, len(m.Definitions))
|
||||||
@@ -1343,11 +1375,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64((&v).Size()))
|
||||||
n26, err := (&v).MarshalTo(dAtA[i:])
|
n28, err := (&v).MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n26
|
i += n28
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.ExternalDocs != nil {
|
if m.ExternalDocs != nil {
|
||||||
@@ -1356,11 +1388,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.ExternalDocs.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.ExternalDocs.Size()))
|
||||||
n27, err := m.ExternalDocs.MarshalTo(dAtA[i:])
|
n29, err := m.ExternalDocs.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n27
|
i += n29
|
||||||
}
|
}
|
||||||
if m.Example != nil {
|
if m.Example != nil {
|
||||||
dAtA[i] = 0xa2
|
dAtA[i] = 0xa2
|
||||||
@@ -1368,11 +1400,11 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Example.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Example.Size()))
|
||||||
n28, err := m.Example.MarshalTo(dAtA[i:])
|
n30, err := m.Example.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n28
|
i += n30
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@@ -1396,11 +1428,11 @@ func (m *JSONSchemaPropsOrArray) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
||||||
n29, err := m.Schema.MarshalTo(dAtA[i:])
|
n31, err := m.Schema.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n29
|
i += n31
|
||||||
}
|
}
|
||||||
if len(m.JSONSchemas) > 0 {
|
if len(m.JSONSchemas) > 0 {
|
||||||
for _, msg := range m.JSONSchemas {
|
for _, msg := range m.JSONSchemas {
|
||||||
@@ -1444,11 +1476,11 @@ func (m *JSONSchemaPropsOrBool) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
||||||
n30, err := m.Schema.MarshalTo(dAtA[i:])
|
n32, err := m.Schema.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n30
|
i += n32
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@@ -1472,11 +1504,11 @@ func (m *JSONSchemaPropsOrStringArray) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Schema.Size()))
|
||||||
n31, err := m.Schema.MarshalTo(dAtA[i:])
|
n33, err := m.Schema.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n31
|
i += n33
|
||||||
}
|
}
|
||||||
if len(m.Property) > 0 {
|
if len(m.Property) > 0 {
|
||||||
for _, s := range m.Property {
|
for _, s := range m.Property {
|
||||||
@@ -1547,11 +1579,11 @@ func (m *WebhookClientConfig) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintGenerated(dAtA, i, uint64(m.Service.Size()))
|
i = encodeVarintGenerated(dAtA, i, uint64(m.Service.Size()))
|
||||||
n32, err := m.Service.MarshalTo(dAtA[i:])
|
n34, err := m.Service.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n32
|
i += n34
|
||||||
}
|
}
|
||||||
if m.CABundle != nil {
|
if m.CABundle != nil {
|
||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
@@ -1785,6 +1817,20 @@ func (m *CustomResourceDefinitionVersion) Size() (n int) {
|
|||||||
n += 1 + l + sovGenerated(uint64(l))
|
n += 1 + l + sovGenerated(uint64(l))
|
||||||
n += 2
|
n += 2
|
||||||
n += 2
|
n += 2
|
||||||
|
if m.Schema != nil {
|
||||||
|
l = m.Schema.Size()
|
||||||
|
n += 1 + l + sovGenerated(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Subresources != nil {
|
||||||
|
l = m.Subresources.Size()
|
||||||
|
n += 1 + l + sovGenerated(uint64(l))
|
||||||
|
}
|
||||||
|
if len(m.AdditionalPrinterColumns) > 0 {
|
||||||
|
for _, e := range m.AdditionalPrinterColumns {
|
||||||
|
l = e.Size()
|
||||||
|
n += 1 + l + sovGenerated(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2239,6 +2285,9 @@ func (this *CustomResourceDefinitionVersion) String() string {
|
|||||||
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
|
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
|
||||||
`Served:` + fmt.Sprintf("%v", this.Served) + `,`,
|
`Served:` + fmt.Sprintf("%v", this.Served) + `,`,
|
||||||
`Storage:` + fmt.Sprintf("%v", this.Storage) + `,`,
|
`Storage:` + fmt.Sprintf("%v", this.Storage) + `,`,
|
||||||
|
`Schema:` + strings.Replace(fmt.Sprintf("%v", this.Schema), "CustomResourceValidation", "CustomResourceValidation", 1) + `,`,
|
||||||
|
`Subresources:` + strings.Replace(fmt.Sprintf("%v", this.Subresources), "CustomResourceSubresources", "CustomResourceSubresources", 1) + `,`,
|
||||||
|
`AdditionalPrinterColumns:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.AdditionalPrinterColumns), "CustomResourceColumnDefinition", "CustomResourceColumnDefinition", 1), `&`, ``, 1) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
@@ -4414,6 +4463,103 @@ func (m *CustomResourceDefinitionVersion) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.Storage = bool(v != 0)
|
m.Storage = bool(v != 0)
|
||||||
|
case 4:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Schema", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowGenerated
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthGenerated
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Schema == nil {
|
||||||
|
m.Schema = &CustomResourceValidation{}
|
||||||
|
}
|
||||||
|
if err := m.Schema.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 5:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Subresources", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowGenerated
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthGenerated
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Subresources == nil {
|
||||||
|
m.Subresources = &CustomResourceSubresources{}
|
||||||
|
}
|
||||||
|
if err := m.Subresources.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 6:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field AdditionalPrinterColumns", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowGenerated
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthGenerated
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.AdditionalPrinterColumns = append(m.AdditionalPrinterColumns, CustomResourceColumnDefinition{})
|
||||||
|
if err := m.AdditionalPrinterColumns[len(m.AdditionalPrinterColumns)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||||
@@ -7107,175 +7253,177 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptorGenerated = []byte{
|
var fileDescriptorGenerated = []byte{
|
||||||
// 2711 bytes of a gzipped FileDescriptorProto
|
// 2740 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0xdd, 0x6f, 0x24, 0x47,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x73, 0x1c, 0x47,
|
||||||
0x11, 0xbf, 0xd9, 0xf5, 0xda, 0xeb, 0xb6, 0x7d, 0xb6, 0xfb, 0xe2, 0xcb, 0x9c, 0xb9, 0xec, 0xda,
|
0x15, 0xf7, 0xec, 0x6a, 0xa5, 0x55, 0x4b, 0xb2, 0xa4, 0xb6, 0xe5, 0x8c, 0x85, 0xbd, 0x2b, 0xad,
|
||||||
0x1b, 0x12, 0x99, 0x70, 0xb7, 0xce, 0x1d, 0x09, 0x09, 0x91, 0x78, 0xf0, 0xda, 0x4e, 0xe4, 0x70,
|
0x49, 0x4a, 0x04, 0x7b, 0x15, 0x9b, 0x84, 0x84, 0x54, 0x71, 0xd0, 0x4a, 0x4a, 0x4a, 0xc6, 0xfa,
|
||||||
0x3e, 0x9b, 0xde, 0xbb, 0x0b, 0x90, 0xcf, 0xf6, 0x4c, 0xef, 0x7a, 0xce, 0xf3, 0x75, 0xd3, 0x33,
|
0xa0, 0xd7, 0x76, 0x80, 0x7c, 0xb6, 0x66, 0x7b, 0x57, 0x63, 0xcd, 0x97, 0xa7, 0x67, 0x56, 0x52,
|
||||||
0x6b, 0x5b, 0x7c, 0x08, 0x12, 0x45, 0x20, 0x04, 0x04, 0x91, 0x13, 0x12, 0x02, 0x1e, 0x00, 0xf1,
|
0x05, 0x28, 0x48, 0x2a, 0x05, 0x45, 0x01, 0xa1, 0x88, 0x2f, 0x14, 0x70, 0x00, 0x8a, 0x0b, 0x45,
|
||||||
0x82, 0x10, 0x3c, 0xc0, 0x1b, 0xfc, 0x01, 0xf7, 0x18, 0xf1, 0x94, 0xa7, 0x15, 0xb7, 0xfc, 0x0b,
|
0xc1, 0x01, 0x6e, 0xf0, 0x07, 0xf8, 0x98, 0xe2, 0x94, 0xd3, 0x16, 0xde, 0xfc, 0x0b, 0x54, 0x51,
|
||||||
0x48, 0x48, 0x7e, 0x42, 0xfd, 0x31, 0x3d, 0xb3, 0xb3, 0xbb, 0x39, 0x2b, 0xb7, 0x9b, 0x7b, 0xf3,
|
0xa5, 0x13, 0xd5, 0x1f, 0xd3, 0x33, 0x3b, 0xbb, 0x6b, 0xab, 0xe2, 0xdd, 0x98, 0x9b, 0xe6, 0xbd,
|
||||||
0x54, 0x55, 0xd7, 0xaf, 0xba, 0xba, 0xaa, 0xba, 0xaa, 0xd7, 0xa0, 0x71, 0xf0, 0x22, 0xad, 0x5a,
|
0xd7, 0xef, 0xf7, 0xfa, 0xf5, 0x7b, 0xaf, 0x5f, 0xbf, 0x15, 0xa8, 0xef, 0xbf, 0x44, 0xcb, 0xa6,
|
||||||
0xde, 0xea, 0x41, 0xb4, 0x47, 0x02, 0x97, 0x84, 0x84, 0xae, 0xb6, 0x88, 0x6b, 0x7a, 0xc1, 0xaa,
|
0xbb, 0xbc, 0x1f, 0xee, 0x12, 0xdf, 0x21, 0x01, 0xa1, 0xcb, 0x4d, 0xe2, 0xd4, 0x5c, 0x7f, 0x59,
|
||||||
0x64, 0x60, 0xdf, 0x22, 0x47, 0x21, 0x71, 0xa9, 0xe5, 0xb9, 0xf4, 0x32, 0xf6, 0x2d, 0x4a, 0x82,
|
0x32, 0xb0, 0x67, 0x92, 0xc3, 0x80, 0x38, 0xd4, 0x74, 0x1d, 0x7a, 0x05, 0x7b, 0x26, 0x25, 0x7e,
|
||||||
0x16, 0x09, 0x56, 0xfd, 0x83, 0x26, 0xe3, 0xd1, 0x6e, 0x81, 0xd5, 0xd6, 0x95, 0x3d, 0x12, 0xe2,
|
0x93, 0xf8, 0xcb, 0xde, 0x7e, 0x83, 0xf1, 0x68, 0xa7, 0xc0, 0x72, 0xf3, 0xea, 0x2e, 0x09, 0xf0,
|
||||||
0x2b, 0xab, 0x4d, 0xe2, 0x92, 0x00, 0x87, 0xc4, 0xac, 0xfa, 0x81, 0x17, 0x7a, 0xf0, 0xab, 0x42,
|
0xd5, 0xe5, 0x06, 0x71, 0x88, 0x8f, 0x03, 0x52, 0x2b, 0x7b, 0xbe, 0x1b, 0xb8, 0xf0, 0xeb, 0x42,
|
||||||
0x5d, 0xb5, 0x4b, 0xfa, 0x6d, 0xa5, 0xae, 0xea, 0x1f, 0x34, 0x19, 0x8f, 0x76, 0x0b, 0x54, 0xa5,
|
0x5d, 0xb9, 0x43, 0xfa, 0x6d, 0xa5, 0xae, 0xec, 0xed, 0x37, 0x18, 0x8f, 0x76, 0x0a, 0x94, 0xa5,
|
||||||
0xba, 0xc5, 0xcb, 0x4d, 0x2b, 0xdc, 0x8f, 0xf6, 0xaa, 0x86, 0xe7, 0xac, 0x36, 0xbd, 0xa6, 0xb7,
|
0xba, 0xf9, 0x2b, 0x0d, 0x33, 0xd8, 0x0b, 0x77, 0xcb, 0x86, 0x6b, 0x2f, 0x37, 0xdc, 0x86, 0xbb,
|
||||||
0xca, 0xb5, 0xee, 0x45, 0x0d, 0xfe, 0xc5, 0x3f, 0xf8, 0x5f, 0x02, 0x6d, 0xf1, 0xb9, 0xc4, 0x78,
|
0xcc, 0xb5, 0xee, 0x86, 0x75, 0xfe, 0xc5, 0x3f, 0xf8, 0x5f, 0x02, 0x6d, 0xfe, 0xf9, 0xd8, 0x78,
|
||||||
0x07, 0x1b, 0xfb, 0x96, 0x4b, 0x82, 0xe3, 0xc4, 0x62, 0x87, 0x84, 0x78, 0xb5, 0xd5, 0x63, 0xe3,
|
0x1b, 0x1b, 0x7b, 0xa6, 0x43, 0xfc, 0xa3, 0xd8, 0x62, 0x9b, 0x04, 0x78, 0xb9, 0xd9, 0x65, 0xe3,
|
||||||
0xe2, 0xea, 0xa0, 0x55, 0x41, 0xe4, 0x86, 0x96, 0x43, 0x7a, 0x16, 0x7c, 0xf9, 0x41, 0x0b, 0xa8,
|
0xfc, 0x72, 0xbf, 0x55, 0x7e, 0xe8, 0x04, 0xa6, 0x4d, 0xba, 0x16, 0x7c, 0xf5, 0x51, 0x0b, 0xa8,
|
||||||
0xb1, 0x4f, 0x1c, 0x9c, 0x5d, 0x57, 0x39, 0xd1, 0xc0, 0xfc, 0xba, 0xe7, 0xb6, 0x48, 0xc0, 0x76,
|
0xb1, 0x47, 0x6c, 0x9c, 0x5e, 0x57, 0x3a, 0xd6, 0xc0, 0xec, 0xaa, 0xeb, 0x34, 0x89, 0xcf, 0x76,
|
||||||
0x89, 0xc8, 0x9d, 0x88, 0xd0, 0x10, 0xd6, 0x40, 0x3e, 0xb2, 0x4c, 0x5d, 0x5b, 0xd2, 0x56, 0x26,
|
0x89, 0xc8, 0xdd, 0x90, 0xd0, 0x00, 0x56, 0x40, 0x36, 0x34, 0x6b, 0xba, 0xb6, 0xa0, 0x2d, 0x8d,
|
||||||
0x6b, 0xcf, 0xde, 0x6b, 0x97, 0xcf, 0x74, 0xda, 0xe5, 0xfc, 0xcd, 0xad, 0x8d, 0x93, 0x76, 0x79,
|
0x57, 0x9e, 0xbb, 0xdf, 0x2a, 0x9e, 0x6a, 0xb7, 0x8a, 0xd9, 0x5b, 0x1b, 0x6b, 0xc7, 0xad, 0xe2,
|
||||||
0x79, 0x10, 0x52, 0x78, 0xec, 0x13, 0x5a, 0xbd, 0xb9, 0xb5, 0x81, 0xd8, 0x62, 0xf8, 0x0a, 0x98,
|
0x62, 0x3f, 0xa4, 0xe0, 0xc8, 0x23, 0xb4, 0x7c, 0x6b, 0x63, 0x0d, 0xb1, 0xc5, 0xf0, 0x55, 0x30,
|
||||||
0x37, 0x09, 0xb5, 0x02, 0x62, 0xae, 0xed, 0x6e, 0xdd, 0x12, 0xfa, 0xf5, 0x1c, 0xd7, 0x78, 0x41,
|
0x5b, 0x23, 0xd4, 0xf4, 0x49, 0x6d, 0x65, 0x67, 0xe3, 0xb6, 0xd0, 0xaf, 0x67, 0xb8, 0xc6, 0xf3,
|
||||||
0x6a, 0x9c, 0xdf, 0xc8, 0x0a, 0xa0, 0xde, 0x35, 0xf0, 0x1b, 0x60, 0xc2, 0xdb, 0xbb, 0x4d, 0x8c,
|
0x52, 0xe3, 0xec, 0x5a, 0x5a, 0x00, 0x75, 0xaf, 0x81, 0xdf, 0x02, 0x63, 0xee, 0xee, 0x1d, 0x62,
|
||||||
0x90, 0xea, 0xf9, 0xa5, 0xfc, 0xca, 0xd4, 0xd5, 0xcb, 0xd5, 0xe4, 0x04, 0x95, 0x09, 0xfc, 0xd8,
|
0x04, 0x54, 0xcf, 0x2e, 0x64, 0x97, 0x26, 0xae, 0x5d, 0x29, 0xc7, 0x27, 0xa8, 0x4c, 0xe0, 0xc7,
|
||||||
0xe4, 0x66, 0xab, 0x08, 0x1f, 0x6e, 0xc6, 0x27, 0x57, 0x9b, 0x95, 0x68, 0x13, 0x3b, 0x42, 0x0b,
|
0x26, 0x37, 0x5b, 0x46, 0xf8, 0x60, 0x3d, 0x3a, 0xb9, 0xca, 0xb4, 0x44, 0x1b, 0xdb, 0x16, 0x5a,
|
||||||
0x8a, 0xd5, 0x55, 0xfe, 0x90, 0x03, 0x30, 0xbd, 0x79, 0xea, 0x7b, 0x2e, 0x25, 0x43, 0xd9, 0x3d,
|
0x50, 0xa4, 0xae, 0xf4, 0x87, 0x0c, 0x80, 0xc9, 0xcd, 0x53, 0xcf, 0x75, 0x28, 0x19, 0xc8, 0xee,
|
||||||
0x05, 0x73, 0x06, 0xd7, 0x1c, 0x12, 0x53, 0xe2, 0xea, 0xb9, 0x4f, 0x63, 0xbd, 0x2e, 0xf1, 0xe7,
|
0x29, 0x98, 0x31, 0xb8, 0xe6, 0x80, 0xd4, 0x24, 0xae, 0x9e, 0xf9, 0x2c, 0xd6, 0xeb, 0x12, 0x7f,
|
||||||
0xd6, 0x33, 0xea, 0x50, 0x0f, 0x00, 0xbc, 0x01, 0xc6, 0x03, 0x42, 0x23, 0x3b, 0xd4, 0xf3, 0x4b,
|
0x66, 0x35, 0xa5, 0x0e, 0x75, 0x01, 0xc0, 0x9b, 0x60, 0xd4, 0x27, 0x34, 0xb4, 0x02, 0x3d, 0xbb,
|
||||||
0xda, 0xca, 0xd4, 0xd5, 0x4b, 0x03, 0xa1, 0x78, 0x7c, 0xb3, 0xe0, 0xab, 0xb6, 0xae, 0x54, 0xeb,
|
0xa0, 0x2d, 0x4d, 0x5c, 0xbb, 0xdc, 0x17, 0x8a, 0xc7, 0x37, 0x0b, 0xbe, 0x72, 0xf3, 0x6a, 0xb9,
|
||||||
0x21, 0x0e, 0x23, 0x5a, 0x3b, 0x2b, 0x91, 0xc6, 0x11, 0xd7, 0x81, 0xa4, 0xae, 0xca, 0x8f, 0x72,
|
0x1a, 0xe0, 0x20, 0xa4, 0x95, 0xd3, 0x12, 0x69, 0x14, 0x71, 0x1d, 0x48, 0xea, 0x2a, 0xfd, 0x38,
|
||||||
0x60, 0x2e, 0xed, 0xa5, 0x96, 0x45, 0x0e, 0xe1, 0x21, 0x98, 0x08, 0x44, 0xb0, 0x70, 0x3f, 0x4d,
|
0x03, 0x66, 0x92, 0x5e, 0x6a, 0x9a, 0xe4, 0x00, 0x1e, 0x80, 0x31, 0x5f, 0x04, 0x0b, 0xf7, 0xd3,
|
||||||
0x5d, 0xdd, 0xad, 0x3e, 0x54, 0x5a, 0x55, 0x7b, 0x82, 0xb0, 0x36, 0xc5, 0xce, 0x4c, 0x7e, 0xa0,
|
0xc4, 0xb5, 0x9d, 0xf2, 0x63, 0xa5, 0x55, 0xb9, 0x2b, 0x08, 0x2b, 0x13, 0xec, 0xcc, 0xe4, 0x07,
|
||||||
0x18, 0x0d, 0x7e, 0x1b, 0x14, 0x03, 0x79, 0x50, 0x3c, 0x9a, 0xa6, 0xae, 0x7e, 0x7d, 0x88, 0xc8,
|
0x8a, 0xd0, 0xe0, 0xbb, 0x20, 0xef, 0xcb, 0x83, 0xe2, 0xd1, 0x34, 0x71, 0xed, 0x9b, 0x03, 0x44,
|
||||||
0x42, 0x71, 0x6d, 0xba, 0xd3, 0x2e, 0x17, 0xe3, 0x2f, 0xa4, 0x00, 0x2b, 0x1f, 0xe6, 0x40, 0x69,
|
0x16, 0x8a, 0x2b, 0x93, 0xed, 0x56, 0x31, 0x1f, 0x7d, 0x21, 0x05, 0x58, 0xfa, 0x28, 0x03, 0x0a,
|
||||||
0x3d, 0xa2, 0xa1, 0xe7, 0x20, 0x42, 0xbd, 0x28, 0x30, 0xc8, 0xba, 0x67, 0x47, 0x8e, 0xbb, 0x41,
|
0xab, 0x21, 0x0d, 0x5c, 0x1b, 0x11, 0xea, 0x86, 0xbe, 0x41, 0x56, 0x5d, 0x2b, 0xb4, 0x9d, 0x35,
|
||||||
0x1a, 0x96, 0x6b, 0x85, 0x2c, 0x5a, 0x97, 0xc0, 0x98, 0x8b, 0x1d, 0x22, 0xa3, 0x67, 0x5a, 0xfa,
|
0x52, 0x37, 0x1d, 0x33, 0x60, 0xd1, 0xba, 0x00, 0x46, 0x1c, 0x6c, 0x13, 0x19, 0x3d, 0x93, 0xd2,
|
||||||
0x74, 0xec, 0x3a, 0x76, 0x08, 0xe2, 0x1c, 0x26, 0xc1, 0x82, 0x45, 0xe6, 0x82, 0x92, 0xb8, 0x71,
|
0xa7, 0x23, 0x5b, 0xd8, 0x26, 0x88, 0x73, 0x98, 0x04, 0x0b, 0x16, 0x99, 0x0b, 0x4a, 0xe2, 0xe6,
|
||||||
0xec, 0x13, 0xc4, 0x39, 0xf0, 0x69, 0x30, 0xde, 0xf0, 0x02, 0x07, 0x8b, 0x73, 0x9c, 0x4c, 0x4e,
|
0x91, 0x47, 0x10, 0xe7, 0xc0, 0x67, 0xc0, 0x68, 0xdd, 0xf5, 0x6d, 0x2c, 0xce, 0x71, 0x3c, 0x3e,
|
||||||
0xe6, 0x65, 0x4e, 0x45, 0x92, 0x0b, 0x9f, 0x07, 0x53, 0x26, 0xa1, 0x46, 0x60, 0xf9, 0x0c, 0x5a,
|
0x99, 0x57, 0x38, 0x15, 0x49, 0x2e, 0x7c, 0x01, 0x4c, 0xd4, 0x08, 0x35, 0x7c, 0xd3, 0x63, 0xd0,
|
||||||
0x1f, 0xe3, 0xc2, 0xe7, 0xa4, 0xf0, 0xd4, 0x46, 0xc2, 0x42, 0x69, 0x39, 0x78, 0x09, 0x14, 0xfd,
|
0xfa, 0x08, 0x17, 0x3e, 0x23, 0x85, 0x27, 0xd6, 0x62, 0x16, 0x4a, 0xca, 0xc1, 0xcb, 0x20, 0xef,
|
||||||
0xc0, 0xf2, 0x02, 0x2b, 0x3c, 0xd6, 0x0b, 0x4b, 0xda, 0x4a, 0xa1, 0x36, 0x27, 0xd7, 0x14, 0x77,
|
0xf9, 0xa6, 0xeb, 0x9b, 0xc1, 0x91, 0x9e, 0x5b, 0xd0, 0x96, 0x72, 0x95, 0x19, 0xb9, 0x26, 0xbf,
|
||||||
0x25, 0x1d, 0x29, 0x09, 0xb8, 0x04, 0x8a, 0xaf, 0xd6, 0x77, 0xae, 0xef, 0xe2, 0x70, 0x5f, 0x1f,
|
0x23, 0xe9, 0x48, 0x49, 0xc0, 0x05, 0x90, 0xbf, 0x5e, 0xdd, 0xde, 0xda, 0xc1, 0xc1, 0x9e, 0x3e,
|
||||||
0xe7, 0x08, 0x63, 0x4c, 0x1a, 0x15, 0x6f, 0x4b, 0x6a, 0xe5, 0xdd, 0x1c, 0xd0, 0xb3, 0x5e, 0x89,
|
0xca, 0x11, 0x46, 0x98, 0x34, 0xca, 0xdf, 0x91, 0xd4, 0xd2, 0x7b, 0x19, 0xa0, 0xa7, 0xbd, 0x12,
|
||||||
0x5d, 0x0a, 0x5f, 0x06, 0x45, 0x1a, 0xb2, 0x8a, 0xd3, 0x3c, 0x96, 0x3e, 0x79, 0x26, 0x06, 0xab,
|
0xb9, 0x14, 0xbe, 0x02, 0xf2, 0x34, 0x60, 0x15, 0xa7, 0x71, 0x24, 0x7d, 0xf2, 0x6c, 0x04, 0x56,
|
||||||
0x4b, 0xfa, 0x49, 0xbb, 0x7c, 0x3e, 0x59, 0x11, 0x53, 0xb9, 0x3f, 0xd4, 0x5a, 0xf8, 0x5b, 0x0d,
|
0x95, 0xf4, 0xe3, 0x56, 0xf1, 0x5c, 0xbc, 0x22, 0xa2, 0x72, 0x7f, 0xa8, 0xb5, 0xf0, 0xb7, 0x1a,
|
||||||
0x9c, 0x3b, 0x24, 0x7b, 0xfb, 0x9e, 0x77, 0xb0, 0x6e, 0x5b, 0xc4, 0x0d, 0xd7, 0x3d, 0xb7, 0x61,
|
0x38, 0x73, 0x40, 0x76, 0xf7, 0x5c, 0x77, 0x7f, 0xd5, 0x32, 0x89, 0x13, 0xac, 0xba, 0x4e, 0xdd,
|
||||||
0x35, 0x65, 0x0c, 0xa0, 0x87, 0x8c, 0x81, 0xd7, 0x7a, 0x35, 0xd7, 0x1e, 0xef, 0xb4, 0xcb, 0xe7,
|
0x6c, 0xc8, 0x18, 0x40, 0x8f, 0x19, 0x03, 0xaf, 0x75, 0x6b, 0xae, 0x3c, 0xd5, 0x6e, 0x15, 0xcf,
|
||||||
0xfa, 0x30, 0x50, 0x3f, 0x3b, 0x2a, 0xef, 0xe5, 0xb3, 0x4e, 0x48, 0x05, 0xc5, 0x3b, 0xa0, 0xc8,
|
0xf4, 0x60, 0xa0, 0x5e, 0x76, 0x94, 0xde, 0xcf, 0xa6, 0x9d, 0x90, 0x08, 0x8a, 0x77, 0x40, 0x9e,
|
||||||
0x92, 0xcd, 0xc4, 0x21, 0x96, 0xe9, 0xf2, 0xec, 0xe9, 0x52, 0x53, 0x64, 0xf6, 0x36, 0x09, 0x71,
|
0x25, 0x5b, 0x0d, 0x07, 0x58, 0xa6, 0xcb, 0x73, 0x27, 0x4b, 0x4d, 0x91, 0xd9, 0x9b, 0x24, 0xc0,
|
||||||
0x0d, 0x4a, 0xb7, 0x81, 0x84, 0x86, 0x94, 0x56, 0xf8, 0x5d, 0x30, 0x46, 0x7d, 0x62, 0x48, 0x77,
|
0x15, 0x28, 0xdd, 0x06, 0x62, 0x1a, 0x52, 0x5a, 0xe1, 0xf7, 0xc0, 0x08, 0xf5, 0x88, 0x21, 0xdd,
|
||||||
0xbc, 0xfe, 0xb0, 0x29, 0x31, 0x60, 0x23, 0x75, 0x9f, 0x18, 0x49, 0xc4, 0xb2, 0x2f, 0xc4, 0x61,
|
0xf1, 0xfa, 0xe3, 0xa6, 0x44, 0x9f, 0x8d, 0x54, 0x3d, 0x62, 0xc4, 0x11, 0xcb, 0xbe, 0x10, 0x87,
|
||||||
0xe1, 0xfb, 0x1a, 0x18, 0xa7, 0xbc, 0x8c, 0xc8, 0xd2, 0xf3, 0xe6, 0xa8, 0x2c, 0xc8, 0xd4, 0x2a,
|
0x85, 0x1f, 0x68, 0x60, 0x94, 0xf2, 0x32, 0x22, 0x4b, 0xcf, 0x9b, 0xc3, 0xb2, 0x20, 0x55, 0xab,
|
||||||
0xf1, 0x8d, 0x24, 0x78, 0xe5, 0xbf, 0x39, 0xb0, 0x3c, 0x68, 0xe9, 0xba, 0xe7, 0x9a, 0xe2, 0x38,
|
0xc4, 0x37, 0x92, 0xe0, 0xa5, 0xff, 0x64, 0xc0, 0x62, 0xbf, 0xa5, 0xab, 0xae, 0x53, 0x13, 0xc7,
|
||||||
0xb6, 0x64, 0x06, 0x8a, 0x78, 0x7c, 0x3e, 0x9d, 0x81, 0x27, 0xed, 0xf2, 0x53, 0x0f, 0x54, 0x90,
|
0xb1, 0x21, 0x33, 0x50, 0xc4, 0xe3, 0x0b, 0xc9, 0x0c, 0x3c, 0x6e, 0x15, 0x9f, 0x7e, 0xa4, 0x82,
|
||||||
0x4a, 0xd5, 0xaf, 0xa8, 0x7d, 0x8b, 0x74, 0x5e, 0xee, 0x36, 0xec, 0xa4, 0x5d, 0x9e, 0x55, 0xcb,
|
0x44, 0xaa, 0x7e, 0x4d, 0xed, 0x5b, 0xa4, 0xf3, 0x62, 0xa7, 0x61, 0xc7, 0xad, 0xe2, 0xb4, 0x5a,
|
||||||
0xba, 0x6d, 0x85, 0x2d, 0x00, 0x6d, 0x4c, 0xc3, 0x1b, 0x01, 0x76, 0xa9, 0x50, 0x6b, 0x39, 0x44,
|
0xd6, 0x69, 0x2b, 0x6c, 0x02, 0x68, 0x61, 0x1a, 0xdc, 0xf4, 0xb1, 0x43, 0x85, 0x5a, 0xd3, 0x26,
|
||||||
0xba, 0xef, 0x99, 0xd3, 0x85, 0x07, 0x5b, 0x51, 0x5b, 0x94, 0x90, 0xf0, 0x5a, 0x8f, 0x36, 0xd4,
|
0xd2, 0x7d, 0xcf, 0x9e, 0x2c, 0x3c, 0xd8, 0x8a, 0xca, 0xbc, 0x84, 0x84, 0x37, 0xba, 0xb4, 0xa1,
|
||||||
0x07, 0x81, 0x55, 0x97, 0x80, 0x60, 0xaa, 0x0a, 0x46, 0xaa, 0xee, 0x33, 0x2a, 0x92, 0x5c, 0xf8,
|
0x1e, 0x08, 0xac, 0xba, 0xf8, 0x04, 0x53, 0x55, 0x30, 0x12, 0x75, 0x9f, 0x51, 0x91, 0xe4, 0xc2,
|
||||||
0x05, 0x30, 0xe1, 0x10, 0x4a, 0x71, 0x93, 0xf0, 0x2a, 0x31, 0x99, 0x5c, 0xa4, 0xdb, 0x82, 0x8c,
|
0x2f, 0x81, 0x31, 0x9b, 0x50, 0x8a, 0x1b, 0x84, 0x57, 0x89, 0xf1, 0xf8, 0x22, 0xdd, 0x14, 0x64,
|
||||||
0x62, 0x3e, 0xeb, 0x22, 0x2e, 0x0e, 0xf2, 0xda, 0x35, 0x8b, 0x86, 0xf0, 0x8d, 0x9e, 0x04, 0xa8,
|
0x14, 0xf1, 0x59, 0x17, 0x71, 0xa1, 0x9f, 0xd7, 0x6e, 0x98, 0x34, 0x80, 0x6f, 0x74, 0x25, 0x40,
|
||||||
0x9e, 0x6e, 0x87, 0x6c, 0x35, 0x0f, 0x7f, 0x55, 0xa2, 0x62, 0x4a, 0x2a, 0xf8, 0xbf, 0x03, 0x0a,
|
0xf9, 0x64, 0x3b, 0x64, 0xab, 0x79, 0xf8, 0xab, 0x12, 0x15, 0x51, 0x12, 0xc1, 0xff, 0x5d, 0x90,
|
||||||
0x56, 0x48, 0x9c, 0xf8, 0x86, 0x7d, 0x6d, 0x44, 0xb1, 0x57, 0x9b, 0x91, 0x36, 0x14, 0xb6, 0x18,
|
0x33, 0x03, 0x62, 0x47, 0x37, 0xec, 0x6b, 0x43, 0x8a, 0xbd, 0xca, 0x94, 0xb4, 0x21, 0xb7, 0xc1,
|
||||||
0x1a, 0x12, 0xa0, 0x95, 0x3f, 0xe6, 0xc0, 0x13, 0x83, 0x96, 0xb0, 0xb2, 0x4f, 0x99, 0xc7, 0x7d,
|
0xd0, 0x90, 0x00, 0x2d, 0xfd, 0x31, 0x03, 0x2e, 0xf6, 0x5b, 0xc2, 0xca, 0x3e, 0x65, 0x1e, 0xf7,
|
||||||
0x3b, 0x0a, 0xb0, 0x2d, 0x23, 0x4e, 0x79, 0x7c, 0x97, 0x53, 0x91, 0xe4, 0xb2, 0xc2, 0x4c, 0x2d,
|
0xac, 0xd0, 0xc7, 0x96, 0x8c, 0x38, 0xe5, 0xf1, 0x1d, 0x4e, 0x45, 0x92, 0xcb, 0x0a, 0x33, 0x35,
|
||||||
0xb7, 0x19, 0xd9, 0x38, 0x90, 0xe1, 0xa4, 0x76, 0x5d, 0x97, 0x74, 0xa4, 0x24, 0x60, 0x15, 0x00,
|
0x9d, 0x46, 0x68, 0x61, 0x5f, 0x86, 0x93, 0xda, 0x75, 0x55, 0xd2, 0x91, 0x92, 0x80, 0x65, 0x00,
|
||||||
0xba, 0xef, 0x05, 0x21, 0xc7, 0xe0, 0xad, 0xd1, 0x64, 0xed, 0x2c, 0x2b, 0x10, 0x75, 0x45, 0x45,
|
0xe8, 0x9e, 0xeb, 0x07, 0x1c, 0x83, 0xb7, 0x46, 0xe3, 0x95, 0xd3, 0xac, 0x40, 0x54, 0x15, 0x15,
|
||||||
0x29, 0x09, 0x76, 0xef, 0x1c, 0x58, 0xae, 0x29, 0x4f, 0x5d, 0x65, 0xf1, 0xd7, 0x2c, 0xd7, 0x44,
|
0x25, 0x24, 0xd8, 0xbd, 0xb3, 0x6f, 0x3a, 0x35, 0x79, 0xea, 0x2a, 0x8b, 0xbf, 0x61, 0x3a, 0x35,
|
||||||
0x9c, 0xc3, 0xf0, 0x6d, 0x8b, 0x86, 0x8c, 0x22, 0x8f, 0xbc, 0xcb, 0xeb, 0x5c, 0x52, 0x49, 0x30,
|
0xc4, 0x39, 0x0c, 0xdf, 0x32, 0x69, 0xc0, 0x28, 0xf2, 0xc8, 0x3b, 0xbc, 0xce, 0x25, 0x95, 0x04,
|
||||||
0x7c, 0x83, 0xd5, 0x66, 0x2f, 0xb0, 0x08, 0xd5, 0xc7, 0x13, 0xfc, 0x75, 0x45, 0x45, 0x29, 0x89,
|
0xc3, 0x37, 0x58, 0x6d, 0x76, 0x7d, 0x93, 0x50, 0x7d, 0x34, 0xc6, 0x5f, 0x55, 0x54, 0x94, 0x90,
|
||||||
0xca, 0xaf, 0x8b, 0x83, 0x83, 0x84, 0x95, 0x12, 0xf8, 0x24, 0x28, 0x34, 0x03, 0x2f, 0xf2, 0xa5,
|
0x28, 0xfd, 0x3a, 0xdf, 0x3f, 0x48, 0x58, 0x29, 0x81, 0x97, 0x40, 0xae, 0xe1, 0xbb, 0xa1, 0x27,
|
||||||
0x97, 0x94, 0xb7, 0x5f, 0x61, 0x44, 0x24, 0x78, 0x2c, 0x2a, 0x5b, 0x5d, 0xcd, 0xa4, 0x8a, 0xca,
|
0xbd, 0xa4, 0xbc, 0xfd, 0x2a, 0x23, 0x22, 0xc1, 0x63, 0x51, 0xd9, 0xec, 0x68, 0x26, 0x55, 0x54,
|
||||||
0xb8, 0x85, 0x8c, 0xf9, 0xf0, 0x07, 0x1a, 0x28, 0xb8, 0xd2, 0x39, 0x2c, 0xe4, 0xde, 0x18, 0x51,
|
0x46, 0x2d, 0x64, 0xc4, 0x87, 0x3f, 0xd4, 0x40, 0xce, 0x91, 0xce, 0x61, 0x21, 0xf7, 0xc6, 0x90,
|
||||||
0x5c, 0x70, 0xf7, 0x26, 0xe6, 0x0a, 0xcf, 0x0b, 0x64, 0xf8, 0x1c, 0x28, 0x50, 0xc3, 0xf3, 0x89,
|
0xe2, 0x82, 0xbb, 0x37, 0x36, 0x57, 0x78, 0x5e, 0x20, 0xc3, 0xe7, 0x41, 0x8e, 0x1a, 0xae, 0x47,
|
||||||
0xf4, 0x7a, 0x29, 0x16, 0xaa, 0x33, 0xe2, 0x49, 0xbb, 0x3c, 0x13, 0xab, 0xe3, 0x04, 0x24, 0x84,
|
0xa4, 0xd7, 0x0b, 0x91, 0x50, 0x95, 0x11, 0x8f, 0x5b, 0xc5, 0xa9, 0x48, 0x1d, 0x27, 0x20, 0x21,
|
||||||
0xe1, 0x0f, 0x35, 0x00, 0x5a, 0xd8, 0xb6, 0x4c, 0xcc, 0x2f, 0xf6, 0x02, 0x37, 0x7f, 0xb8, 0x61,
|
0x0c, 0x7f, 0xa4, 0x01, 0xd0, 0xc4, 0x96, 0x59, 0xc3, 0xfc, 0x62, 0xcf, 0x71, 0xf3, 0x07, 0x1b,
|
||||||
0x7d, 0x4b, 0xa9, 0x17, 0x87, 0x96, 0x7c, 0xa3, 0x14, 0x34, 0xfc, 0x40, 0x03, 0xd3, 0x34, 0xda,
|
0xd6, 0xb7, 0x95, 0x7a, 0x71, 0x68, 0xf1, 0x37, 0x4a, 0x40, 0xc3, 0x0f, 0x35, 0x30, 0x49, 0xc3,
|
||||||
0x0b, 0xe4, 0x2a, 0xca, 0x5b, 0x80, 0xa9, 0xab, 0xdf, 0x1c, 0xaa, 0x2d, 0xf5, 0x14, 0x40, 0x6d,
|
0x5d, 0x5f, 0xae, 0xa2, 0xbc, 0x05, 0x98, 0xb8, 0xf6, 0xed, 0x81, 0xda, 0x52, 0x4d, 0x00, 0x54,
|
||||||
0xae, 0xd3, 0x2e, 0x4f, 0xa7, 0x29, 0xa8, 0xcb, 0x00, 0xf8, 0x13, 0x0d, 0x14, 0xe5, 0x09, 0x53,
|
0x66, 0xda, 0xad, 0xe2, 0x64, 0x92, 0x82, 0x3a, 0x0c, 0x80, 0x3f, 0xd5, 0x40, 0x5e, 0x9e, 0x30,
|
||||||
0x7d, 0x82, 0x27, 0xfc, 0x5b, 0x23, 0x3a, 0x58, 0x19, 0x51, 0x49, 0x16, 0x48, 0x02, 0x45, 0xca,
|
0xd5, 0xc7, 0x78, 0xc2, 0xbf, 0x35, 0xa4, 0x83, 0x95, 0x11, 0x15, 0x67, 0x81, 0x24, 0x50, 0xa4,
|
||||||
0x02, 0xf8, 0x0f, 0x0d, 0xe8, 0xd8, 0x14, 0x05, 0x1e, 0xdb, 0xbb, 0x81, 0xe5, 0x86, 0x24, 0x10,
|
0x2c, 0x80, 0xff, 0xd0, 0x80, 0x8e, 0x6b, 0xa2, 0xc0, 0x63, 0x6b, 0xc7, 0x37, 0x9d, 0x80, 0xf8,
|
||||||
0x5d, 0x21, 0xd5, 0x8b, 0xdc, 0xbc, 0xe1, 0xde, 0x85, 0xd9, 0x8e, 0xb3, 0xb6, 0x24, 0xad, 0xd3,
|
0xa2, 0x2b, 0xa4, 0x7a, 0x9e, 0x9b, 0x37, 0xd8, 0xbb, 0x30, 0xdd, 0x71, 0x56, 0x16, 0xa4, 0x75,
|
||||||
0xd7, 0x06, 0x98, 0x81, 0x06, 0x1a, 0xc8, 0x03, 0xcd, 0x50, 0xad, 0x97, 0x3e, 0x39, 0x82, 0x40,
|
0xfa, 0x4a, 0x1f, 0x33, 0x50, 0x5f, 0x03, 0x79, 0xa0, 0x19, 0xaa, 0xf5, 0xd2, 0xc7, 0x87, 0x10,
|
||||||
0x4b, 0x3a, 0x3b, 0x59, 0x1d, 0x92, 0x76, 0x3b, 0x05, 0x5d, 0xf9, 0x20, 0x9f, 0x6d, 0xad, 0xb3,
|
0x68, 0x71, 0x67, 0x27, 0xab, 0x43, 0xdc, 0x6e, 0x27, 0xa0, 0x4b, 0x1f, 0x66, 0xd3, 0xad, 0x75,
|
||||||
0x97, 0x3e, 0xbc, 0x2b, 0x8c, 0x15, 0x5b, 0xa1, 0xba, 0xc6, 0x9d, 0xfb, 0xce, 0x88, 0xce, 0x5e,
|
0xfa, 0xd2, 0x87, 0xf7, 0x84, 0xb1, 0x62, 0x2b, 0x54, 0xd7, 0xb8, 0x73, 0xdf, 0x19, 0xd2, 0xd9,
|
||||||
0xdd, 0xda, 0x49, 0xe3, 0xa5, 0x48, 0x14, 0xa5, 0xec, 0x80, 0xbf, 0xd2, 0xc0, 0x0c, 0x36, 0x0c,
|
0xab, 0x5b, 0x3b, 0x6e, 0xbc, 0x14, 0x89, 0xa2, 0x84, 0x1d, 0xf0, 0x57, 0x1a, 0x98, 0xc2, 0x86,
|
||||||
0xe2, 0x87, 0xc4, 0x14, 0xb5, 0x38, 0xf7, 0x19, 0x94, 0x9b, 0x05, 0x69, 0xd5, 0xcc, 0x5a, 0x1a,
|
0x41, 0xbc, 0x80, 0xd4, 0x44, 0x2d, 0xce, 0x7c, 0x0e, 0xe5, 0x66, 0x4e, 0x5a, 0x35, 0xb5, 0x92,
|
||||||
0x1a, 0x75, 0x5b, 0x02, 0x5f, 0x02, 0x67, 0x69, 0xe8, 0x05, 0xc4, 0x8c, 0x23, 0x57, 0xde, 0x13,
|
0x84, 0x46, 0x9d, 0x96, 0xc0, 0x97, 0xc1, 0x69, 0x1a, 0xb8, 0x3e, 0xa9, 0x45, 0x91, 0x2b, 0xef,
|
||||||
0xb0, 0xd3, 0x2e, 0x9f, 0xad, 0x77, 0x71, 0x50, 0x46, 0xb2, 0xf2, 0x4b, 0x0d, 0x94, 0x1f, 0x90,
|
0x09, 0xd8, 0x6e, 0x15, 0x4f, 0x57, 0x3b, 0x38, 0x28, 0x25, 0x59, 0xfa, 0x74, 0x04, 0x14, 0x1f,
|
||||||
0x19, 0xa7, 0x98, 0x76, 0x9e, 0x06, 0xe3, 0x7c, 0xbb, 0x26, 0xf7, 0x4a, 0x31, 0xd5, 0xb9, 0x71,
|
0x91, 0x19, 0x27, 0x78, 0xed, 0x3c, 0x03, 0x46, 0xf9, 0x76, 0x6b, 0xdc, 0x2b, 0xf9, 0x44, 0xe7,
|
||||||
0x2a, 0x92, 0x5c, 0x56, 0xd7, 0x19, 0x3e, 0xeb, 0x36, 0xf2, 0x5c, 0x50, 0xd5, 0xf5, 0xba, 0x20,
|
0xc6, 0xa9, 0x48, 0x72, 0x59, 0x5d, 0x67, 0xf8, 0xac, 0xdb, 0xc8, 0x72, 0x41, 0x55, 0xd7, 0xab,
|
||||||
0xa3, 0x98, 0x5f, 0xf9, 0x9f, 0x96, 0x0d, 0x95, 0x54, 0xb9, 0xa8, 0x1b, 0xd8, 0x26, 0x70, 0x03,
|
0x82, 0x8c, 0x22, 0x3e, 0x7c, 0x17, 0x8c, 0x8a, 0x69, 0x06, 0x2f, 0xaa, 0x43, 0x2c, 0x8c, 0x80,
|
||||||
0xcc, 0xb1, 0xbe, 0x14, 0x11, 0xdf, 0xb6, 0x0c, 0x4c, 0xf9, 0xf0, 0x22, 0x6c, 0x54, 0xf3, 0x74,
|
0xdb, 0xc9, 0xa1, 0x90, 0x84, 0xec, 0x2e, 0x88, 0xb9, 0x27, 0x5d, 0x10, 0x1f, 0x5a, 0x81, 0x46,
|
||||||
0x3d, 0xc3, 0x47, 0x3d, 0x2b, 0xe0, 0xab, 0x00, 0x8a, 0x5e, 0xad, 0x4b, 0x8f, 0xb8, 0x76, 0x54,
|
0xff, 0xcf, 0x2b, 0x50, 0xe9, 0xbf, 0x5a, 0x3a, 0xef, 0x13, 0x5b, 0xad, 0x1a, 0xd8, 0x22, 0x70,
|
||||||
0xd7, 0x55, 0xef, 0x91, 0x40, 0x7d, 0x56, 0xc1, 0x75, 0x30, 0x6f, 0xe3, 0x3d, 0x62, 0xd7, 0x89,
|
0x0d, 0xcc, 0xb0, 0x47, 0x06, 0x22, 0x9e, 0x65, 0x1a, 0x98, 0xf2, 0x97, 0xa8, 0x08, 0x38, 0x35,
|
||||||
0x4d, 0x8c, 0xd0, 0x0b, 0xb8, 0x2a, 0x31, 0xde, 0x2d, 0x74, 0xda, 0xe5, 0xf9, 0x6b, 0x59, 0x26,
|
0x1c, 0xa9, 0xa6, 0xf8, 0xa8, 0x6b, 0x05, 0xbc, 0x0e, 0xa0, 0x68, 0xbc, 0x3b, 0xf4, 0x88, 0x1e,
|
||||||
0xea, 0x95, 0xaf, 0x2c, 0x67, 0x4f, 0x24, 0xbd, 0x71, 0xd1, 0x01, 0xff, 0x2e, 0x07, 0x16, 0x07,
|
0x42, 0xb5, 0xd0, 0xd5, 0x2e, 0x09, 0xd4, 0x63, 0x15, 0x5c, 0x05, 0xb3, 0x16, 0xde, 0x25, 0x56,
|
||||||
0x57, 0x57, 0xf8, 0x6e, 0xd2, 0xa8, 0x8b, 0x3e, 0xec, 0xad, 0x51, 0x55, 0x72, 0xd9, 0xa9, 0x83,
|
0x95, 0x58, 0xc4, 0x08, 0x5c, 0x9f, 0xab, 0x12, 0x6f, 0xf5, 0xb9, 0x76, 0xab, 0x38, 0x7b, 0x23,
|
||||||
0xde, 0x2e, 0x1d, 0x7e, 0x8f, 0x5d, 0x8a, 0xd8, 0x8e, 0x07, 0xf8, 0x37, 0x47, 0x66, 0x02, 0x03,
|
0xcd, 0x44, 0xdd, 0xf2, 0xa5, 0xc5, 0x74, 0x7a, 0x25, 0x37, 0x2e, 0x9e, 0x33, 0xbf, 0xcb, 0x80,
|
||||||
0xa9, 0x4d, 0x8a, 0xfb, 0x16, 0xdb, 0xfc, 0x7a, 0xc5, 0x36, 0xa9, 0xfc, 0x49, 0xcb, 0xce, 0x6a,
|
0xf9, 0xfe, 0x91, 0x01, 0xdf, 0x8b, 0x5f, 0x5d, 0xa2, 0xa9, 0x7e, 0x6b, 0x58, 0x51, 0x28, 0x9f,
|
||||||
0xc9, 0xed, 0x07, 0x7f, 0xaa, 0x81, 0x59, 0xcf, 0x27, 0xee, 0xda, 0xee, 0xd6, 0xad, 0x2f, 0xd5,
|
0x5d, 0xa0, 0xfb, 0xc9, 0x05, 0xbf, 0xcf, 0x3a, 0x1c, 0x6c, 0x45, 0xd3, 0x98, 0x37, 0x87, 0x66,
|
||||||
0xf9, 0xab, 0x99, 0x74, 0xd5, 0xf5, 0x87, 0xb4, 0x93, 0x8d, 0xd1, 0x42, 0xe1, 0x6e, 0xe0, 0xf9,
|
0x02, 0x03, 0xa9, 0x8c, 0x8b, 0xe6, 0x09, 0x5b, 0xbc, 0x57, 0xc2, 0x16, 0x29, 0xfd, 0x49, 0x4b,
|
||||||
0xb4, 0x76, 0xae, 0xd3, 0x2e, 0xcf, 0xee, 0x74, 0x43, 0xa1, 0x2c, 0x76, 0xc5, 0x01, 0x0b, 0x9b,
|
0x3f, 0xbc, 0xe3, 0x0c, 0x86, 0x3f, 0xd3, 0xc0, 0xb4, 0xeb, 0x11, 0x67, 0x65, 0x67, 0xe3, 0xf6,
|
||||||
0x47, 0x21, 0x09, 0x5c, 0x6c, 0x6f, 0x78, 0x46, 0xe4, 0x10, 0x37, 0x14, 0x86, 0x66, 0xa6, 0x7f,
|
0x57, 0x44, 0x26, 0x4b, 0x57, 0x6d, 0x3d, 0xa6, 0x9d, 0xd7, 0xab, 0xdb, 0x5b, 0x42, 0xe1, 0x8e,
|
||||||
0xed, 0x94, 0xd3, 0xff, 0x13, 0x20, 0x1f, 0x05, 0xb6, 0x8c, 0xe2, 0x29, 0xf5, 0xba, 0x85, 0xae,
|
0xef, 0x7a, 0xb4, 0x72, 0xa6, 0xdd, 0x2a, 0x4e, 0x6f, 0x77, 0x42, 0xa1, 0x34, 0x76, 0xc9, 0x06,
|
||||||
0x21, 0x46, 0xaf, 0x2c, 0x83, 0x31, 0x66, 0x27, 0xbc, 0x00, 0xf2, 0x01, 0x3e, 0xe4, 0x5a, 0xa7,
|
0x73, 0xeb, 0x87, 0x01, 0xf1, 0x1d, 0x6c, 0xad, 0xb9, 0x46, 0x68, 0x13, 0x27, 0x10, 0x86, 0xa6,
|
||||||
0x6b, 0x13, 0x4c, 0x04, 0xe1, 0x43, 0xc4, 0x68, 0x95, 0x3f, 0x5f, 0x04, 0xb3, 0x99, 0xbd, 0xc0,
|
0x46, 0x39, 0xda, 0x09, 0x47, 0x39, 0x17, 0x41, 0x36, 0xf4, 0x2d, 0x19, 0xc5, 0x13, 0x6a, 0x54,
|
||||||
0x45, 0x90, 0x53, 0x4f, 0x66, 0x40, 0x2a, 0xcd, 0x6d, 0x6d, 0xa0, 0x9c, 0x65, 0xc2, 0x17, 0xc0,
|
0x89, 0x6e, 0x20, 0x46, 0x2f, 0x2d, 0x82, 0x11, 0x66, 0x27, 0x3c, 0x0f, 0xb2, 0x3e, 0x3e, 0xe0,
|
||||||
0xb8, 0x78, 0x7d, 0x94, 0xa0, 0x65, 0x55, 0x02, 0x38, 0x95, 0x75, 0x41, 0x89, 0x3a, 0x66, 0x88,
|
0x5a, 0x27, 0x2b, 0x63, 0x4c, 0x04, 0xe1, 0x03, 0xc4, 0x68, 0xa5, 0x3f, 0x5f, 0x00, 0xd3, 0xa9,
|
||||||
0x14, 0xe7, 0x36, 0x90, 0x86, 0xcc, 0x12, 0x61, 0x03, 0x69, 0x20, 0x46, 0xfb, 0xb4, 0x4f, 0x1f,
|
0xbd, 0xc0, 0x79, 0x90, 0x51, 0xf3, 0x4f, 0x20, 0x95, 0x66, 0x36, 0xd6, 0x50, 0xc6, 0xac, 0xc1,
|
||||||
0xf1, 0xdb, 0x4b, 0xe1, 0x14, 0x6f, 0x2f, 0xe3, 0x9f, 0xf8, 0xf6, 0xf2, 0x24, 0x28, 0x84, 0x56,
|
0x17, 0x55, 0xf1, 0x15, 0xa0, 0x45, 0x55, 0xcf, 0x39, 0x95, 0xb5, 0xb4, 0xb1, 0x3a, 0x66, 0x48,
|
||||||
0x68, 0x13, 0x7d, 0xa2, 0xbb, 0x59, 0xbd, 0xc1, 0x88, 0x48, 0xf0, 0xe0, 0x6d, 0x30, 0x61, 0x92,
|
0x54, 0x38, 0x99, 0x0d, 0xa4, 0x2e, 0xb3, 0x44, 0xd8, 0x40, 0xea, 0x88, 0xd1, 0x3e, 0xeb, 0x1c,
|
||||||
0x06, 0x8e, 0xec, 0x50, 0x2f, 0xf2, 0x10, 0x5a, 0x1f, 0x42, 0x08, 0x89, 0x87, 0xb1, 0x0d, 0xa1,
|
0x2b, 0x1a, 0xa4, 0xe5, 0x4e, 0x30, 0x48, 0x1b, 0x7d, 0xe8, 0x20, 0xed, 0x12, 0xc8, 0x05, 0x66,
|
||||||
0x17, 0xc5, 0x00, 0xf0, 0x29, 0x30, 0xe1, 0xe0, 0x23, 0xcb, 0x89, 0x1c, 0x7e, 0x8d, 0x6b, 0x42,
|
0x60, 0x11, 0x7d, 0xac, 0xf3, 0xe5, 0x71, 0x93, 0x11, 0x91, 0xe0, 0xc1, 0x3b, 0x60, 0xac, 0x46,
|
||||||
0x6c, 0x5b, 0x90, 0x50, 0xcc, 0x63, 0x95, 0x91, 0x1c, 0x19, 0x76, 0x44, 0xad, 0x16, 0x91, 0x4c,
|
0xea, 0x38, 0xb4, 0x02, 0x3d, 0xcf, 0x43, 0x68, 0x75, 0x00, 0x21, 0x24, 0xa6, 0x9c, 0x6b, 0x42,
|
||||||
0x1d, 0xf0, 0x82, 0xab, 0x2a, 0xe3, 0x66, 0x86, 0x8f, 0x7a, 0x56, 0x70, 0x30, 0xcb, 0xe5, 0x8b,
|
0x2f, 0x8a, 0x00, 0xe0, 0xd3, 0x60, 0xcc, 0xc6, 0x87, 0xa6, 0x1d, 0xda, 0xbc, 0x27, 0xd3, 0x84,
|
||||||
0xa7, 0x52, 0x60, 0x82, 0x84, 0x62, 0x5e, 0x37, 0x98, 0x94, 0x9f, 0x1e, 0x04, 0x26, 0x17, 0xf7,
|
0xd8, 0xa6, 0x20, 0xa1, 0x88, 0xc7, 0x2a, 0x23, 0x39, 0x34, 0xac, 0x90, 0x9a, 0x4d, 0x22, 0x99,
|
||||||
0xac, 0x80, 0x5f, 0x04, 0x93, 0x0e, 0x3e, 0xba, 0x46, 0xdc, 0x66, 0xb8, 0xaf, 0xcf, 0x2c, 0x69,
|
0x3a, 0xe0, 0xb7, 0xa7, 0xaa, 0x8c, 0xeb, 0x29, 0x3e, 0xea, 0x5a, 0xc1, 0xc1, 0x4c, 0x87, 0x2f,
|
||||||
0x2b, 0xf9, 0xda, 0x4c, 0xa7, 0x5d, 0x9e, 0xdc, 0x8e, 0x89, 0x28, 0xe1, 0x73, 0x61, 0xcb, 0x95,
|
0x9e, 0x48, 0x80, 0x09, 0x12, 0x8a, 0x78, 0x9d, 0x60, 0x52, 0x7e, 0xb2, 0x1f, 0x98, 0x5c, 0xdc,
|
||||||
0xc2, 0x67, 0x53, 0xc2, 0x31, 0x11, 0x25, 0x7c, 0x76, 0xe9, 0xf8, 0x38, 0x64, 0xc9, 0xa5, 0xcf,
|
0xb5, 0x02, 0x7e, 0x19, 0x8c, 0xdb, 0xf8, 0xf0, 0x06, 0x71, 0x1a, 0xc1, 0x9e, 0x3e, 0xb5, 0xa0,
|
||||||
0x76, 0x0f, 0x13, 0xbb, 0x82, 0x8c, 0x62, 0x3e, 0x5c, 0x01, 0x45, 0x07, 0x1f, 0xf1, 0xc1, 0x4f,
|
0x2d, 0x65, 0x2b, 0x53, 0xed, 0x56, 0x71, 0x7c, 0x33, 0x22, 0xa2, 0x98, 0xcf, 0x85, 0x4d, 0x47,
|
||||||
0x9f, 0xe3, 0x6a, 0xf9, 0x23, 0xe1, 0xb6, 0xa4, 0x21, 0xc5, 0xe5, 0x92, 0x96, 0x2b, 0x24, 0xe7,
|
0x0a, 0x9f, 0x4e, 0x08, 0x47, 0x44, 0x14, 0xf3, 0x59, 0x07, 0xe1, 0xe1, 0x80, 0x25, 0x97, 0x3e,
|
||||||
0x53, 0x92, 0x92, 0x86, 0x14, 0x97, 0x05, 0x71, 0xe4, 0x5a, 0x77, 0x22, 0x22, 0x84, 0x21, 0xf7,
|
0xdd, 0xf9, 0x32, 0xdc, 0x11, 0x64, 0x14, 0xf1, 0xe1, 0x12, 0xc8, 0xdb, 0xf8, 0x90, 0xbf, 0xe2,
|
||||||
0x8c, 0x0a, 0xe2, 0x9b, 0x09, 0x0b, 0xa5, 0xe5, 0xd8, 0xe0, 0xe5, 0x44, 0x76, 0x68, 0xf9, 0x36,
|
0xf5, 0x19, 0xae, 0x96, 0x4f, 0x7c, 0x37, 0x25, 0x0d, 0x29, 0x2e, 0x97, 0x34, 0x1d, 0x21, 0x39,
|
||||||
0xd9, 0x69, 0xe8, 0xe7, 0xb8, 0xff, 0x79, 0x6b, 0xb5, 0xad, 0xa8, 0x28, 0x25, 0x01, 0x09, 0x18,
|
0x9b, 0x90, 0x94, 0x34, 0xa4, 0xb8, 0x2c, 0x88, 0x43, 0xc7, 0xbc, 0x1b, 0x12, 0x21, 0x0c, 0xb9,
|
||||||
0x23, 0x6e, 0xe4, 0xe8, 0x8f, 0xf1, 0x86, 0x69, 0x28, 0x21, 0xa8, 0x32, 0x67, 0xd3, 0x8d, 0x1c,
|
0x67, 0x54, 0x10, 0xdf, 0x8a, 0x59, 0x28, 0x29, 0xc7, 0x5e, 0xd1, 0x76, 0x68, 0x05, 0xa6, 0x67,
|
||||||
0xc4, 0xd5, 0xc3, 0x17, 0xc0, 0x8c, 0x83, 0x8f, 0x58, 0x39, 0x20, 0x41, 0xc8, 0x46, 0xc2, 0x05,
|
0x91, 0xed, 0xba, 0x7e, 0x86, 0xfb, 0x9f, 0xf7, 0xc9, 0x9b, 0x8a, 0x8a, 0x12, 0x12, 0x90, 0x80,
|
||||||
0xbe, 0xf9, 0x79, 0xd6, 0xa4, 0x6c, 0xa7, 0x19, 0xa8, 0x5b, 0x8e, 0x2f, 0xb4, 0xdc, 0xd4, 0xc2,
|
0x11, 0xe2, 0x84, 0xb6, 0x7e, 0x96, 0x5f, 0xec, 0x03, 0x09, 0x41, 0x95, 0x39, 0xeb, 0x4e, 0x68,
|
||||||
0xf3, 0xa9, 0x85, 0x69, 0x06, 0xea, 0x96, 0x63, 0x9e, 0x0e, 0xc8, 0x9d, 0xc8, 0x0a, 0x88, 0xa9,
|
0x23, 0xae, 0x1e, 0xbe, 0x08, 0xa6, 0x6c, 0x7c, 0xc8, 0xca, 0x01, 0xf1, 0x03, 0xf6, 0xbe, 0x9f,
|
||||||
0x3f, 0xce, 0xfb, 0x1a, 0xf9, 0x70, 0x2b, 0x68, 0x48, 0x71, 0x61, 0x2b, 0x7e, 0x21, 0xd0, 0x79,
|
0xe3, 0x9b, 0x9f, 0x65, 0x1d, 0xe7, 0x66, 0x92, 0x81, 0x3a, 0xe5, 0xf8, 0x42, 0xd3, 0x49, 0x2c,
|
||||||
0x1a, 0xde, 0x1c, 0x6e, 0x25, 0xdf, 0x09, 0xd6, 0x82, 0x00, 0x1f, 0x8b, 0x9b, 0x26, 0xfd, 0x36,
|
0x3c, 0x97, 0x58, 0x98, 0x64, 0xa0, 0x4e, 0x39, 0xe6, 0x69, 0x9f, 0xdc, 0x0d, 0x4d, 0x9f, 0xd4,
|
||||||
0x00, 0x29, 0x28, 0x60, 0xdb, 0xde, 0x69, 0xe8, 0x17, 0xb8, 0xef, 0x87, 0x7d, 0x83, 0xa8, 0xaa,
|
0xf4, 0xa7, 0x78, 0x93, 0x2a, 0xa7, 0xf0, 0x82, 0x86, 0x14, 0x17, 0x36, 0xa3, 0x71, 0x8f, 0xce,
|
||||||
0xb3, 0xc6, 0x40, 0x90, 0xc0, 0x62, 0xa0, 0x9e, 0xcb, 0x42, 0x63, 0x71, 0xb4, 0xa0, 0x3b, 0x0c,
|
0xd3, 0xf0, 0xd6, 0x60, 0x2b, 0xf9, 0xb6, 0xbf, 0xe2, 0xfb, 0xf8, 0x48, 0xdc, 0x34, 0xc9, 0x41,
|
||||||
0x04, 0x09, 0x2c, 0xbe, 0x53, 0xf7, 0x78, 0xa7, 0xa1, 0x7f, 0x6e, 0xc4, 0x3b, 0x65, 0x20, 0x48,
|
0x0f, 0xa4, 0x20, 0x87, 0x2d, 0x6b, 0xbb, 0xae, 0x9f, 0xe7, 0xbe, 0x1f, 0xf4, 0x0d, 0xa2, 0xaa,
|
||||||
0x60, 0x41, 0x0b, 0xe4, 0x5d, 0x2f, 0xd4, 0x2f, 0x8e, 0xe4, 0x7a, 0xe6, 0x17, 0xce, 0x75, 0x2f,
|
0xce, 0x0a, 0x03, 0x41, 0x02, 0x8b, 0x81, 0xba, 0x0e, 0x0b, 0x8d, 0xf9, 0xe1, 0x82, 0x6e, 0x33,
|
||||||
0x44, 0x0c, 0x03, 0xfe, 0x42, 0x03, 0xc0, 0x4f, 0x42, 0xf4, 0x89, 0xa1, 0x0c, 0x9e, 0x19, 0xc8,
|
0x10, 0x24, 0xb0, 0xf8, 0x4e, 0x9d, 0xa3, 0xed, 0xba, 0xfe, 0x85, 0x21, 0xef, 0x94, 0x81, 0x20,
|
||||||
0x6a, 0x12, 0xdb, 0x9b, 0x6e, 0x18, 0x1c, 0x27, 0xa3, 0x47, 0x2a, 0x07, 0x52, 0x56, 0xc0, 0xdf,
|
0x81, 0x05, 0x4d, 0x90, 0x75, 0xdc, 0x40, 0xbf, 0x30, 0x94, 0xeb, 0x99, 0x5f, 0x38, 0x5b, 0x6e,
|
||||||
0x6b, 0xe0, 0xb1, 0xf4, 0x6c, 0xa7, 0xcc, 0x2b, 0x71, 0x8f, 0xdc, 0x18, 0x76, 0x98, 0xd7, 0x3c,
|
0x80, 0x18, 0x06, 0xfc, 0xa5, 0x06, 0x80, 0x17, 0x87, 0xe8, 0xc5, 0x81, 0x4c, 0x11, 0x52, 0x90,
|
||||||
0xcf, 0xae, 0xe9, 0x9d, 0x76, 0xf9, 0xb1, 0xb5, 0x3e, 0xa8, 0xa8, 0xaf, 0x2d, 0xf0, 0x2f, 0x1a,
|
0xe5, 0x38, 0xb6, 0xd7, 0x9d, 0xc0, 0x3f, 0x8a, 0xdf, 0x91, 0x89, 0x1c, 0x48, 0x58, 0x01, 0x7f,
|
||||||
0x98, 0x97, 0x55, 0x34, 0x65, 0x61, 0x99, 0x3b, 0x90, 0x0c, 0xdb, 0x81, 0x59, 0x1c, 0xe1, 0x47,
|
0xaf, 0x81, 0xb3, 0xc9, 0x36, 0x59, 0x99, 0x57, 0xe0, 0x1e, 0xb9, 0x39, 0xe8, 0x30, 0xaf, 0xb8,
|
||||||
0xf5, 0x83, 0x63, 0x0f, 0x1f, 0xf5, 0x9a, 0x06, 0xff, 0xae, 0x81, 0x69, 0x93, 0xf8, 0xc4, 0x35,
|
0xae, 0x55, 0xd1, 0xdb, 0xad, 0xe2, 0xd9, 0x95, 0x1e, 0xa8, 0xa8, 0xa7, 0x2d, 0xf0, 0x2f, 0x1a,
|
||||||
0x89, 0x6b, 0x30, 0x5b, 0x97, 0x86, 0x32, 0x69, 0x66, 0x6d, 0xdd, 0x48, 0x41, 0x08, 0x33, 0xab,
|
0x98, 0x95, 0x55, 0x34, 0x61, 0x61, 0x91, 0x3b, 0x90, 0x0c, 0xda, 0x81, 0x69, 0x1c, 0xe1, 0x47,
|
||||||
0xd2, 0xcc, 0xe9, 0x34, 0xeb, 0xa4, 0x5d, 0x3e, 0x9f, 0x2c, 0x4d, 0x73, 0x50, 0x97, 0x95, 0xf0,
|
0xf5, 0xeb, 0x71, 0x17, 0x1f, 0x75, 0x9b, 0x06, 0xff, 0xae, 0x81, 0xc9, 0x1a, 0xf1, 0x88, 0x53,
|
||||||
0x43, 0x0d, 0xcc, 0x26, 0x07, 0x20, 0xae, 0x94, 0xe5, 0x11, 0xc6, 0x01, 0x6f, 0x5f, 0xd7, 0xba,
|
0x23, 0x8e, 0xc1, 0x6c, 0x5d, 0x18, 0xc8, 0xd8, 0x20, 0x6d, 0xeb, 0x5a, 0x02, 0x42, 0x98, 0x59,
|
||||||
0x01, 0x51, 0xd6, 0x02, 0xf8, 0x57, 0x8d, 0x75, 0x6a, 0xf1, 0xdc, 0x48, 0xf5, 0x0a, 0xf7, 0xe5,
|
0x96, 0x66, 0x4e, 0x26, 0x59, 0xc7, 0xad, 0xe2, 0xb9, 0x78, 0x69, 0x92, 0x83, 0x3a, 0xac, 0x84,
|
||||||
0xdb, 0x43, 0xf7, 0xa5, 0x42, 0x10, 0xae, 0xbc, 0x94, 0xb4, 0x82, 0x8a, 0x73, 0xd2, 0x2e, 0x2f,
|
0x1f, 0x69, 0x60, 0x3a, 0x3e, 0x00, 0x71, 0xa5, 0x2c, 0x0e, 0x31, 0x0e, 0x78, 0xfb, 0xba, 0xd2,
|
||||||
0xa4, 0x3d, 0xa9, 0x18, 0x28, 0x6d, 0x21, 0xfc, 0xb1, 0x06, 0xa6, 0x49, 0xd2, 0x71, 0x53, 0xfd,
|
0x09, 0x88, 0xd2, 0x16, 0xc0, 0xbf, 0x6a, 0xac, 0x53, 0x8b, 0xde, 0x7d, 0x54, 0x2f, 0x71, 0x5f,
|
||||||
0xc9, 0xa1, 0x38, 0xb1, 0x6f, 0x13, 0x2f, 0x5e, 0xbb, 0x52, 0x2c, 0x8a, 0xba, 0xb0, 0x59, 0x07,
|
0xbe, 0x3d, 0x70, 0x5f, 0x2a, 0x04, 0xe1, 0xca, 0xcb, 0x71, 0x2b, 0xa8, 0x38, 0xc7, 0xad, 0xe2,
|
||||||
0x49, 0x8e, 0xb0, 0xe3, 0xdb, 0x44, 0xff, 0xfc, 0x90, 0x3b, 0xc8, 0x4d, 0xa1, 0x17, 0xc5, 0x00,
|
0x5c, 0xd2, 0x93, 0x8a, 0x81, 0x92, 0x16, 0xc2, 0x9f, 0x68, 0x60, 0x92, 0xc4, 0x1d, 0x37, 0xd5,
|
||||||
0x8b, 0x6c, 0xf2, 0xc9, 0x64, 0x0e, 0x9c, 0x03, 0xf9, 0x03, 0x22, 0x7f, 0xb9, 0x43, 0xec, 0x4f,
|
0x2f, 0x0d, 0xc4, 0x89, 0x3d, 0x9b, 0x78, 0xf1, 0x52, 0x4f, 0xb0, 0x28, 0xea, 0xc0, 0x66, 0x1d,
|
||||||
0x68, 0x82, 0x42, 0x0b, 0xdb, 0x51, 0x3c, 0xbc, 0x0d, 0xb9, 0xea, 0x22, 0xa1, 0xfc, 0xa5, 0xdc,
|
0x24, 0x39, 0xc4, 0xb6, 0x67, 0x11, 0xfd, 0x8b, 0x03, 0xee, 0x20, 0xd7, 0x85, 0x5e, 0x14, 0x01,
|
||||||
0x8b, 0xda, 0xe2, 0x5d, 0x0d, 0x9c, 0xef, 0x9f, 0xd0, 0x8f, 0xd4, 0xac, 0xdf, 0x68, 0x60, 0xbe,
|
0xcc, 0xb3, 0x97, 0x4f, 0x2a, 0x73, 0xe0, 0x0c, 0xc8, 0xee, 0x13, 0xf9, 0x33, 0x2c, 0x62, 0x7f,
|
||||||
0x27, 0x77, 0xfb, 0x58, 0x74, 0xa7, 0xdb, 0xa2, 0xd7, 0x87, 0x9d, 0x84, 0xf5, 0x30, 0xb0, 0xdc,
|
0xc2, 0x1a, 0xc8, 0x35, 0xb1, 0x15, 0x46, 0x8f, 0xb7, 0x01, 0x57, 0x5d, 0x24, 0x94, 0xbf, 0x9c,
|
||||||
0x26, 0xef, 0x3c, 0xd2, 0xe6, 0xfd, 0x4c, 0x03, 0x73, 0xd9, 0x74, 0x78, 0x94, 0xfe, 0xaa, 0xdc,
|
0x79, 0x49, 0x9b, 0xbf, 0xa7, 0x81, 0x73, 0xbd, 0x13, 0xfa, 0x89, 0x9a, 0xf5, 0x1b, 0x0d, 0xcc,
|
||||||
0xcd, 0x81, 0xf3, 0xfd, 0x1b, 0x26, 0x18, 0xa8, 0xc9, 0x70, 0x34, 0x13, 0x36, 0x48, 0xa6, 0x4c,
|
0x76, 0xe5, 0x6e, 0x0f, 0x8b, 0xee, 0x76, 0x5a, 0xf4, 0xfa, 0xa0, 0x93, 0xb0, 0x1a, 0xf8, 0xa6,
|
||||||
0x35, 0x54, 0xbe, 0xaf, 0x81, 0xa9, 0xdb, 0x4a, 0x2e, 0xfe, 0xcd, 0x68, 0xe8, 0xb3, 0x7d, 0x5c,
|
0xd3, 0xe0, 0x9d, 0x47, 0xd2, 0xbc, 0x9f, 0x6b, 0x60, 0x26, 0x9d, 0x0e, 0x4f, 0xd2, 0x5f, 0xa5,
|
||||||
0x7f, 0x12, 0x06, 0x45, 0x69, 0xdc, 0xca, 0xdf, 0x34, 0xb0, 0xd0, 0xb7, 0xb0, 0xb2, 0x11, 0x14,
|
0x7b, 0x19, 0x70, 0xae, 0x77, 0xc3, 0x04, 0x7d, 0xf5, 0x32, 0x1c, 0xce, 0x0b, 0xbb, 0xd7, 0x34,
|
||||||
0xdb, 0xb6, 0x77, 0x28, 0x9e, 0x68, 0x52, 0x4f, 0x66, 0x6b, 0x9c, 0x8a, 0x24, 0x37, 0xe5, 0xbd,
|
0xee, 0x03, 0x0d, 0x4c, 0xdc, 0x51, 0x72, 0xd1, 0x0f, 0x80, 0x03, 0x7f, 0xdb, 0x47, 0xf5, 0x27,
|
||||||
0xdc, 0x67, 0xe5, 0xbd, 0xca, 0x3f, 0x35, 0x70, 0xf1, 0x93, 0x22, 0xf1, 0x91, 0x1c, 0xe9, 0x0a,
|
0x66, 0x50, 0x94, 0xc4, 0x2d, 0xfd, 0x4d, 0x03, 0x73, 0x3d, 0x0b, 0x2b, 0x7b, 0x82, 0x62, 0xcb,
|
||||||
0x28, 0xca, 0xa6, 0xe8, 0x98, 0x1f, 0xa7, 0x9c, 0x03, 0x64, 0xd1, 0xe0, 0xff, 0xcc, 0x20, 0xfe,
|
0x72, 0x0f, 0xc4, 0x88, 0x26, 0x31, 0xff, 0x5c, 0xe1, 0x54, 0x24, 0xb9, 0x09, 0xef, 0x65, 0x3e,
|
||||||
0xaa, 0xbc, 0xa7, 0x81, 0xb9, 0x3a, 0x09, 0x5a, 0x96, 0x41, 0x10, 0x69, 0x90, 0x80, 0xb8, 0x06,
|
0x2f, 0xef, 0x95, 0xfe, 0xa9, 0x81, 0x0b, 0x0f, 0x8b, 0xc4, 0x27, 0x72, 0xa4, 0x4b, 0x20, 0x2f,
|
||||||
0x81, 0xab, 0x60, 0x92, 0xff, 0x58, 0xe3, 0x63, 0x23, 0x7e, 0xc9, 0x9c, 0x97, 0x2e, 0x9f, 0xbc,
|
0x9b, 0xa2, 0x23, 0x7e, 0x9c, 0xf2, 0x1d, 0x20, 0x8b, 0x06, 0xff, 0xcf, 0x14, 0xf1, 0x57, 0xe9,
|
||||||
0x1e, 0x33, 0x50, 0x22, 0xa3, 0x5e, 0x3d, 0x73, 0x03, 0x5f, 0x3d, 0x2f, 0x82, 0x31, 0x3f, 0x79,
|
0x7d, 0x0d, 0xcc, 0x54, 0x89, 0xdf, 0x34, 0x0d, 0x82, 0x48, 0x9d, 0xf8, 0xc4, 0x31, 0x08, 0x5c,
|
||||||
0xe0, 0x2b, 0x32, 0x2e, 0x7f, 0xd3, 0xe3, 0xd4, 0xca, 0xbf, 0x34, 0xd0, 0xef, 0x1f, 0x0b, 0x60,
|
0x06, 0xe3, 0xfc, 0x97, 0x37, 0x0f, 0x1b, 0xd1, 0x58, 0x7a, 0x56, 0xba, 0x7c, 0x7c, 0x2b, 0x62,
|
||||||
0x0b, 0x4c, 0x50, 0x61, 0x9c, 0x74, 0xde, 0xce, 0x43, 0x3a, 0x2f, 0xbb, 0x55, 0x51, 0xf8, 0x63,
|
0xa0, 0x58, 0x46, 0x8d, 0xb0, 0x33, 0x7d, 0x47, 0xd8, 0x17, 0xc0, 0x88, 0x17, 0x0f, 0xf8, 0xf2,
|
||||||
0x6a, 0x0c, 0xc6, 0xfc, 0x67, 0xe0, 0x5a, 0xe4, 0x9a, 0xf2, 0x49, 0x6e, 0x5a, 0xf8, 0x6f, 0x7d,
|
0x8c, 0xcb, 0x67, 0x7a, 0x9c, 0x5a, 0xfa, 0x97, 0x06, 0x7a, 0xfd, 0x97, 0x08, 0x6c, 0x82, 0x31,
|
||||||
0x4d, 0xd0, 0x90, 0xe2, 0xc2, 0x0b, 0xe2, 0xf1, 0x28, 0xf5, 0x22, 0x13, 0x3f, 0x1c, 0xd5, 0x2e,
|
0x2a, 0x8c, 0x93, 0xce, 0xdb, 0x7e, 0x4c, 0xe7, 0xa5, 0xb7, 0x2a, 0x0a, 0x7f, 0x44, 0x8d, 0xc0,
|
||||||
0xdf, 0xbb, 0x5f, 0x3a, 0xf3, 0xd1, 0xfd, 0xd2, 0x99, 0x8f, 0xef, 0x97, 0xce, 0x7c, 0xbf, 0x53,
|
0x98, 0xff, 0x0c, 0x5c, 0x09, 0x9d, 0x9a, 0x1c, 0xc9, 0x4d, 0x0a, 0xff, 0xad, 0xae, 0x08, 0x1a,
|
||||||
0xd2, 0xee, 0x75, 0x4a, 0xda, 0x47, 0x9d, 0x92, 0xf6, 0x71, 0xa7, 0xa4, 0xfd, 0xbb, 0x53, 0xd2,
|
0x52, 0x5c, 0x78, 0x5e, 0x0c, 0x8f, 0x12, 0x13, 0x99, 0x68, 0x70, 0x54, 0xb9, 0x72, 0xff, 0x41,
|
||||||
0x7e, 0xfe, 0x9f, 0xd2, 0x99, 0x6f, 0x4d, 0x48, 0xd3, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xc9,
|
0xe1, 0xd4, 0xc7, 0x0f, 0x0a, 0xa7, 0x3e, 0x79, 0x50, 0x38, 0xf5, 0x83, 0x76, 0x41, 0xbb, 0xdf,
|
||||||
0x4a, 0x8d, 0x8a, 0xee, 0x27, 0x00, 0x00,
|
0x2e, 0x68, 0x1f, 0xb7, 0x0b, 0xda, 0x27, 0xed, 0x82, 0xf6, 0xef, 0x76, 0x41, 0xfb, 0xc5, 0xa7,
|
||||||
|
0x85, 0x53, 0xdf, 0x19, 0x93, 0xa6, 0xfd, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xc5, 0xe4, 0x3a,
|
||||||
|
0xbb, 0x29, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
@@ -203,10 +203,14 @@ message CustomResourceDefinitionSpec {
|
|||||||
optional string scope = 4;
|
optional string scope = 4;
|
||||||
|
|
||||||
// Validation describes the validation methods for CustomResources
|
// Validation describes the validation methods for CustomResources
|
||||||
|
// Optional, the global validation schema for all versions.
|
||||||
|
// Top-level and per-version schemas are mutually exclusive.
|
||||||
// +optional
|
// +optional
|
||||||
optional CustomResourceValidation validation = 5;
|
optional CustomResourceValidation validation = 5;
|
||||||
|
|
||||||
// Subresources describes the subresources for CustomResources
|
// Subresources describes the subresources for CustomResource
|
||||||
|
// Optional, the global subresources for all versions.
|
||||||
|
// Top-level and per-version subresources are mutually exclusive.
|
||||||
// +optional
|
// +optional
|
||||||
optional CustomResourceSubresources subresources = 6;
|
optional CustomResourceSubresources subresources = 6;
|
||||||
|
|
||||||
@@ -225,6 +229,8 @@ message CustomResourceDefinitionSpec {
|
|||||||
repeated CustomResourceDefinitionVersion versions = 7;
|
repeated CustomResourceDefinitionVersion versions = 7;
|
||||||
|
|
||||||
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||||
|
// Optional, the global columns for all versions.
|
||||||
|
// Top-level and per-version columns are mutually exclusive.
|
||||||
// +optional
|
// +optional
|
||||||
repeated CustomResourceColumnDefinition additionalPrinterColumns = 8;
|
repeated CustomResourceColumnDefinition additionalPrinterColumns = 8;
|
||||||
|
|
||||||
@@ -262,6 +268,30 @@ message CustomResourceDefinitionVersion {
|
|||||||
// Storage flags the version as storage version. There must be exactly one
|
// Storage flags the version as storage version. There must be exactly one
|
||||||
// flagged as storage version.
|
// flagged as storage version.
|
||||||
optional bool storage = 3;
|
optional bool storage = 3;
|
||||||
|
|
||||||
|
// Schema describes the schema for CustomResource used in validation, pruning, and defaulting.
|
||||||
|
// Top-level and per-version schemas are mutually exclusive.
|
||||||
|
// Per-version schemas must not all be set to identical values (top-level validation schema should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// +optional
|
||||||
|
optional CustomResourceValidation schema = 4;
|
||||||
|
|
||||||
|
// Subresources describes the subresources for CustomResource
|
||||||
|
// Top-level and per-version subresources are mutually exclusive.
|
||||||
|
// Per-version subresources must not all be set to identical values (top-level subresources should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// +optional
|
||||||
|
optional CustomResourceSubresources subresources = 5;
|
||||||
|
|
||||||
|
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||||
|
// Top-level and per-version columns are mutually exclusive.
|
||||||
|
// Per-version columns must not all be set to identical values (top-level columns should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// NOTE: CRDs created prior to 1.13 populated the top-level additionalPrinterColumns field by default. To apply an
|
||||||
|
// update that changes to per-version additionalPrinterColumns, the top-level additionalPrinterColumns field must
|
||||||
|
// be explicitly set to null
|
||||||
|
// +optional
|
||||||
|
repeated CustomResourceColumnDefinition additionalPrinterColumns = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.
|
// CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.
|
||||||
|
@@ -47,9 +47,13 @@ type CustomResourceDefinitionSpec struct {
|
|||||||
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
|
// 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"`
|
Scope ResourceScope `json:"scope" protobuf:"bytes,4,opt,name=scope,casttype=ResourceScope"`
|
||||||
// Validation describes the validation methods for CustomResources
|
// Validation describes the validation methods for CustomResources
|
||||||
|
// Optional, the global validation schema for all versions.
|
||||||
|
// Top-level and per-version schemas are mutually exclusive.
|
||||||
// +optional
|
// +optional
|
||||||
Validation *CustomResourceValidation `json:"validation,omitempty" protobuf:"bytes,5,opt,name=validation"`
|
Validation *CustomResourceValidation `json:"validation,omitempty" protobuf:"bytes,5,opt,name=validation"`
|
||||||
// Subresources describes the subresources for CustomResources
|
// Subresources describes the subresources for CustomResource
|
||||||
|
// Optional, the global subresources for all versions.
|
||||||
|
// Top-level and per-version subresources are mutually exclusive.
|
||||||
// +optional
|
// +optional
|
||||||
Subresources *CustomResourceSubresources `json:"subresources,omitempty" protobuf:"bytes,6,opt,name=subresources"`
|
Subresources *CustomResourceSubresources `json:"subresources,omitempty" protobuf:"bytes,6,opt,name=subresources"`
|
||||||
// Versions is the list of all supported versions for this resource.
|
// Versions is the list of all supported versions for this resource.
|
||||||
@@ -66,6 +70,8 @@ type CustomResourceDefinitionSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Versions []CustomResourceDefinitionVersion `json:"versions,omitempty" protobuf:"bytes,7,rep,name=versions"`
|
Versions []CustomResourceDefinitionVersion `json:"versions,omitempty" protobuf:"bytes,7,rep,name=versions"`
|
||||||
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||||
|
// Optional, the global columns for all versions.
|
||||||
|
// Top-level and per-version columns are mutually exclusive.
|
||||||
// +optional
|
// +optional
|
||||||
AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,8,rep,name=additionalPrinterColumns"`
|
AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,8,rep,name=additionalPrinterColumns"`
|
||||||
|
|
||||||
@@ -159,6 +165,27 @@ type CustomResourceDefinitionVersion struct {
|
|||||||
// Storage flags the version as storage version. There must be exactly one
|
// Storage flags the version as storage version. There must be exactly one
|
||||||
// flagged as storage version.
|
// flagged as storage version.
|
||||||
Storage bool `json:"storage" protobuf:"varint,3,opt,name=storage"`
|
Storage bool `json:"storage" protobuf:"varint,3,opt,name=storage"`
|
||||||
|
// Schema describes the schema for CustomResource used in validation, pruning, and defaulting.
|
||||||
|
// Top-level and per-version schemas are mutually exclusive.
|
||||||
|
// Per-version schemas must not all be set to identical values (top-level validation schema should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// +optional
|
||||||
|
Schema *CustomResourceValidation `json:"schema,omitempty" protobuf:"bytes,4,opt,name=schema"`
|
||||||
|
// Subresources describes the subresources for CustomResource
|
||||||
|
// Top-level and per-version subresources are mutually exclusive.
|
||||||
|
// Per-version subresources must not all be set to identical values (top-level subresources should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// +optional
|
||||||
|
Subresources *CustomResourceSubresources `json:"subresources,omitempty" protobuf:"bytes,5,opt,name=subresources"`
|
||||||
|
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||||
|
// Top-level and per-version columns are mutually exclusive.
|
||||||
|
// Per-version columns must not all be set to identical values (top-level columns should be used instead)
|
||||||
|
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||||
|
// NOTE: CRDs created prior to 1.13 populated the top-level additionalPrinterColumns field by default. To apply an
|
||||||
|
// update that changes to per-version additionalPrinterColumns, the top-level additionalPrinterColumns field must
|
||||||
|
// be explicitly set to null
|
||||||
|
// +optional
|
||||||
|
AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,6,rep,name=additionalPrinterColumns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomResourceColumnDefinition specifies a column for server side printing.
|
// CustomResourceColumnDefinition specifies a column for server side printing.
|
||||||
|
@@ -464,7 +464,17 @@ func autoConvert_v1beta1_CustomResourceDefinitionSpec_To_apiextensions_CustomRes
|
|||||||
out.Validation = nil
|
out.Validation = nil
|
||||||
}
|
}
|
||||||
out.Subresources = (*apiextensions.CustomResourceSubresources)(unsafe.Pointer(in.Subresources))
|
out.Subresources = (*apiextensions.CustomResourceSubresources)(unsafe.Pointer(in.Subresources))
|
||||||
out.Versions = *(*[]apiextensions.CustomResourceDefinitionVersion)(unsafe.Pointer(&in.Versions))
|
if in.Versions != nil {
|
||||||
|
in, out := &in.Versions, &out.Versions
|
||||||
|
*out = make([]apiextensions.CustomResourceDefinitionVersion, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if err := Convert_v1beta1_CustomResourceDefinitionVersion_To_apiextensions_CustomResourceDefinitionVersion(&(*in)[i], &(*out)[i], s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Versions = nil
|
||||||
|
}
|
||||||
out.AdditionalPrinterColumns = *(*[]apiextensions.CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns))
|
out.AdditionalPrinterColumns = *(*[]apiextensions.CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns))
|
||||||
out.Conversion = (*apiextensions.CustomResourceConversion)(unsafe.Pointer(in.Conversion))
|
out.Conversion = (*apiextensions.CustomResourceConversion)(unsafe.Pointer(in.Conversion))
|
||||||
return nil
|
return nil
|
||||||
@@ -492,7 +502,17 @@ func autoConvert_apiextensions_CustomResourceDefinitionSpec_To_v1beta1_CustomRes
|
|||||||
out.Validation = nil
|
out.Validation = nil
|
||||||
}
|
}
|
||||||
out.Subresources = (*CustomResourceSubresources)(unsafe.Pointer(in.Subresources))
|
out.Subresources = (*CustomResourceSubresources)(unsafe.Pointer(in.Subresources))
|
||||||
out.Versions = *(*[]CustomResourceDefinitionVersion)(unsafe.Pointer(&in.Versions))
|
if in.Versions != nil {
|
||||||
|
in, out := &in.Versions, &out.Versions
|
||||||
|
*out = make([]CustomResourceDefinitionVersion, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if err := Convert_apiextensions_CustomResourceDefinitionVersion_To_v1beta1_CustomResourceDefinitionVersion(&(*in)[i], &(*out)[i], s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Versions = nil
|
||||||
|
}
|
||||||
out.AdditionalPrinterColumns = *(*[]CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns))
|
out.AdditionalPrinterColumns = *(*[]CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns))
|
||||||
out.Conversion = (*CustomResourceConversion)(unsafe.Pointer(in.Conversion))
|
out.Conversion = (*CustomResourceConversion)(unsafe.Pointer(in.Conversion))
|
||||||
return nil
|
return nil
|
||||||
@@ -535,6 +555,17 @@ func autoConvert_v1beta1_CustomResourceDefinitionVersion_To_apiextensions_Custom
|
|||||||
out.Name = in.Name
|
out.Name = in.Name
|
||||||
out.Served = in.Served
|
out.Served = in.Served
|
||||||
out.Storage = in.Storage
|
out.Storage = in.Storage
|
||||||
|
if in.Schema != nil {
|
||||||
|
in, out := &in.Schema, &out.Schema
|
||||||
|
*out = new(apiextensions.CustomResourceValidation)
|
||||||
|
if err := Convert_v1beta1_CustomResourceValidation_To_apiextensions_CustomResourceValidation(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Schema = nil
|
||||||
|
}
|
||||||
|
out.Subresources = (*apiextensions.CustomResourceSubresources)(unsafe.Pointer(in.Subresources))
|
||||||
|
out.AdditionalPrinterColumns = *(*[]apiextensions.CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +578,17 @@ func autoConvert_apiextensions_CustomResourceDefinitionVersion_To_v1beta1_Custom
|
|||||||
out.Name = in.Name
|
out.Name = in.Name
|
||||||
out.Served = in.Served
|
out.Served = in.Served
|
||||||
out.Storage = in.Storage
|
out.Storage = in.Storage
|
||||||
|
if in.Schema != nil {
|
||||||
|
in, out := &in.Schema, &out.Schema
|
||||||
|
*out = new(CustomResourceValidation)
|
||||||
|
if err := Convert_apiextensions_CustomResourceValidation_To_v1beta1_CustomResourceValidation(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Schema = nil
|
||||||
|
}
|
||||||
|
out.Subresources = (*CustomResourceSubresources)(unsafe.Pointer(in.Subresources))
|
||||||
|
out.AdditionalPrinterColumns = *(*[]CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -264,7 +264,9 @@ func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefiniti
|
|||||||
if in.Versions != nil {
|
if in.Versions != nil {
|
||||||
in, out := &in.Versions, &out.Versions
|
in, out := &in.Versions, &out.Versions
|
||||||
*out = make([]CustomResourceDefinitionVersion, len(*in))
|
*out = make([]CustomResourceDefinitionVersion, len(*in))
|
||||||
copy(*out, *in)
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if in.AdditionalPrinterColumns != nil {
|
if in.AdditionalPrinterColumns != nil {
|
||||||
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
|
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
|
||||||
@@ -321,6 +323,21 @@ func (in *CustomResourceDefinitionStatus) DeepCopy() *CustomResourceDefinitionSt
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *CustomResourceDefinitionVersion) DeepCopyInto(out *CustomResourceDefinitionVersion) {
|
func (in *CustomResourceDefinitionVersion) DeepCopyInto(out *CustomResourceDefinitionVersion) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Schema != nil {
|
||||||
|
in, out := &in.Schema, &out.Schema
|
||||||
|
*out = new(CustomResourceValidation)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Subresources != nil {
|
||||||
|
in, out := &in.Subresources, &out.Subresources
|
||||||
|
*out = new(CustomResourceSubresources)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.AdditionalPrinterColumns != nil {
|
||||||
|
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
|
||||||
|
*out = make([]CustomResourceColumnDefinition, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:go_default_library",
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||||
|
@@ -18,15 +18,16 @@ package validation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
|
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
validationutil "k8s.io/apimachinery/pkg/util/validation"
|
validationutil "k8s.io/apimachinery/pkg/util/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||||
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
||||||
@@ -99,6 +100,17 @@ func ValidateUpdateCustomResourceDefinitionStatus(obj, oldObj *apiextensions.Cus
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateCustomResourceDefinitionVersion statically validates.
|
||||||
|
func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path, statusEnabled bool) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(version.Schema, statusEnabled, fldPath.Child("schema"))...)
|
||||||
|
allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(version.Subresources, fldPath.Child("subresources"))...)
|
||||||
|
for i := range version.AdditionalPrinterColumns {
|
||||||
|
allErrs = append(allErrs, ValidateCustomResourceColumnDefinition(&version.AdditionalPrinterColumns[i], fldPath.Child("additionalPrinterColumns").Index(i))...)
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateCustomResourceDefinitionSpec statically validates
|
// ValidateCustomResourceDefinitionSpec statically validates
|
||||||
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
@@ -128,7 +140,32 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
|||||||
if errs := validationutil.IsDNS1035Label(version.Name); len(errs) > 0 {
|
if errs := validationutil.IsDNS1035Label(version.Name); len(errs) > 0 {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
|
||||||
}
|
}
|
||||||
|
subresources := getSubresourcesForVersion(spec, version.Name)
|
||||||
|
allErrs = append(allErrs, ValidateCustomResourceDefinitionVersion(&version, fldPath.Child("versions").Index(i), hasStatusEnabled(subresources))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The top-level and per-version fields are mutual exclusive
|
||||||
|
if spec.Validation != nil && hasPerVersionSchema(spec.Versions) {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("validation"), "top-level and per-version schemas are mutually exclusive"))
|
||||||
|
}
|
||||||
|
if spec.Subresources != nil && hasPerVersionSubresources(spec.Versions) {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("subresources"), "top-level and per-version subresources are mutually exclusive"))
|
||||||
|
}
|
||||||
|
if len(spec.AdditionalPrinterColumns) > 0 && hasPerVersionColumns(spec.Versions) {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalPrinterColumns"), "top-level and per-version additionalPrinterColumns are mutually exclusive"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-version fields may not all be set to identical values (top-level field should be used instead)
|
||||||
|
if hasIdenticalPerVersionSchema(spec.Versions) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "per-version schemas may not all be set to identical values (top-level validation should be used instead)"))
|
||||||
|
}
|
||||||
|
if hasIdenticalPerVersionSubresources(spec.Versions) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "per-version subresources may not all be set to identical values (top-level subresources should be used instead)"))
|
||||||
|
}
|
||||||
|
if hasIdenticalPerVersionColumns(spec.Versions) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "per-version additionalPrinterColumns may not all be set to identical values (top-level additionalPrinterColumns should be used instead)"))
|
||||||
|
}
|
||||||
|
|
||||||
if !uniqueNames {
|
if !uniqueNames {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "must contain unique version names"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "must contain unique version names"))
|
||||||
}
|
}
|
||||||
@@ -161,11 +198,7 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
|||||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&spec.Names, fldPath.Child("names"))...)
|
allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&spec.Names, fldPath.Child("names"))...)
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
|
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
|
||||||
statusEnabled := false
|
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(spec.Validation, hasAnyStatusEnabled(spec), fldPath.Child("validation"))...)
|
||||||
if spec.Subresources != nil && spec.Subresources.Status != nil {
|
|
||||||
statusEnabled = true
|
|
||||||
}
|
|
||||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(spec.Validation, statusEnabled, fldPath.Child("validation"))...)
|
|
||||||
} else if spec.Validation != nil {
|
} else if spec.Validation != nil {
|
||||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("validation"), "disabled by feature-gate CustomResourceValidation"))
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("validation"), "disabled by feature-gate CustomResourceValidation"))
|
||||||
}
|
}
|
||||||
@@ -177,7 +210,7 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range spec.AdditionalPrinterColumns {
|
for i := range spec.AdditionalPrinterColumns {
|
||||||
if errs := ValidateCustomResourceColumnDefinition(&spec.AdditionalPrinterColumns[i], fldPath.Child("columns").Index(i)); len(errs) > 0 {
|
if errs := ValidateCustomResourceColumnDefinition(&spec.AdditionalPrinterColumns[i], fldPath.Child("additionalPrinterColumns").Index(i)); len(errs) > 0 {
|
||||||
allErrs = append(allErrs, errs...)
|
allErrs = append(allErrs, errs...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,6 +283,118 @@ func ValidateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.Cus
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSubresourcesForVersion returns the subresources for given version in given CRD spec.
|
||||||
|
// NOTE That this function assumes version always exist since it's used by the validation process
|
||||||
|
// that iterates through the existing versions.
|
||||||
|
func getSubresourcesForVersion(crd *apiextensions.CustomResourceDefinitionSpec, version string) *apiextensions.CustomResourceSubresources {
|
||||||
|
if !hasPerVersionSubresources(crd.Versions) {
|
||||||
|
return crd.Subresources
|
||||||
|
}
|
||||||
|
for _, v := range crd.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return v.Subresources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasAnyStatusEnabled returns true if given CRD spec has at least one Status Subresource set
|
||||||
|
// among the top-level and per-version Subresources.
|
||||||
|
func hasAnyStatusEnabled(crd *apiextensions.CustomResourceDefinitionSpec) bool {
|
||||||
|
if hasStatusEnabled(crd.Subresources) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, v := range crd.Versions {
|
||||||
|
if hasStatusEnabled(v.Subresources) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasStatusEnabled returns true if given CRD Subresources has non-nil Status set.
|
||||||
|
func hasStatusEnabled(subresources *apiextensions.CustomResourceSubresources) bool {
|
||||||
|
if subresources != nil && subresources.Status != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionSchema returns true if a CRD uses per-version schema.
|
||||||
|
func hasPerVersionSchema(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Schema != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionSubresources returns true if a CRD uses per-version subresources.
|
||||||
|
func hasPerVersionSubresources(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Subresources != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionColumns returns true if a CRD uses per-version columns.
|
||||||
|
func hasPerVersionColumns(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if len(v.AdditionalPrinterColumns) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasIdenticalPerVersionSchema returns true if a CRD sets identical non-nil values
|
||||||
|
// to all per-version schemas
|
||||||
|
func hasIdenticalPerVersionSchema(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
if len(versions) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value := versions[0].Schema
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Schema == nil || !apiequality.Semantic.DeepEqual(v.Schema, value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasIdenticalPerVersionSubresources returns true if a CRD sets identical non-nil values
|
||||||
|
// to all per-version subresources
|
||||||
|
func hasIdenticalPerVersionSubresources(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
if len(versions) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value := versions[0].Subresources
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Subresources == nil || !apiequality.Semantic.DeepEqual(v.Subresources, value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasIdenticalPerVersionColumns returns true if a CRD sets identical non-nil values
|
||||||
|
// to all per-version columns
|
||||||
|
func hasIdenticalPerVersionColumns(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
if len(versions) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
value := versions[0].AdditionalPrinterColumns
|
||||||
|
for _, v := range versions {
|
||||||
|
if len(v.AdditionalPrinterColumns) == 0 || !apiequality.Semantic.DeepEqual(v.AdditionalPrinterColumns, value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateCustomResourceDefinitionStatus statically validates
|
// ValidateCustomResourceDefinitionStatus statically validates
|
||||||
func ValidateCustomResourceDefinitionStatus(status *apiextensions.CustomResourceDefinitionStatus, fldPath *field.Path) field.ErrorList {
|
func ValidateCustomResourceDefinitionStatus(status *apiextensions.CustomResourceDefinitionStatus, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
@@ -594,6 +594,55 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
|||||||
},
|
},
|
||||||
errors: []validationMatch{},
|
errors: []validationMatch{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "per-version fields may not all be set to identical values (top-level field should be used instead)",
|
||||||
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Version: "version",
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
Schema: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validValidationSchema,
|
||||||
|
},
|
||||||
|
Subresources: &apiextensions.CustomResourceSubresources{},
|
||||||
|
AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
Schema: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validValidationSchema,
|
||||||
|
},
|
||||||
|
Subresources: &apiextensions.CustomResourceSubresources{},
|
||||||
|
AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: apiextensions.NamespaceScoped,
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: []validationMatch{
|
||||||
|
// Per-version schema/subresources/columns may not all be set to identical values.
|
||||||
|
// Note that the test will fail if we de-duplicate the expected errors below.
|
||||||
|
invalid("spec", "versions"),
|
||||||
|
invalid("spec", "versions"),
|
||||||
|
invalid("spec", "versions"),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -1003,6 +1052,87 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
|||||||
immutable("spec", "names", "plural"),
|
immutable("spec", "names", "plural"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "top-level and per-version fields are mutually exclusive",
|
||||||
|
old: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "plural.group.com",
|
||||||
|
ResourceVersion: "42",
|
||||||
|
},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Version: "version",
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
Subresources: &apiextensions.CustomResourceSubresources{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: apiextensions.NamespaceScoped,
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "plural.group.com",
|
||||||
|
ResourceVersion: "42",
|
||||||
|
},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Version: "version",
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
Schema: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validValidationSchema,
|
||||||
|
},
|
||||||
|
Subresources: &apiextensions.CustomResourceSubresources{},
|
||||||
|
AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validValidationSchema,
|
||||||
|
},
|
||||||
|
Subresources: &apiextensions.CustomResourceSubresources{},
|
||||||
|
Scope: apiextensions.NamespaceScoped,
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: []validationMatch{
|
||||||
|
forbidden("spec", "validation"),
|
||||||
|
forbidden("spec", "subresources"),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -1090,7 +1220,27 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "all allowed fields at the root of the schema with status",
|
name: "all allowed fields at the root of the schema with status",
|
||||||
input: apiextensions.CustomResourceValidation{
|
input: apiextensions.CustomResourceValidation{
|
||||||
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
OpenAPIV3Schema: validValidationSchema,
|
||||||
|
},
|
||||||
|
statusEnabled: true,
|
||||||
|
wantError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := ValidateCustomResourceDefinitionValidation(&tt.input, tt.statusEnabled, field.NewPath("spec", "validation"))
|
||||||
|
if !tt.wantError && len(got) > 0 {
|
||||||
|
t.Errorf("Expected no error, but got: %v", got)
|
||||||
|
} else if tt.wantError && len(got) == 0 {
|
||||||
|
t.Error("Expected error, but got none")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var example = apiextensions.JSON(`"This is an example"`)
|
||||||
|
|
||||||
|
var validValidationSchema = &apiextensions.JSONSchemaProps{
|
||||||
Description: "This is a description",
|
Description: "This is a description",
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Format: "date-time",
|
Format: "date-time",
|
||||||
@@ -1119,25 +1269,7 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
|
|||||||
Description: "This is an external documentation description",
|
Description: "This is an external documentation description",
|
||||||
},
|
},
|
||||||
Example: &example,
|
Example: &example,
|
||||||
},
|
|
||||||
},
|
|
||||||
statusEnabled: true,
|
|
||||||
wantError: false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got := ValidateCustomResourceDefinitionValidation(&tt.input, tt.statusEnabled, field.NewPath("spec", "validation"))
|
|
||||||
if !tt.wantError && len(got) > 0 {
|
|
||||||
t.Errorf("Expected no error, but got: %v", got)
|
|
||||||
} else if tt.wantError && len(got) == 0 {
|
|
||||||
t.Error("Expected error, but got none")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var example = apiextensions.JSON(`"This is an example"`)
|
|
||||||
|
|
||||||
func float64Ptr(f float64) *float64 {
|
func float64Ptr(f float64) *float64 {
|
||||||
return &f
|
return &f
|
||||||
|
@@ -182,7 +182,9 @@ func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefiniti
|
|||||||
if in.Versions != nil {
|
if in.Versions != nil {
|
||||||
in, out := &in.Versions, &out.Versions
|
in, out := &in.Versions, &out.Versions
|
||||||
*out = make([]CustomResourceDefinitionVersion, len(*in))
|
*out = make([]CustomResourceDefinitionVersion, len(*in))
|
||||||
copy(*out, *in)
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if in.AdditionalPrinterColumns != nil {
|
if in.AdditionalPrinterColumns != nil {
|
||||||
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
|
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
|
||||||
@@ -239,6 +241,21 @@ func (in *CustomResourceDefinitionStatus) DeepCopy() *CustomResourceDefinitionSt
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *CustomResourceDefinitionVersion) DeepCopyInto(out *CustomResourceDefinitionVersion) {
|
func (in *CustomResourceDefinitionVersion) DeepCopyInto(out *CustomResourceDefinitionVersion) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Schema != nil {
|
||||||
|
in, out := &in.Schema, &out.Schema
|
||||||
|
*out = new(CustomResourceValidation)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Subresources != nil {
|
||||||
|
in, out := &in.Subresources, &out.Subresources
|
||||||
|
*out = new(CustomResourceSubresources)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.AdditionalPrinterColumns != nil {
|
||||||
|
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
|
||||||
|
*out = make([]CustomResourceColumnDefinition, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@ go_library(
|
|||||||
"customresource_discovery.go",
|
"customresource_discovery.go",
|
||||||
"customresource_discovery_controller.go",
|
"customresource_discovery_controller.go",
|
||||||
"customresource_handler.go",
|
"customresource_handler.go",
|
||||||
|
"helpers.go",
|
||||||
],
|
],
|
||||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver",
|
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver",
|
||||||
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver",
|
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver",
|
||||||
|
@@ -136,7 +136,11 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
|
|||||||
Categories: crd.Status.AcceptedNames.Categories,
|
Categories: crd.Status.AcceptedNames.Categories,
|
||||||
})
|
})
|
||||||
|
|
||||||
if crd.Spec.Subresources != nil && crd.Spec.Subresources.Status != nil {
|
subresources, err := getSubresourcesForVersion(crd, version.Version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if subresources != nil && subresources.Status != nil {
|
||||||
apiResourcesForDiscovery = append(apiResourcesForDiscovery, metav1.APIResource{
|
apiResourcesForDiscovery = append(apiResourcesForDiscovery, metav1.APIResource{
|
||||||
Name: crd.Status.AcceptedNames.Plural + "/status",
|
Name: crd.Status.AcceptedNames.Plural + "/status",
|
||||||
Namespaced: crd.Spec.Scope == apiextensions.NamespaceScoped,
|
Namespaced: crd.Spec.Scope == apiextensions.NamespaceScoped,
|
||||||
@@ -145,7 +149,7 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil {
|
if subresources != nil && subresources.Scale != nil {
|
||||||
apiResourcesForDiscovery = append(apiResourcesForDiscovery, metav1.APIResource{
|
apiResourcesForDiscovery = append(apiResourcesForDiscovery, metav1.APIResource{
|
||||||
Group: autoscaling.GroupName,
|
Group: autoscaling.GroupName,
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
|
@@ -220,10 +220,16 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var handler http.HandlerFunc
|
var handler http.HandlerFunc
|
||||||
|
subresources, err := getSubresourcesForVersion(crd, requestInfo.APIVersion)
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
http.Error(w, "the server could not properly serve the CR subresources", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case subresource == "status" && crd.Spec.Subresources != nil && crd.Spec.Subresources.Status != nil:
|
case subresource == "status" && subresources != nil && subresources.Status != nil:
|
||||||
handler = r.serveStatus(w, req, requestInfo, crdInfo, terminating, supportedTypes)
|
handler = r.serveStatus(w, req, requestInfo, crdInfo, terminating, supportedTypes)
|
||||||
case subresource == "scale" && crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil:
|
case subresource == "scale" && subresources != nil && subresources.Scale != nil:
|
||||||
handler = r.serveScale(w, req, requestInfo, crdInfo, terminating, supportedTypes)
|
handler = r.serveScale(w, req, requestInfo, crdInfo, terminating, supportedTypes)
|
||||||
case len(subresource) == 0:
|
case len(subresource) == 0:
|
||||||
handler = r.serveResource(w, req, requestInfo, crdInfo, terminating, supportedTypes)
|
handler = r.serveResource(w, req, requestInfo, crdInfo, terminating, supportedTypes)
|
||||||
@@ -443,19 +449,28 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
|||||||
typer := newUnstructuredObjectTyper(parameterScheme)
|
typer := newUnstructuredObjectTyper(parameterScheme)
|
||||||
creator := unstructuredCreator{}
|
creator := unstructuredCreator{}
|
||||||
|
|
||||||
validator, _, err := apiservervalidation.NewSchemaValidator(crd.Spec.Validation)
|
validationSchema, err := getSchemaForVersion(crd, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return nil, fmt.Errorf("the server could not properly serve the CR schema")
|
||||||
|
}
|
||||||
|
validator, _, err := apiservervalidation.NewSchemaValidator(validationSchema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var statusSpec *apiextensions.CustomResourceSubresourceStatus
|
var statusSpec *apiextensions.CustomResourceSubresourceStatus
|
||||||
var statusValidator *validate.SchemaValidator
|
var statusValidator *validate.SchemaValidator
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && crd.Spec.Subresources != nil && crd.Spec.Subresources.Status != nil {
|
subresources, err := getSubresourcesForVersion(crd, v.Name)
|
||||||
statusSpec = crd.Spec.Subresources.Status
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return nil, fmt.Errorf("the server could not properly serve the CR subresources")
|
||||||
|
}
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && subresources != nil && subresources.Status != nil {
|
||||||
|
statusSpec = subresources.Status
|
||||||
// for the status subresource, validate only against the status schema
|
// for the status subresource, validate only against the status schema
|
||||||
if crd.Spec.Validation != nil && crd.Spec.Validation.OpenAPIV3Schema != nil && crd.Spec.Validation.OpenAPIV3Schema.Properties != nil {
|
if validationSchema != nil && validationSchema.OpenAPIV3Schema != nil && validationSchema.OpenAPIV3Schema.Properties != nil {
|
||||||
if statusSchema, ok := crd.Spec.Validation.OpenAPIV3Schema.Properties["status"]; ok {
|
if statusSchema, ok := validationSchema.OpenAPIV3Schema.Properties["status"]; ok {
|
||||||
openapiSchema := &spec.Schema{}
|
openapiSchema := &spec.Schema{}
|
||||||
if err := apiservervalidation.ConvertJSONSchemaProps(&statusSchema, openapiSchema); err != nil {
|
if err := apiservervalidation.ConvertJSONSchemaProps(&statusSchema, openapiSchema); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -466,11 +481,16 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scaleSpec *apiextensions.CustomResourceSubresourceScale
|
var scaleSpec *apiextensions.CustomResourceSubresourceScale
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil {
|
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && subresources != nil && subresources.Scale != nil {
|
||||||
scaleSpec = crd.Spec.Subresources.Scale
|
scaleSpec = subresources.Scale
|
||||||
}
|
}
|
||||||
|
|
||||||
table, err := tableconvertor.New(crd.Spec.AdditionalPrinterColumns)
|
columns, err := getColumnsForVersion(crd, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return nil, fmt.Errorf("the server could not properly serve the CR columns")
|
||||||
|
}
|
||||||
|
table, err := tableconvertor.New(columns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(2).Infof("The CRD for %v has an invalid printer specification, falling back to default printing: %v", kind, err)
|
glog.V(2).Infof("The CRD for %v has an invalid printer specification, falling back to default printing: %v", kind, err)
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
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 apiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getSchemaForVersion returns the validation schema for given version in given CRD.
|
||||||
|
func getSchemaForVersion(crd *apiextensions.CustomResourceDefinition, version string) (*apiextensions.CustomResourceValidation, error) {
|
||||||
|
if !hasPerVersionSchema(crd.Spec.Versions) {
|
||||||
|
return crd.Spec.Validation, nil
|
||||||
|
}
|
||||||
|
if crd.Spec.Validation != nil {
|
||||||
|
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive", crd.Name, version)
|
||||||
|
}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return v.Schema, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSubresourcesForVersion returns the subresources for given version in given CRD.
|
||||||
|
func getSubresourcesForVersion(crd *apiextensions.CustomResourceDefinition, version string) (*apiextensions.CustomResourceSubresources, error) {
|
||||||
|
if !hasPerVersionSubresources(crd.Spec.Versions) {
|
||||||
|
return crd.Spec.Subresources, nil
|
||||||
|
}
|
||||||
|
if crd.Spec.Subresources != nil {
|
||||||
|
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive", crd.Name, version)
|
||||||
|
}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return v.Subresources, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getColumnsForVersion returns the columns for given version in given CRD.
|
||||||
|
// NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
|
||||||
|
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
|
||||||
|
// the original CRD object instead.
|
||||||
|
func getColumnsForVersion(crd *apiextensions.CustomResourceDefinition, version string) ([]apiextensions.CustomResourceColumnDefinition, error) {
|
||||||
|
if !hasPerVersionColumns(crd.Spec.Versions) {
|
||||||
|
return serveDefaultColumnsIfEmpty(crd.Spec.AdditionalPrinterColumns), nil
|
||||||
|
}
|
||||||
|
if len(crd.Spec.AdditionalPrinterColumns) > 0 {
|
||||||
|
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive", crd.Name, version)
|
||||||
|
}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveDefaultColumnsIfEmpty applies logically defaulting to columns, if the input columns is empty.
|
||||||
|
// NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
|
||||||
|
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
|
||||||
|
// the original CRD object instead.
|
||||||
|
func serveDefaultColumnsIfEmpty(columns []apiextensions.CustomResourceColumnDefinition) []apiextensions.CustomResourceColumnDefinition {
|
||||||
|
if len(columns) > 0 {
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
return []apiextensions.CustomResourceColumnDefinition{
|
||||||
|
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionSchema returns true if a CRD uses per-version schema.
|
||||||
|
func hasPerVersionSchema(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Schema != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionSubresources returns true if a CRD uses per-version subresources.
|
||||||
|
func hasPerVersionSubresources(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Subresources != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionColumns returns true if a CRD uses per-version columns.
|
||||||
|
func hasPerVersionColumns(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if len(v.AdditionalPrinterColumns) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@@ -41,7 +41,7 @@ const (
|
|||||||
// CustomResourceSubresources defines the subresources for CustomResources
|
// CustomResourceSubresources defines the subresources for CustomResources
|
||||||
CustomResourceSubresources utilfeature.Feature = "CustomResourceSubresources"
|
CustomResourceSubresources utilfeature.Feature = "CustomResourceSubresources"
|
||||||
|
|
||||||
// owner: @mbohlool
|
// owner: @mbohlool, @roycaihw
|
||||||
// alpha: v1.13
|
// alpha: v1.13
|
||||||
//
|
//
|
||||||
// CustomResourceWebhookConversion defines the webhook conversion for Custom Resources.
|
// CustomResourceWebhookConversion defines the webhook conversion for Custom Resources.
|
||||||
|
@@ -57,9 +57,25 @@ func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
|||||||
// if the feature gate is disabled, drop the feature.
|
// if the feature gate is disabled, drop the feature.
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
|
||||||
crd.Spec.Validation = nil
|
crd.Spec.Validation = nil
|
||||||
|
for i := range crd.Spec.Versions {
|
||||||
|
crd.Spec.Versions[i].Schema = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
|
||||||
crd.Spec.Subresources = nil
|
crd.Spec.Subresources = nil
|
||||||
|
for i := range crd.Spec.Versions {
|
||||||
|
crd.Spec.Versions[i].Subresources = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// On CREATE, if the CustomResourceWebhookConversion feature gate is off, we auto-clear
|
||||||
|
// the per-version fields. This is to be consistent with the other built-in types, as the
|
||||||
|
// apiserver drops unknown fields.
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) {
|
||||||
|
for i := range crd.Spec.Versions {
|
||||||
|
crd.Spec.Versions[i].Schema = nil
|
||||||
|
crd.Spec.Versions[i].Subresources = nil
|
||||||
|
crd.Spec.Versions[i].AdditionalPrinterColumns = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) && crd.Spec.Conversion != nil {
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) && crd.Spec.Conversion != nil {
|
||||||
crd.Spec.Conversion.WebhookClientConfig = nil
|
crd.Spec.Conversion.WebhookClientConfig = nil
|
||||||
@@ -96,10 +112,36 @@ func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
|||||||
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
|
||||||
newCRD.Spec.Validation = nil
|
newCRD.Spec.Validation = nil
|
||||||
oldCRD.Spec.Validation = nil
|
oldCRD.Spec.Validation = nil
|
||||||
|
for i := range newCRD.Spec.Versions {
|
||||||
|
newCRD.Spec.Versions[i].Schema = nil
|
||||||
|
}
|
||||||
|
for i := range oldCRD.Spec.Versions {
|
||||||
|
oldCRD.Spec.Versions[i].Schema = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
|
||||||
newCRD.Spec.Subresources = nil
|
newCRD.Spec.Subresources = nil
|
||||||
oldCRD.Spec.Subresources = nil
|
oldCRD.Spec.Subresources = nil
|
||||||
|
for i := range newCRD.Spec.Versions {
|
||||||
|
newCRD.Spec.Versions[i].Subresources = nil
|
||||||
|
}
|
||||||
|
for i := range oldCRD.Spec.Versions {
|
||||||
|
oldCRD.Spec.Versions[i].Subresources = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On UPDATE, if the CustomResourceWebhookConversion feature gate is off, we auto-clear
|
||||||
|
// the per-version fields if the old CRD doesn't use per-version fields already.
|
||||||
|
// This is to be consistent with the other built-in types, as the apiserver drops unknown
|
||||||
|
// fields. If the old CRD already uses per-version fields, the CRD is allowed to continue
|
||||||
|
// use per-version fields.
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) &&
|
||||||
|
!hasPerVersionField(oldCRD.Spec.Versions) {
|
||||||
|
for i := range newCRD.Spec.Versions {
|
||||||
|
newCRD.Spec.Versions[i].Schema = nil
|
||||||
|
newCRD.Spec.Versions[i].Subresources = nil
|
||||||
|
newCRD.Spec.Versions[i].AdditionalPrinterColumns = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) && newCRD.Spec.Conversion != nil {
|
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) && newCRD.Spec.Conversion != nil {
|
||||||
if oldCRD.Spec.Conversion == nil || newCRD.Spec.Conversion.WebhookClientConfig == nil {
|
if oldCRD.Spec.Conversion == nil || newCRD.Spec.Conversion.WebhookClientConfig == nil {
|
||||||
@@ -117,6 +159,16 @@ func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasPerVersionField returns true if a CRD uses per-version schema/subresources/columns fields.
|
||||||
|
func hasPerVersionField(versions []apiextensions.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Schema != nil || v.Subresources != nil || len(v.AdditionalPrinterColumns) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates a new CustomResourceDefinition.
|
// Validate validates a new CustomResourceDefinition.
|
||||||
func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition))
|
return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition))
|
||||||
|
@@ -26,6 +26,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server/options:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server/options:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/apiextensions-apiserver/test/integration/fixtures:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/test/integration/fixtures:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
@@ -40,6 +41,8 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||||
|
@@ -370,13 +370,13 @@ func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewScaleClient returns a scale client.
|
// CreateNewVersionedScaleClient returns a scale client.
|
||||||
func CreateNewScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config) (scale.ScalesGetter, error) {
|
func CreateNewVersionedScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) {
|
||||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
groupResource, err := discoveryClient.ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
|
groupResource, err := discoveryClient.ServerResourcesForGroupVersion(crd.Spec.Group + "/" + version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -386,12 +386,12 @@ func CreateNewScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, co
|
|||||||
Group: metav1.APIGroup{
|
Group: metav1.APIGroup{
|
||||||
Name: crd.Spec.Group,
|
Name: crd.Spec.Group,
|
||||||
Versions: []metav1.GroupVersionForDiscovery{
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
{Version: crd.Spec.Version},
|
{Version: version},
|
||||||
},
|
},
|
||||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: crd.Spec.Version},
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: version},
|
||||||
},
|
},
|
||||||
VersionedResources: map[string][]metav1.APIResource{
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
crd.Spec.Version: groupResource.APIResources,
|
version: groupResource.APIResources,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,8 @@ import (
|
|||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
|
||||||
|
|
||||||
func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
|
func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
|
||||||
return instantiateVersionedCustomResource(t, instanceToCreate, client, definition, definition.Spec.Versions[0].Name)
|
return instantiateVersionedCustomResource(t, instanceToCreate, client, definition, definition.Spec.Versions[0].Name)
|
||||||
}
|
}
|
||||||
@@ -92,3 +94,97 @@ func updateCustomResourceDefinitionWithRetry(client clientset.Interface, name st
|
|||||||
}
|
}
|
||||||
return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
|
return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSchemaForVersion returns the validation schema for given version in given CRD.
|
||||||
|
func getSchemaForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) (*apiextensionsv1beta1.CustomResourceValidation, error) {
|
||||||
|
if !hasPerVersionSchema(crd.Spec.Versions) {
|
||||||
|
return crd.Spec.Validation, nil
|
||||||
|
}
|
||||||
|
if crd.Spec.Validation != nil {
|
||||||
|
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive", crd.Name, version)
|
||||||
|
}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return v.Schema, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSubresourcesForVersion returns the subresources for given version in given CRD.
|
||||||
|
func getSubresourcesForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) (*apiextensionsv1beta1.CustomResourceSubresources, error) {
|
||||||
|
if !hasPerVersionSubresources(crd.Spec.Versions) {
|
||||||
|
return crd.Spec.Subresources, nil
|
||||||
|
}
|
||||||
|
if crd.Spec.Subresources != nil {
|
||||||
|
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive", crd.Name, version)
|
||||||
|
}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return v.Subresources, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getColumnsForVersion returns the columns for given version in given CRD.
|
||||||
|
// NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
|
||||||
|
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
|
||||||
|
// the original CRD object instead.
|
||||||
|
func getColumnsForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) ([]apiextensionsv1beta1.CustomResourceColumnDefinition, error) {
|
||||||
|
if !hasPerVersionColumns(crd.Spec.Versions) {
|
||||||
|
return serveDefaultColumnsIfEmpty(crd.Spec.AdditionalPrinterColumns), nil
|
||||||
|
}
|
||||||
|
if len(crd.Spec.AdditionalPrinterColumns) > 0 {
|
||||||
|
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive", crd.Name, version)
|
||||||
|
}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if version == v.Name {
|
||||||
|
return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveDefaultColumnsIfEmpty applies logically defaulting to columns, if the input columns is empty.
|
||||||
|
// NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
|
||||||
|
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
|
||||||
|
// the original CRD object instead.
|
||||||
|
func serveDefaultColumnsIfEmpty(columns []apiextensionsv1beta1.CustomResourceColumnDefinition) []apiextensionsv1beta1.CustomResourceColumnDefinition {
|
||||||
|
if len(columns) > 0 {
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
return []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||||
|
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionSchema returns true if a CRD uses per-version schema.
|
||||||
|
func hasPerVersionSchema(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Schema != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionSubresources returns true if a CRD uses per-version subresources.
|
||||||
|
func hasPerVersionSubresources(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v.Subresources != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPerVersionColumns returns true if a CRD uses per-version columns.
|
||||||
|
func hasPerVersionColumns(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if len(v.AdditionalPrinterColumns) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -29,17 +30,23 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
|
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
var labelSelectorPath = ".status.labelSelector"
|
var labelSelectorPath = ".status.labelSelector"
|
||||||
|
var anotherLabelSelectorPath = ".status.anotherLabelSelector"
|
||||||
|
|
||||||
func NewNoxuSubresourcesCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
func NewNoxuSubresourcesCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiextensionsv1beta1.CustomResourceDefinition {
|
||||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
return []*apiextensionsv1beta1.CustomResourceDefinition{
|
||||||
|
// CRD that uses top-level subresources
|
||||||
|
{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
Group: "mygroup.example.com",
|
Group: "mygroup.example.com",
|
||||||
@@ -51,11 +58,19 @@ func NewNoxuSubresourcesCRD(scope apiextensionsv1beta1.ResourceScope) *apiextens
|
|||||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||||
ListKind: "NoxuItemList",
|
ListKind: "NoxuItemList",
|
||||||
},
|
},
|
||||||
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
|
||||||
{Name: "v1beta1", Served: true, Storage: false},
|
|
||||||
{Name: "v1", Served: true, Storage: true},
|
|
||||||
},
|
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
|
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "v1beta1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||||
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||||
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
|
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
|
||||||
@@ -65,13 +80,58 @@ func NewNoxuSubresourcesCRD(scope apiextensionsv1beta1.ResourceScope) *apiextens
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
// CRD that uses per-version subresources
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||||
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
|
Group: "mygroup.example.com",
|
||||||
|
Version: "v1beta1",
|
||||||
|
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||||
|
Plural: "noxus",
|
||||||
|
Singular: "nonenglishnoxu",
|
||||||
|
Kind: "WishIHadChosenNoxu",
|
||||||
|
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||||
|
ListKind: "NoxuItemList",
|
||||||
|
},
|
||||||
|
Scope: scope,
|
||||||
|
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "v1beta1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||||
|
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||||
|
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
|
||||||
|
SpecReplicasPath: ".spec.replicas",
|
||||||
|
StatusReplicasPath: ".status.replicas",
|
||||||
|
LabelSelectorPath: &labelSelectorPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||||
|
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||||
|
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
|
||||||
|
SpecReplicasPath: ".spec.replicas",
|
||||||
|
StatusReplicasPath: ".status.replicas",
|
||||||
|
LabelSelectorPath: &anotherLabelSelectorPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNoxuSubresourceInstance(namespace, name string) *unstructured.Unstructured {
|
func NewNoxuSubresourceInstance(namespace, name, version string) *unstructured.Unstructured {
|
||||||
return &unstructured.Unstructured{
|
return &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]interface{}{
|
||||||
"apiVersion": "mygroup.example.com/v1beta1",
|
"apiVersion": fmt.Sprintf("mygroup.example.com/%s", version),
|
||||||
"kind": "WishIHadChosenNoxu",
|
"kind": "WishIHadChosenNoxu",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"namespace": namespace,
|
"namespace": namespace,
|
||||||
@@ -89,30 +149,31 @@ func NewNoxuSubresourceInstance(namespace, name string) *unstructured.Unstructur
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStatusSubresource(t *testing.T) {
|
func TestStatusSubresource(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
_, err = instantiateVersionedCustomResource(t, NewNoxuSubresourceInstance(ns, "foo", v.Name), noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// status should not be set after creation
|
// status should not be set after creation
|
||||||
if val, ok := gottenNoxuInstance.Object["status"]; ok {
|
if val, ok := gottenNoxuInstance.Object["status"]; ok {
|
||||||
t.Fatalf("status should not be set after creation, got %v", val)
|
t.Fatalf("status should not be set after creation, got %v", val)
|
||||||
@@ -192,9 +253,16 @@ func TestStatusSubresource(t *testing.T) {
|
|||||||
if statusNum != int64(20) {
|
if statusNum != int64(20) {
|
||||||
t.Fatalf(".status.num: expected: %v, got: %v", int64(20), statusNum)
|
t.Fatalf(".status.num: expected: %v, got: %v", int64(20), statusNum)
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScaleSubresource(t *testing.T) {
|
func TestScaleSubresource(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
groupResource := schema.GroupResource{
|
groupResource := schema.GroupResource{
|
||||||
Group: "mygroup.example.com",
|
Group: "mygroup.example.com",
|
||||||
Resource: "noxus",
|
Resource: "noxus",
|
||||||
@@ -215,29 +283,37 @@ func TestScaleSubresource(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
|
// Start with a new CRD, so that the object doesn't have resourceVersion
|
||||||
|
noxuDefinition := noxuDefinition.DeepCopy()
|
||||||
|
|
||||||
|
subresources, err := getSubresourcesForVersion(noxuDefinition, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
// set invalid json path for specReplicasPath
|
// set invalid json path for specReplicasPath
|
||||||
noxuDefinition.Spec.Subresources.Scale.SpecReplicasPath = "foo,bar"
|
subresources.Scale.SpecReplicasPath = "foo,bar"
|
||||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: specReplicasPath should be a valid json path under .spec")
|
t.Fatalf("unexpected non-error: specReplicasPath should be a valid json path under .spec")
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition.Spec.Subresources.Scale.SpecReplicasPath = ".spec.replicas"
|
subresources.Scale.SpecReplicasPath = ".spec.replicas"
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
_, err = instantiateVersionedCustomResource(t, NewNoxuSubresourceInstance(ns, "foo", v.Name), noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scaleClient, err := fixtures.CreateNewScaleClient(noxuDefinition, config)
|
scaleClient, err := fixtures.CreateNewVersionedScaleClient(noxuDefinition, config, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -247,7 +323,7 @@ func TestScaleSubresource(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = unstructured.SetNestedField(gottenNoxuInstance.Object, "bar", "status", "labelSelector")
|
err = unstructured.SetNestedField(gottenNoxuInstance.Object, "bar", strings.Split((*subresources.Scale.LabelSelectorPath)[1:], ".")...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -269,7 +345,7 @@ func TestScaleSubresource(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check self link
|
// check self link
|
||||||
expectedSelfLink := "/apis/mygroup.example.com/v1beta1/namespaces/not-the-default/noxus/foo/scale"
|
expectedSelfLink := fmt.Sprintf("/apis/mygroup.example.com/%s/namespaces/not-the-default/noxus/foo/scale", v.Name)
|
||||||
if gottenScale.GetSelfLink() != expectedSelfLink {
|
if gottenScale.GetSelfLink() != expectedSelfLink {
|
||||||
t.Fatalf("Scale.Metadata.SelfLink: expected: %v, got: %v", expectedSelfLink, gottenScale.GetSelfLink())
|
t.Fatalf("Scale.Metadata.SelfLink: expected: %v, got: %v", expectedSelfLink, gottenScale.GetSelfLink())
|
||||||
}
|
}
|
||||||
@@ -301,9 +377,9 @@ func TestScaleSubresource(t *testing.T) {
|
|||||||
if specReplicas != 5 {
|
if specReplicas != 5 {
|
||||||
t.Fatalf("replicas: expected: %v, got: %v", 5, specReplicas)
|
t.Fatalf("replicas: expected: %v, got: %v", 5, specReplicas)
|
||||||
}
|
}
|
||||||
statusLabelSelector, found, err := unstructured.NestedString(updatedNoxuInstance.Object, "status", "labelSelector")
|
statusLabelSelector, found, err := unstructured.NestedString(updatedNoxuInstance.Object, strings.Split((*subresources.Scale.LabelSelectorPath)[1:], ".")...)
|
||||||
if !found || err != nil {
|
if !found || err != nil {
|
||||||
t.Fatalf("unable to get .status.labelSelector")
|
t.Fatalf("unable to get %s", *subresources.Scale.LabelSelectorPath)
|
||||||
}
|
}
|
||||||
if statusLabelSelector != "bar" {
|
if statusLabelSelector != "bar" {
|
||||||
t.Fatalf("scale should not update status: expected %v, got: %v", "bar", statusLabelSelector)
|
t.Fatalf("scale should not update status: expected %v, got: %v", "bar", statusLabelSelector)
|
||||||
@@ -323,9 +399,16 @@ func TestScaleSubresource(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: .spec.replicas should be less than 2147483647")
|
t.Fatalf("unexpected non-error: .spec.replicas should be less than 2147483647")
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidationSchemaWithStatus(t *testing.T) {
|
func TestValidationSchemaWithStatus(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -342,7 +425,7 @@ func TestValidationSchemaWithStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fields other than properties in root schema are not allowed
|
// fields other than properties in root schema are not allowed
|
||||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[0]
|
||||||
noxuDefinition.Spec.Subresources = &apiextensionsv1beta1.CustomResourceSubresources{
|
noxuDefinition.Spec.Subresources = &apiextensionsv1beta1.CustomResourceSubresources{
|
||||||
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||||
}
|
}
|
||||||
@@ -373,6 +456,7 @@ func TestValidationSchemaWithStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateOnlyStatus(t *testing.T) {
|
func TestValidateOnlyStatus(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -407,26 +491,39 @@ func TestValidateOnlyStatus(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for i, noxuDefinition := range noxuDefinitions {
|
||||||
|
if i == 0 {
|
||||||
noxuDefinition.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
noxuDefinition.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
OpenAPIV3Schema: schema,
|
OpenAPIV3Schema: schema,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
noxuDefinition.Spec.Versions[0].Schema = &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: schema,
|
||||||
|
}
|
||||||
|
schemaWithDescription := schema.DeepCopy()
|
||||||
|
schemaWithDescription.Description = "test"
|
||||||
|
noxuDefinition.Spec.Versions[1].Schema = &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: schemaWithDescription,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
|
||||||
// set .spec.num = 10 and .status.num = 10
|
// set .spec.num = 10 and .status.num = 10
|
||||||
noxuInstance := NewNoxuSubresourceInstance(ns, "foo")
|
noxuInstance := NewNoxuSubresourceInstance(ns, "foo", v.Name)
|
||||||
err = unstructured.SetNestedField(noxuInstance.Object, int64(10), "status", "num")
|
err = unstructured.SetNestedField(noxuInstance.Object, int64(10), "status", "num")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
createdNoxuInstance, err := instantiateCustomResource(t, noxuInstance, noxuResourceClient, noxuDefinition)
|
createdNoxuInstance, err := instantiateVersionedCustomResource(t, noxuInstance, noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
@@ -457,9 +554,16 @@ func TestValidateOnlyStatus(t *testing.T) {
|
|||||||
if !strings.Contains(statusError.Error(), "Invalid value") {
|
if !strings.Contains(statusError.Error(), "Invalid value") {
|
||||||
t.Fatalf("expected 'Invalid value' in error, got: %v", err)
|
t.Fatalf("expected 'Invalid value' in error, got: %v", err)
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubresourcesDiscovery(t *testing.T) {
|
func TestSubresourcesDiscovery(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -475,14 +579,16 @@ func TestSubresourcesDiscovery(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
group := "mygroup.example.com"
|
group := "mygroup.example.com"
|
||||||
version := "v1beta1"
|
version := v.Name
|
||||||
|
|
||||||
resources, err := apiExtensionClient.Discovery().ServerResourcesForGroupVersion(group + "/" + version)
|
resources, err := apiExtensionClient.Discovery().ServerResourcesForGroupVersion(group + "/" + version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -542,23 +648,31 @@ func TestSubresourcesDiscovery(t *testing.T) {
|
|||||||
t.Fatalf("incorrect scale via discovery: expected: %v, got: %v", expectedVerbs, scale.Verbs)
|
t.Fatalf("incorrect scale via discovery: expected: %v, got: %v", expectedVerbs, scale.Verbs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGeneration(t *testing.T) {
|
func TestGeneration(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
_, err = instantiateVersionedCustomResource(t, NewNoxuSubresourceInstance(ns, "foo", v.Name), noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
@@ -606,9 +720,16 @@ func TestGeneration(t *testing.T) {
|
|||||||
if updatedInstance.GetGeneration() != 2 {
|
if updatedInstance.GetGeneration() != 2 {
|
||||||
t.Fatalf("updating spec should increment .metadata.generation: expected: %v, got: %v", 2, updatedStatusInstance.GetGeneration())
|
t.Fatalf("updating spec should increment .metadata.generation: expected: %v, got: %v", 2, updatedStatusInstance.GetGeneration())
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubresourcePatch(t *testing.T) {
|
func TestSubresourcePatch(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
groupResource := schema.GroupResource{
|
groupResource := schema.GroupResource{
|
||||||
Group: "mygroup.example.com",
|
Group: "mygroup.example.com",
|
||||||
Resource: "noxus",
|
Resource: "noxus",
|
||||||
@@ -629,22 +750,24 @@ func TestSubresourcePatch(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
|
||||||
t.Logf("Creating foo")
|
t.Logf("Creating foo")
|
||||||
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
_, err = instantiateVersionedCustomResource(t, NewNoxuSubresourceInstance(ns, "foo", v.Name), noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scaleClient, err := fixtures.CreateNewScaleClient(noxuDefinition, config)
|
scaleClient, err := fixtures.CreateNewVersionedScaleClient(noxuDefinition, config, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -694,6 +817,7 @@ func TestSubresourcePatch(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// an empty patch is a no-op patch. make sure it does not increment resourceVersion
|
// an empty patch is a no-op patch. make sure it does not increment resourceVersion
|
||||||
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "status", "num")
|
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 999, "status", "num")
|
||||||
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 10, "spec", "num")
|
expectInt64(t, patchedNoxuInstance.UnstructuredContent(), 10, "spec", "num")
|
||||||
@@ -771,4 +895,10 @@ func TestSubresourcePatch(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources")
|
t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources")
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,10 +28,14 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,6 +52,11 @@ func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition {
|
|||||||
ListKind: "TablemList",
|
ListKind: "TablemList",
|
||||||
},
|
},
|
||||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||||
|
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "v1beta1",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||||
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
||||||
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
||||||
@@ -56,13 +65,28 @@ func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition {
|
|||||||
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
|
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "v1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||||
|
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
||||||
|
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
||||||
|
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"},
|
||||||
|
{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"},
|
||||||
|
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
|
||||||
|
{Name: "Zeta", Type: "integer", Description: "the zeta field", Format: "int64", Priority: 42, JSONPath: ".spec.zeta"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTableInstance(name string) *unstructured.Unstructured {
|
func newTableInstance(name string) *unstructured.Unstructured {
|
||||||
return &unstructured.Unstructured{
|
return &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]interface{}{
|
||||||
"apiVersion": "mygroup.example.com/v1beta1",
|
"apiVersion": "mygroup.example.com/v1",
|
||||||
"kind": "Table",
|
"kind": "Table",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -73,12 +97,14 @@ func newTableInstance(name string) *unstructured.Unstructured {
|
|||||||
"gamma": "bar",
|
"gamma": "bar",
|
||||||
"delta": "hello",
|
"delta": "hello",
|
||||||
"epsilon": []int64{1, 2, 3},
|
"epsilon": []int64{1, 2, 3},
|
||||||
|
"zeta": 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableGet(t *testing.T) {
|
func TestTableGet(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -107,14 +133,15 @@ func TestTableGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("table crd created: %#v", crd)
|
t.Logf("table crd created: %#v", crd)
|
||||||
|
|
||||||
crClient := newNamespacedCustomResourceClient("", dynamicClient, crd)
|
crClient := newNamespacedCustomResourceVersionedClient("", dynamicClient, crd, "v1")
|
||||||
foo, err := crClient.Create(newTableInstance("foo"), metav1.CreateOptions{})
|
foo, err := crClient.Create(newTableInstance("foo"), metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
t.Logf("foo created: %#v", foo.UnstructuredContent())
|
t.Logf("foo created: %#v", foo.UnstructuredContent())
|
||||||
|
|
||||||
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version}
|
for i, v := range crd.Spec.Versions {
|
||||||
|
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name}
|
||||||
gvk := gv.WithKind(crd.Spec.Names.Kind)
|
gvk := gv.WithKind(crd.Spec.Names.Kind)
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
@@ -149,7 +176,12 @@ func TestTableGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("%v table list: %#v", gvk, tbl)
|
t.Logf("%v table list: %#v", gvk, tbl)
|
||||||
|
|
||||||
if got, expected := len(tbl.ColumnDefinitions), 6; got != expected {
|
columns, err := getColumnsForVersion(crd, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectColumnNum := len(columns) + 1
|
||||||
|
if got, expected := len(tbl.ColumnDefinitions), expectColumnNum; got != expected {
|
||||||
t.Errorf("expected %d headers, got %d", expected, got)
|
t.Errorf("expected %d headers, got %d", expected, got)
|
||||||
} else {
|
} else {
|
||||||
age := metav1beta1.TableColumnDefinition{Name: "Age", Type: "date", Format: "", Description: "Custom resource definition column (in JSONPath format): .metadata.creationTimestamp", Priority: 0}
|
age := metav1beta1.TableColumnDefinition{Name: "Age", Type: "date", Format: "", Description: "Custom resource definition column (in JSONPath format): .metadata.creationTimestamp", Priority: 0}
|
||||||
@@ -176,10 +208,18 @@ func TestTableGet(t *testing.T) {
|
|||||||
if got, expected := tbl.ColumnDefinitions[5], epsilon; got != expected {
|
if got, expected := tbl.ColumnDefinitions[5], epsilon; got != expected {
|
||||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate extra column for v1
|
||||||
|
if i == 1 {
|
||||||
|
zeta := metav1beta1.TableColumnDefinition{Name: "Zeta", Type: "integer", Format: "int64", Description: "the zeta field", Priority: 42}
|
||||||
|
if got, expected := tbl.ColumnDefinitions[6], zeta; got != expected {
|
||||||
|
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if got, expected := len(tbl.Rows), 1; got != expected {
|
if got, expected := len(tbl.Rows), 1; got != expected {
|
||||||
t.Errorf("expected %d rows, got %d", expected, got)
|
t.Errorf("expected %d rows, got %d", expected, got)
|
||||||
} else if got, expected := len(tbl.Rows[0].Cells), 6; got != expected {
|
} else if got, expected := len(tbl.Rows[0].Cells), expectColumnNum; got != expected {
|
||||||
t.Errorf("expected %d cells, got %d", expected, got)
|
t.Errorf("expected %d cells, got %d", expected, got)
|
||||||
} else {
|
} else {
|
||||||
if got, expected := tbl.Rows[0].Cells[0], "foo"; got != expected {
|
if got, expected := tbl.Rows[0].Cells[0], "foo"; got != expected {
|
||||||
@@ -207,8 +247,106 @@ func TestTableGet(t *testing.T) {
|
|||||||
if got, expected := tbl.Rows[0].Cells[5], "[1 2 3]"; got != expected {
|
if got, expected := tbl.Rows[0].Cells[5], "[1 2 3]"; got != expected {
|
||||||
t.Errorf("expected cell[5] to equal %q, got %q", expected, got)
|
t.Errorf("expected cell[5] to equal %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
|
// Validate extra column for v1
|
||||||
|
if i == 1 {
|
||||||
|
if got, expected := tbl.Rows[0].Cells[6], int64(5); got != expected {
|
||||||
|
t.Errorf("expected cell[6] to equal %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestColumnsPatch tests the case that a CRD was created with no top-level or
|
||||||
|
// per-version columns. One should be able to PATCH the CRD setting per-version columns.
|
||||||
|
func TestColumnsPatch(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tearDown()
|
||||||
|
|
||||||
|
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicClient, err := dynamic.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRD with no top-level and per-version columns should be created successfully
|
||||||
|
crd := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)[0]
|
||||||
|
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// One should be able to patch the CRD to use per-version columns. The top-level columns
|
||||||
|
// should not be defaulted during creation, and apiserver should not return validation
|
||||||
|
// error about top-level and per-version columns being mutual exclusive.
|
||||||
|
patch := []byte(`{"spec":{"versions":[{"name":"v1beta1","served":true,"storage":true,"additionalPrinterColumns":[{"name":"Age","type":"date","JSONPath":".metadata.creationTimestamp"}]},{"name":"v1","served":true,"storage":false,"additionalPrinterColumns":[{"name":"Age2","type":"date","JSONPath":".metadata.creationTimestamp"}]}]}}`)
|
||||||
|
|
||||||
|
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crd.Name, types.MergePatchType, patch)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("columns crd patched: %#v", crd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPatchCleanTopLevelColumns tests the case that a CRD was created with top-level columns.
|
||||||
|
// One should be able to PATCH the CRD cleaning the top-level columns and setting per-version
|
||||||
|
// columns.
|
||||||
|
func TestPatchCleanTopLevelColumns(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tearDown()
|
||||||
|
|
||||||
|
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicClient, err := dynamic.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crd := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)[0]
|
||||||
|
crd.Spec.AdditionalPrinterColumns = []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||||
|
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
|
||||||
|
}
|
||||||
|
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("columns crd created: %#v", crd)
|
||||||
|
|
||||||
|
// One should be able to patch the CRD to use per-version columns by cleaning
|
||||||
|
// the top-level columns.
|
||||||
|
patch := []byte(`{"spec":{"additionalPrinterColumns":null,"versions":[{"name":"v1beta1","served":true,"storage":true,"additionalPrinterColumns":[{"name":"Age","type":"date","JSONPath":".metadata.creationTimestamp"}]},{"name":"v1","served":true,"storage":false,"additionalPrinterColumns":[{"name":"Age2","type":"date","JSONPath":".metadata.creationTimestamp"}]}]}}`)
|
||||||
|
|
||||||
|
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crd.Name, types.MergePatchType, patch)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("columns crd patched: %#v", crd)
|
||||||
|
}
|
||||||
|
|
||||||
func abs(x float64) float64 {
|
func abs(x float64) float64 {
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
|
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -25,8 +26,11 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,22 +88,8 @@ func TestForProperValidationErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNoxuValidationCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
func newNoxuValidationCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiextensionsv1beta1.CustomResourceDefinition {
|
||||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
validationSchema := &apiextensionsv1beta1.JSONSchemaProps{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
|
||||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
|
||||||
Group: "mygroup.example.com",
|
|
||||||
Version: "v1beta1",
|
|
||||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
|
||||||
Plural: "noxus",
|
|
||||||
Singular: "nonenglishnoxu",
|
|
||||||
Kind: "WishIHadChosenNoxu",
|
|
||||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
|
||||||
ListKind: "NoxuItemList",
|
|
||||||
},
|
|
||||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
|
||||||
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
|
||||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
|
||||||
Required: []string{"alpha", "beta"},
|
Required: []string{"alpha", "beta"},
|
||||||
AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{
|
AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{
|
||||||
Allows: true,
|
Allows: true,
|
||||||
@@ -144,6 +134,70 @@ func newNoxuValidationCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensio
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
validationSchemaWithDescription := validationSchema.DeepCopy()
|
||||||
|
validationSchemaWithDescription.Description = "test"
|
||||||
|
return []*apiextensionsv1beta1.CustomResourceDefinition{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||||
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
|
Group: "mygroup.example.com",
|
||||||
|
Version: "v1beta1",
|
||||||
|
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||||
|
Plural: "noxus",
|
||||||
|
Singular: "nonenglishnoxu",
|
||||||
|
Kind: "WishIHadChosenNoxu",
|
||||||
|
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||||
|
ListKind: "NoxuItemList",
|
||||||
|
},
|
||||||
|
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||||
|
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validationSchema,
|
||||||
|
},
|
||||||
|
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "v1beta1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||||
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
|
Group: "mygroup.example.com",
|
||||||
|
Version: "v1beta1",
|
||||||
|
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||||
|
Plural: "noxus",
|
||||||
|
Singular: "nonenglishnoxu",
|
||||||
|
Kind: "WishIHadChosenNoxu",
|
||||||
|
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||||
|
ListKind: "NoxuItemList",
|
||||||
|
},
|
||||||
|
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||||
|
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "v1beta1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
Schema: &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validationSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
Schema: &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: validationSchemaWithDescription,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -168,42 +222,58 @@ func newNoxuValidationInstance(namespace, name string) *unstructured.Unstructure
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomResourceValidation(t *testing.T) {
|
func TestCustomResourceValidation(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
instanceToCreate := newNoxuValidationInstance(ns, "foo")
|
||||||
|
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||||
|
_, err = instantiateVersionedCustomResource(t, instanceToCreate, noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomResourceUpdateValidation(t *testing.T) {
|
func TestCustomResourceUpdateValidation(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
instanceToCreate := newNoxuValidationInstance(ns, "foo")
|
||||||
|
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||||
|
_, err = instantiateVersionedCustomResource(t, instanceToCreate, noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create noxu instance: %v", err)
|
t.Fatalf("unable to create noxu instance: %v", err)
|
||||||
}
|
}
|
||||||
@@ -229,23 +299,30 @@ func TestCustomResourceUpdateValidation(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: alpha and beta should be present while updating %v", gottenNoxuInstance)
|
t.Fatalf("unexpected non-error: alpha and beta should be present while updating %v", gottenNoxuInstance)
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomResourceValidationErrors(t *testing.T) {
|
func TestCustomResourceValidationErrors(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for _, noxuDefinition := range noxuDefinitions {
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -309,7 +386,11 @@ func TestCustomResourceValidationErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
_, err := noxuResourceClient.Create(tc.instanceFn(), metav1.CreateOptions{})
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
instanceToCreate := tc.instanceFn()
|
||||||
|
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||||
|
_, err := noxuResourceClient.Create(instanceToCreate, metav1.CreateOptions{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("%v: expected %v", tc.name, tc.expectedError)
|
t.Errorf("%v: expected %v", tc.name, tc.expectedError)
|
||||||
continue
|
continue
|
||||||
@@ -321,35 +402,55 @@ func TestCustomResourceValidationErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCRValidationOnCRDUpdate(t *testing.T) {
|
func TestCRValidationOnCRDUpdate(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
for i, noxuDefinition := range noxuDefinitions {
|
||||||
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
|
// Re-define the CRD to make sure we start with a clean CRD
|
||||||
|
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[i]
|
||||||
|
validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// set stricter schema
|
// set stricter schema
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta", "epsilon"}
|
validationSchema.OpenAPIV3Schema.Required = []string{"alpha", "beta", "epsilon"}
|
||||||
|
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ns := "not-the-default"
|
ns := "not-the-default"
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||||
|
instanceToCreate := newNoxuValidationInstance(ns, "foo")
|
||||||
|
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||||
|
|
||||||
// CR is rejected
|
// CR is rejected
|
||||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
_, err = instantiateVersionedCustomResource(t, instanceToCreate, noxuResourceClient, noxuDefinition, v.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: CR should be rejected")
|
t.Fatalf("unexpected non-error: CR should be rejected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the CRD to a less stricter schema
|
// update the CRD to a less stricter schema
|
||||||
_, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
_, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||||
crd.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
|
validationSchema, err := getSchemaForVersion(crd, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
validationSchema.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -357,9 +458,9 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
|
|||||||
|
|
||||||
// CR is now accepted
|
// CR is now accepted
|
||||||
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||||
_, err := noxuResourceClient.Create(newNoxuValidationInstance(ns, "foo"), metav1.CreateOptions{})
|
_, err := noxuResourceClient.Create(instanceToCreate, metav1.CreateOptions{})
|
||||||
if statusError, isStatus := err.(*apierrors.StatusError); isStatus {
|
if _, isStatus := err.(*apierrors.StatusError); isStatus {
|
||||||
if strings.Contains(statusError.Error(), "is invalid") {
|
if apierrors.IsInvalid(err) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,36 +472,51 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForbiddenFieldsInSchema(t *testing.T) {
|
func TestForbiddenFieldsInSchema(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.AdditionalProperties.Allows = false
|
for i, noxuDefinition := range noxuDefinitions {
|
||||||
|
for _, v := range noxuDefinition.Spec.Versions {
|
||||||
|
// Re-define the CRD to make sure we start with a clean CRD
|
||||||
|
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[i]
|
||||||
|
validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
validationSchema.OpenAPIV3Schema.AdditionalProperties.Allows = false
|
||||||
|
|
||||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: additionalProperties cannot be set to false")
|
t.Fatalf("unexpected non-error: additionalProperties cannot be set to false")
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||||
Type: "array",
|
Type: "array",
|
||||||
UniqueItems: true,
|
UniqueItems: true,
|
||||||
}
|
}
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.AdditionalProperties.Allows = true
|
validationSchema.OpenAPIV3Schema.AdditionalProperties.Allows = true
|
||||||
|
|
||||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("unexpected non-error: uniqueItems cannot be set to true")
|
t.Fatalf("unexpected non-error: uniqueItems cannot be set to true")
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta")
|
validationSchema.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta")
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||||
Type: "array",
|
Type: "array",
|
||||||
UniqueItems: false,
|
UniqueItems: false,
|
||||||
}
|
}
|
||||||
@@ -410,12 +526,17 @@ func TestForbiddenFieldsInSchema(t *testing.T) {
|
|||||||
t.Fatal("unexpected non-error: $ref cannot be non-empty string")
|
t.Fatal("unexpected non-error: $ref cannot be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Ref = nil
|
validationSchema.OpenAPIV3Schema.Ref = nil
|
||||||
|
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func float64Ptr(f float64) *float64 {
|
func float64Ptr(f float64) *float64 {
|
||||||
|
@@ -27,10 +27,13 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
|
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -354,6 +357,7 @@ values:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestYAMLSubresource(t *testing.T) {
|
func TestYAMLSubresource(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -369,7 +373,7 @@ func TestYAMLSubresource(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.ClusterScoped)
|
noxuDefinition := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.ClusterScoped)[0]
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
Reference in New Issue
Block a user