From 132d2afca0794b4bcaedb6dbbefe4e9d66e80239 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Mon, 24 Feb 2020 21:20:45 -0800 Subject: [PATCH] Adding IngressClass to networking/v1beta1 Co-authored-by: Christopher M. Luciano --- api/openapi-spec/swagger.json | 892 +++++++++++++++++- .../v1beta1/zz_generated.conversion.go | 2 + pkg/apis/networking/register.go | 2 + pkg/apis/networking/types.go | 80 +- pkg/apis/networking/v1beta1/BUILD | 2 + .../v1beta1/zz_generated.conversion.go | 110 ++- pkg/apis/networking/validation/BUILD | 4 + pkg/apis/networking/validation/validation.go | 113 ++- .../networking/validation/validation_test.go | 310 ++++++ pkg/apis/networking/zz_generated.deepcopy.go | 86 ++ pkg/features/kube_features.go | 7 + .../default_storage_factory_builder.go | 1 + pkg/kubeapiserver/options/BUILD | 44 +- pkg/kubeapiserver/options/plugins.go | 4 + pkg/kubelet/pod_container_deletor.go | 8 +- pkg/master/storageversionhashdata/data.go | 1 + pkg/printers/internalversion/printers.go | 45 +- pkg/printers/internalversion/printers_test.go | 68 +- pkg/registry/BUILD | 1 + pkg/registry/networking/ingressclass/BUILD | 48 + pkg/registry/networking/ingressclass/doc.go | 17 + .../networking/ingressclass/storage/BUILD | 32 + .../ingressclass/storage/storage.go | 54 ++ .../networking/ingressclass/strategy.go | 96 ++ .../networking/ingressclass/strategy_test.go | 72 ++ pkg/registry/networking/rest/BUILD | 1 + .../networking/rest/storage_settings.go | 9 + plugin/BUILD | 1 + .../pkg/admission/defaultingressclass/BUILD | 53 ++ .../defaultingressclass/admission.go | 173 ++++ .../defaultingressclass/admission_test.go | 198 ++++ .../api/extensions/v1beta1/generated.pb.go | 498 +++++----- .../api/extensions/v1beta1/generated.proto | 13 + .../k8s.io/api/extensions/v1beta1/types.go | 13 + .../v1beta1/types_swagger_doc_generated.go | 9 +- .../v1beta1/zz_generated.deepcopy.go | 5 + .../src/k8s.io/api/networking/v1beta1/BUILD | 1 + .../api/networking/v1beta1/generated.pb.go | 830 ++++++++++++++-- .../api/networking/v1beta1/generated.proto | 69 +- .../k8s.io/api/networking/v1beta1/register.go | 2 + .../k8s.io/api/networking/v1beta1/types.go | 77 +- .../v1beta1/types_swagger_doc_generated.go | 41 +- .../v1beta1/well_known_annotations.go | 32 + .../v1beta1/zz_generated.deepcopy.go | 87 ++ .../HEAD/extensions.v1beta1.Ingress.json | 21 +- .../HEAD/extensions.v1beta1.Ingress.pb | Bin 288 -> 292 bytes .../HEAD/extensions.v1beta1.Ingress.yaml | 21 +- .../networking.k8s.io.v1beta1.Ingress.json | 21 +- .../HEAD/networking.k8s.io.v1beta1.Ingress.pb | Bin 295 -> 299 bytes .../networking.k8s.io.v1beta1.Ingress.yaml | 21 +- ...etworking.k8s.io.v1beta1.IngressClass.json | 51 + .../networking.k8s.io.v1beta1.IngressClass.pb | Bin 0 -> 246 bytes ...etworking.k8s.io.v1beta1.IngressClass.yaml | 37 + .../pkg/util/validation/validation.go | 38 + .../pkg/util/validation/validation_test.go | 47 + .../admission/initializer/initializer_test.go | 4 +- .../src/k8s.io/client-go/informers/generic.go | 2 + .../informers/networking/v1beta1/BUILD | 1 + .../networking/v1beta1/ingressclass.go | 89 ++ .../informers/networking/v1beta1/interface.go | 7 + .../kubernetes/typed/networking/v1beta1/BUILD | 1 + .../typed/networking/v1beta1/fake/BUILD | 1 + .../v1beta1/fake/fake_ingressclass.go | 122 +++ .../v1beta1/fake/fake_networking_client.go | 4 + .../networking/v1beta1/generated_expansion.go | 2 + .../typed/networking/v1beta1/ingressclass.go | 168 ++++ .../networking/v1beta1/networking_client.go | 5 + .../listers/networking/v1beta1/BUILD | 1 + .../networking/v1beta1/expansion_generated.go | 4 + .../networking/v1beta1/ingressclass.go | 65 ++ .../kubectl/pkg/describe/versioned/BUILD | 1 + .../pkg/describe/versioned/describe.go | 35 + .../pkg/describe/versioned/describe_test.go | 38 + test/e2e/network/BUILD | 2 + test/e2e/network/ingressclass.go | 123 +++ test/integration/etcd/data.go | 4 + 76 files changed, 4749 insertions(+), 398 deletions(-) create mode 100644 pkg/registry/networking/ingressclass/BUILD create mode 100644 pkg/registry/networking/ingressclass/doc.go create mode 100644 pkg/registry/networking/ingressclass/storage/BUILD create mode 100644 pkg/registry/networking/ingressclass/storage/storage.go create mode 100644 pkg/registry/networking/ingressclass/strategy.go create mode 100644 pkg/registry/networking/ingressclass/strategy_test.go create mode 100644 plugin/pkg/admission/defaultingressclass/BUILD create mode 100644 plugin/pkg/admission/defaultingressclass/admission.go create mode 100644 plugin/pkg/admission/defaultingressclass/admission_test.go create mode 100644 staging/src/k8s.io/api/networking/v1beta1/well_known_annotations.go create mode 100644 staging/src/k8s.io/api/testdata/HEAD/networking.k8s.io.v1beta1.IngressClass.json create mode 100644 staging/src/k8s.io/api/testdata/HEAD/networking.k8s.io.v1beta1.IngressClass.pb create mode 100644 staging/src/k8s.io/api/testdata/HEAD/networking.k8s.io.v1beta1.IngressClass.yaml create mode 100644 staging/src/k8s.io/client-go/informers/networking/v1beta1/ingressclass.go create mode 100644 staging/src/k8s.io/client-go/kubernetes/typed/networking/v1beta1/fake/fake_ingressclass.go create mode 100644 staging/src/k8s.io/client-go/kubernetes/typed/networking/v1beta1/ingressclass.go create mode 100644 staging/src/k8s.io/client-go/listers/networking/v1beta1/ingressclass.go create mode 100644 test/e2e/network/ingressclass.go diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 67df876db21..cc28f2d90fd 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -10927,6 +10927,10 @@ "$ref": "#/definitions/io.k8s.api.extensions.v1beta1.IngressBackend", "description": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default." }, + "ingressClassName": { + "description": "IngressClassName is the name of the IngressClass cluster resource. The associated IngressClass defines which controller will implement the resource. This replaces the deprecated `kubernetes.io/ingress.class` annotation. For backwards compatibility, when that annotation is set, it must be given precedence over this field. The controller may emit a warning if the field and annotation have different values. Implementations of this API should ignore Ingresses without a class specified. An IngressClass resource may be marked as default, which can be used to set a default value for this field. For more information, refer to the IngressClass documentation.", + "type": "string" + }, "rules": { "description": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", "items": { @@ -11793,6 +11797,85 @@ ], "type": "object" }, + "io.k8s.api.networking.v1beta1.IngressClass": { + "description": "IngressClass represents the class of the Ingress, referenced by the Ingress Spec. The `ingressclass.kubernetes.io/is-default-class` annotation can be used to indicate that an IngressClass should be considered default. When a single IngressClass resource has this annotation set to true, new Ingress resources without a class specified will be assigned this default class.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClassSpec", + "description": "Spec is the desired state of the IngressClass. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.networking.v1beta1.IngressClassList": { + "description": "IngressClassList is a collection of IngressClasses.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of IngressClasses.", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + }, + "type": "array", + "x-kubernetes-list-type": "set" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "networking.k8s.io", + "kind": "IngressClassList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.networking.v1beta1.IngressClassSpec": { + "description": "IngressClassSpec provides information about the class of an Ingress.", + "properties": { + "controller": { + "description": "Controller refers to the name of the controller that should handle this class. This allows for different \"flavors\" that are controlled by the same controller. For example, you may have different Parameters for the same implementing controller. This should be specified as a domain-prefixed path no more than 250 characters in length, e.g. \"acme.io/ingress-controller\". This field is immutable.", + "type": "string" + }, + "parameters": { + "$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference", + "description": "Parameters is a link to a resource containing additional configuration for the controller. This is optional if the controller does not require extra parameters. Example configuration resources include `core.ConfigMap` or a controller specific Custom Resource." + } + }, + "type": "object" + }, "io.k8s.api.networking.v1beta1.IngressList": { "description": "IngressList is a collection of Ingress.", "properties": { @@ -11848,6 +11931,10 @@ "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressBackend", "description": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default." }, + "ingressClassName": { + "description": "IngressClassName is the name of the IngressClass cluster resource. The associated IngressClass defines which controller will implement the resource. This replaces the deprecated `kubernetes.io/ingress.class` annotation. For backwards compatibility, when that annotation is set, it must be given precedence over this field. The controller may emit a warning if the field and annotation have different values. Implementations of this API should ignore Ingresses without a class specified. An IngressClass resource may be marked as default, which can be used to set a default value for this field. For more information, refer to the IngressClass documentation.", + "type": "string" + }, "rules": { "description": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", "items": { @@ -11886,7 +11973,7 @@ "type": "array" }, "secretName": { - "description": "SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.", + "description": "SecretName is the name of the secret used to terminate TLS traffic on port 443. Field is left optional to allow TLS routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.", "type": "string" } }, @@ -70870,6 +70957,593 @@ ] } }, + "/apis/networking.k8s.io/v1beta1/ingressclasses": { + "delete": { + "consumes": [ + "*/*" + ], + "description": "delete collection of IngressClass", + "operationId": "deleteNetworkingV1beta1CollectionIngressClass", + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "type": "boolean", + "uniqueItems": true + }, + { + "in": "body", + "name": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "type": "string", + "uniqueItems": true + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "type": "integer", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "type": "integer", + "uniqueItems": true + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "type": "boolean", + "uniqueItems": true + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "type": "string", + "uniqueItems": true + }, + { + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "in": "query", + "name": "resourceVersion", + "type": "string", + "uniqueItems": true + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "type": "integer", + "uniqueItems": true + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "type": "boolean", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "get": { + "consumes": [ + "*/*" + ], + "description": "list or watch objects of kind IngressClass", + "operationId": "listNetworkingV1beta1IngressClass", + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "type": "boolean", + "uniqueItems": true + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "type": "integer", + "uniqueItems": true + }, + { + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "in": "query", + "name": "resourceVersion", + "type": "string", + "uniqueItems": true + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "type": "integer", + "uniqueItems": true + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "type": "boolean", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClassList" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "If 'true', then the output is pretty printed.", + "in": "query", + "name": "pretty", + "type": "string", + "uniqueItems": true + } + ], + "post": { + "consumes": [ + "*/*" + ], + "description": "create an IngressClass", + "operationId": "createNetworkingV1beta1IngressClass", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "type": "string", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + } + }, + "/apis/networking.k8s.io/v1beta1/ingressclasses/{name}": { + "delete": { + "consumes": [ + "*/*" + ], + "description": "delete an IngressClass", + "operationId": "deleteNetworkingV1beta1IngressClass", + "parameters": [ + { + "in": "body", + "name": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "type": "integer", + "uniqueItems": true + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "type": "boolean", + "uniqueItems": true + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "type": "string", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "get": { + "consumes": [ + "*/*" + ], + "description": "read the specified IngressClass", + "operationId": "readNetworkingV1beta1IngressClass", + "parameters": [ + { + "description": "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'. Deprecated. Planned for removal in 1.18.", + "in": "query", + "name": "exact", + "type": "boolean", + "uniqueItems": true + }, + { + "description": "Should this value be exported. Export strips fields that a user can not specify. Deprecated. Planned for removal in 1.18.", + "in": "query", + "name": "export", + "type": "boolean", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "name of the IngressClass", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "description": "If 'true', then the output is pretty printed.", + "in": "query", + "name": "pretty", + "type": "string", + "uniqueItems": true + } + ], + "patch": { + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "description": "partially update the specified IngressClass", + "operationId": "patchNetworkingV1beta1IngressClass", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "type": "string", + "uniqueItems": true + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "type": "boolean", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "put": { + "consumes": [ + "*/*" + ], + "description": "replace the specified IngressClass", + "operationId": "replaceNetworkingV1beta1IngressClass", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "type": "string", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.networking.v1beta1.IngressClass" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + } + }, "/apis/networking.k8s.io/v1beta1/ingresses": { "get": { "consumes": [ @@ -71773,6 +72447,222 @@ } } }, + "/apis/networking.k8s.io/v1beta1/watch/ingressclasses": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch individual changes to a list of IngressClass. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchNetworkingV1beta1IngressClassList", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "type": "boolean", + "uniqueItems": true + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "type": "integer", + "uniqueItems": true + }, + { + "description": "If 'true', then the output is pretty printed.", + "in": "query", + "name": "pretty", + "type": "string", + "uniqueItems": true + }, + { + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "in": "query", + "name": "resourceVersion", + "type": "string", + "uniqueItems": true + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "type": "integer", + "uniqueItems": true + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "type": "boolean", + "uniqueItems": true + } + ] + }, + "/apis/networking.k8s.io/v1beta1/watch/ingressclasses/{name}": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch changes to an object of kind IngressClass. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchNetworkingV1beta1IngressClass", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "networking_v1beta1" + ], + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "networking.k8s.io", + "kind": "IngressClass", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "type": "boolean", + "uniqueItems": true + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "type": "string", + "uniqueItems": true + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "type": "integer", + "uniqueItems": true + }, + { + "description": "name of the IngressClass", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "description": "If 'true', then the output is pretty printed.", + "in": "query", + "name": "pretty", + "type": "string", + "uniqueItems": true + }, + { + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "in": "query", + "name": "resourceVersion", + "type": "string", + "uniqueItems": true + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "type": "integer", + "uniqueItems": true + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "type": "boolean", + "uniqueItems": true + } + ] + }, "/apis/networking.k8s.io/v1beta1/watch/ingresses": { "get": { "consumes": [ diff --git a/pkg/apis/extensions/v1beta1/zz_generated.conversion.go b/pkg/apis/extensions/v1beta1/zz_generated.conversion.go index 386f345d166..b5482619cf9 100644 --- a/pkg/apis/extensions/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/extensions/v1beta1/zz_generated.conversion.go @@ -1404,6 +1404,7 @@ func Convert_networking_IngressRuleValue_To_v1beta1_IngressRuleValue(in *network } func autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSpec, out *networking.IngressSpec, s conversion.Scope) error { + out.IngressClassName = (*string)(unsafe.Pointer(in.IngressClassName)) out.Backend = (*networking.IngressBackend)(unsafe.Pointer(in.Backend)) out.TLS = *(*[]networking.IngressTLS)(unsafe.Pointer(&in.TLS)) out.Rules = *(*[]networking.IngressRule)(unsafe.Pointer(&in.Rules)) @@ -1416,6 +1417,7 @@ func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSp } func autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.IngressSpec, out *v1beta1.IngressSpec, s conversion.Scope) error { + out.IngressClassName = (*string)(unsafe.Pointer(in.IngressClassName)) out.Backend = (*v1beta1.IngressBackend)(unsafe.Pointer(in.Backend)) out.TLS = *(*[]v1beta1.IngressTLS)(unsafe.Pointer(&in.TLS)) out.Rules = *(*[]v1beta1.IngressRule)(unsafe.Pointer(&in.Rules)) diff --git a/pkg/apis/networking/register.go b/pkg/apis/networking/register.go index 30996dfa87c..81dad49b1de 100644 --- a/pkg/apis/networking/register.go +++ b/pkg/apis/networking/register.go @@ -50,6 +50,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &NetworkPolicyList{}, &Ingress{}, &IngressList{}, + &IngressClass{}, + &IngressClassList{}, ) return nil } diff --git a/pkg/apis/networking/types.go b/pkg/apis/networking/types.go index 1b2d04f149d..c10b7570549 100644 --- a/pkg/apis/networking/types.go +++ b/pkg/apis/networking/types.go @@ -235,10 +235,23 @@ type IngressList struct { // IngressSpec describes the Ingress the user wishes to exist. type IngressSpec struct { - // A default backend capable of servicing requests that don't match any - // rule. At least one of 'backend' or 'rules' must be specified. This field - // is optional to allow the loadbalancer controller or defaulting logic to - // specify a global default. + // IngressClassName is the name of the IngressClass cluster resource. The + // associated IngressClass defines which controller will implement the + // resource. This replaces the deprecated `kubernetes.io/ingress.class` + // annotation. For backwards compatibility, when that annotation is set, it + // must be given precedence over this field. The controller may emit a + // warning if the field and annotation have different values. + // Implementations of this API should ignore Ingresses without a class + // specified. An IngressClass resource may be marked as default, which can + // be used to set a default value for this field. For more information, + // refer to the IngressClass documentation. + // +optional + IngressClassName *string + + // Backend is a default backend capable of servicing requests that don't + // match any rule. At least one of 'backend' or 'rules' must be specified. + // This field is optional to allow the loadbalancer controller or defaulting + // logic to specify a global default. // +optional Backend *IngressBackend @@ -257,6 +270,55 @@ type IngressSpec struct { // TODO: Add the ability to specify load-balancer IP through claims } +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IngressClass represents the class of the Ingress, referenced by the Ingress +// Spec. The `ingressclass.kubernetes.io/is-default-class` annotation can be +// used to indicate that an IngressClass should be considered default. When a +// single IngressClass resource has this annotation set to true, new Ingress +// resources without a class specified will be assigned this default class. +type IngressClass struct { + metav1.TypeMeta + metav1.ObjectMeta + + // Spec is the desired state of the IngressClass. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + // +optional + Spec IngressClassSpec +} + +// IngressClassSpec provides information about the class of an Ingress. +type IngressClassSpec struct { + // Controller refers to the name of the controller that should handle this + // class. This allows for different "flavors" that are controlled by the + // same controller. For example, you may have different Parameters for the + // same implementing controller. This should be specified as a + // domain-prefixed path no more than 250 characters in length, e.g. + // "acme.io/ingress-controller". This field is immutable. + Controller string + + // Parameters is a link to a resource containing additional configuration + // for the controller. This is optional if the controller does not require + // extra parameters. Example configuration resources include + // `core.ConfigMap` or a controller specific Custom Resource. + // +optional + Parameters *api.TypedLocalObjectReference +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IngressClassList is a collection of IngressClasses. +type IngressClassList struct { + metav1.TypeMeta + // Standard object's metadata. + // +optional + metav1.ListMeta + + // Items is the list of IngressClasses. + // +listType=set + Items []IngressClass +} + // IngressTLS describes the transport layer security associated with an Ingress. type IngressTLS struct { // Hosts are a list of hosts included in the TLS certificate. The values in @@ -265,11 +327,11 @@ type IngressTLS struct { // Ingress, if left unspecified. // +optional Hosts []string - // SecretName is the name of the secret used to terminate SSL traffic on 443. - // Field is left optional to allow SSL routing based on SNI hostname alone. - // If the SNI host in a listener conflicts with the "Host" header field used - // by an IngressRule, the SNI host is used for termination and value of the - // Host header is used for routing. + // SecretName is the name of the secret used to terminate TLS traffic on + // port 443. Field is left optional to allow TLS routing based on SNI + // hostname alone. If the SNI host in a listener conflicts with the "Host" + // header field used by an IngressRule, the SNI host is used for termination + // and value of the Host header is used for routing. // +optional SecretName string // TODO: Consider specifying different modes of termination, protocols etc. diff --git a/pkg/apis/networking/v1beta1/BUILD b/pkg/apis/networking/v1beta1/BUILD index 413a8b77cae..e561152a209 100644 --- a/pkg/apis/networking/v1beta1/BUILD +++ b/pkg/apis/networking/v1beta1/BUILD @@ -13,8 +13,10 @@ go_library( ], importpath = "k8s.io/kubernetes/pkg/apis/networking/v1beta1", deps = [ + "//pkg/apis/core:go_default_library", "//pkg/apis/core/v1:go_default_library", "//pkg/apis/networking:go_default_library", + "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/networking/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", diff --git a/pkg/apis/networking/v1beta1/zz_generated.conversion.go b/pkg/apis/networking/v1beta1/zz_generated.conversion.go index ea1e7a40673..124e38788bb 100644 --- a/pkg/apis/networking/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/networking/v1beta1/zz_generated.conversion.go @@ -23,10 +23,12 @@ package v1beta1 import ( unsafe "unsafe" + v1 "k8s.io/api/core/v1" v1beta1 "k8s.io/api/networking/v1beta1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" - v1 "k8s.io/kubernetes/pkg/apis/core/v1" + core "k8s.io/kubernetes/pkg/apis/core" + corev1 "k8s.io/kubernetes/pkg/apis/core/v1" networking "k8s.io/kubernetes/pkg/apis/networking" ) @@ -77,6 +79,36 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClass)(nil), (*networking.IngressClass)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_IngressClass_To_networking_IngressClass(a.(*v1beta1.IngressClass), b.(*networking.IngressClass), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*networking.IngressClass)(nil), (*v1beta1.IngressClass)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_networking_IngressClass_To_v1beta1_IngressClass(a.(*networking.IngressClass), b.(*v1beta1.IngressClass), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClassList)(nil), (*networking.IngressClassList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_IngressClassList_To_networking_IngressClassList(a.(*v1beta1.IngressClassList), b.(*networking.IngressClassList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*networking.IngressClassList)(nil), (*v1beta1.IngressClassList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_networking_IngressClassList_To_v1beta1_IngressClassList(a.(*networking.IngressClassList), b.(*v1beta1.IngressClassList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClassSpec)(nil), (*networking.IngressClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(a.(*v1beta1.IngressClassSpec), b.(*networking.IngressClassSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*networking.IngressClassSpec)(nil), (*v1beta1.IngressClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(a.(*networking.IngressClassSpec), b.(*v1beta1.IngressClassSpec), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta1.IngressList)(nil), (*networking.IngressList)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_IngressList_To_networking_IngressList(a.(*v1beta1.IngressList), b.(*networking.IngressList), scope) }); err != nil { @@ -240,6 +272,76 @@ func Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networking. return autoConvert_networking_IngressBackend_To_v1beta1_IngressBackend(in, out, s) } +func autoConvert_v1beta1_IngressClass_To_networking_IngressClass(in *v1beta1.IngressClass, out *networking.IngressClass, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_IngressClass_To_networking_IngressClass is an autogenerated conversion function. +func Convert_v1beta1_IngressClass_To_networking_IngressClass(in *v1beta1.IngressClass, out *networking.IngressClass, s conversion.Scope) error { + return autoConvert_v1beta1_IngressClass_To_networking_IngressClass(in, out, s) +} + +func autoConvert_networking_IngressClass_To_v1beta1_IngressClass(in *networking.IngressClass, out *v1beta1.IngressClass, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_networking_IngressClass_To_v1beta1_IngressClass is an autogenerated conversion function. +func Convert_networking_IngressClass_To_v1beta1_IngressClass(in *networking.IngressClass, out *v1beta1.IngressClass, s conversion.Scope) error { + return autoConvert_networking_IngressClass_To_v1beta1_IngressClass(in, out, s) +} + +func autoConvert_v1beta1_IngressClassList_To_networking_IngressClassList(in *v1beta1.IngressClassList, out *networking.IngressClassList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]networking.IngressClass)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta1_IngressClassList_To_networking_IngressClassList is an autogenerated conversion function. +func Convert_v1beta1_IngressClassList_To_networking_IngressClassList(in *v1beta1.IngressClassList, out *networking.IngressClassList, s conversion.Scope) error { + return autoConvert_v1beta1_IngressClassList_To_networking_IngressClassList(in, out, s) +} + +func autoConvert_networking_IngressClassList_To_v1beta1_IngressClassList(in *networking.IngressClassList, out *v1beta1.IngressClassList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]v1beta1.IngressClass)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_networking_IngressClassList_To_v1beta1_IngressClassList is an autogenerated conversion function. +func Convert_networking_IngressClassList_To_v1beta1_IngressClassList(in *networking.IngressClassList, out *v1beta1.IngressClassList, s conversion.Scope) error { + return autoConvert_networking_IngressClassList_To_v1beta1_IngressClassList(in, out, s) +} + +func autoConvert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in *v1beta1.IngressClassSpec, out *networking.IngressClassSpec, s conversion.Scope) error { + out.Controller = in.Controller + out.Parameters = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters)) + return nil +} + +// Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec is an autogenerated conversion function. +func Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in *v1beta1.IngressClassSpec, out *networking.IngressClassSpec, s conversion.Scope) error { + return autoConvert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in, out, s) +} + +func autoConvert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(in *networking.IngressClassSpec, out *v1beta1.IngressClassSpec, s conversion.Scope) error { + out.Controller = in.Controller + out.Parameters = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters)) + return nil +} + +// Convert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec is an autogenerated conversion function. +func Convert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(in *networking.IngressClassSpec, out *v1beta1.IngressClassSpec, s conversion.Scope) error { + return autoConvert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(in, out, s) +} + func autoConvert_v1beta1_IngressList_To_networking_IngressList(in *v1beta1.IngressList, out *networking.IngressList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { @@ -329,6 +431,7 @@ func Convert_networking_IngressRuleValue_To_v1beta1_IngressRuleValue(in *network } func autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSpec, out *networking.IngressSpec, s conversion.Scope) error { + out.IngressClassName = (*string)(unsafe.Pointer(in.IngressClassName)) out.Backend = (*networking.IngressBackend)(unsafe.Pointer(in.Backend)) out.TLS = *(*[]networking.IngressTLS)(unsafe.Pointer(&in.TLS)) out.Rules = *(*[]networking.IngressRule)(unsafe.Pointer(&in.Rules)) @@ -341,6 +444,7 @@ func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSp } func autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.IngressSpec, out *v1beta1.IngressSpec, s conversion.Scope) error { + out.IngressClassName = (*string)(unsafe.Pointer(in.IngressClassName)) out.Backend = (*v1beta1.IngressBackend)(unsafe.Pointer(in.Backend)) out.TLS = *(*[]v1beta1.IngressTLS)(unsafe.Pointer(&in.TLS)) out.Rules = *(*[]v1beta1.IngressRule)(unsafe.Pointer(&in.Rules)) @@ -353,7 +457,7 @@ func Convert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.Ingres } func autoConvert_v1beta1_IngressStatus_To_networking_IngressStatus(in *v1beta1.IngressStatus, out *networking.IngressStatus, s conversion.Scope) error { - if err := v1.Convert_v1_LoadBalancerStatus_To_core_LoadBalancerStatus(&in.LoadBalancer, &out.LoadBalancer, s); err != nil { + if err := corev1.Convert_v1_LoadBalancerStatus_To_core_LoadBalancerStatus(&in.LoadBalancer, &out.LoadBalancer, s); err != nil { return err } return nil @@ -365,7 +469,7 @@ func Convert_v1beta1_IngressStatus_To_networking_IngressStatus(in *v1beta1.Ingre } func autoConvert_networking_IngressStatus_To_v1beta1_IngressStatus(in *networking.IngressStatus, out *v1beta1.IngressStatus, s conversion.Scope) error { - if err := v1.Convert_core_LoadBalancerStatus_To_v1_LoadBalancerStatus(&in.LoadBalancer, &out.LoadBalancer, s); err != nil { + if err := corev1.Convert_core_LoadBalancerStatus_To_v1_LoadBalancerStatus(&in.LoadBalancer, &out.LoadBalancer, s); err != nil { return err } return nil diff --git a/pkg/apis/networking/validation/BUILD b/pkg/apis/networking/validation/BUILD index 666006f9303..300489bad8a 100644 --- a/pkg/apis/networking/validation/BUILD +++ b/pkg/apis/networking/validation/BUILD @@ -17,11 +17,14 @@ go_test( "//pkg/apis/core:go_default_library", "//pkg/apis/networking:go_default_library", "//pkg/features:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", ], ) @@ -38,6 +41,7 @@ go_library( "//pkg/apis/networking:go_default_library", "//pkg/features:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/validation/path:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go index 50a202cde56..21f6bfe0bbd 100644 --- a/pkg/apis/networking/validation/validation.go +++ b/pkg/apis/networking/validation/validation.go @@ -22,6 +22,7 @@ import ( "strings" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" + pathvalidation "k8s.io/apimachinery/pkg/api/validation/path" unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" @@ -32,6 +33,11 @@ import ( "k8s.io/kubernetes/pkg/apis/networking" ) +const ( + annotationIngressClass = "kubernetes.io/ingress.class" + maxLenIngressClassController = 250 +) + // ValidateNetworkPolicyName can be used to check whether the given networkpolicy // name is valid. func ValidateNetworkPolicyName(name string, prefix bool) []string { @@ -174,15 +180,35 @@ func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorLi return allErrs } -// ValidateIngress tests if required fields in the Ingress are set. +// ValidateIngressName validates that the given name can be used as an Ingress +// name. +var ValidateIngressName = apimachineryvalidation.NameIsDNSSubdomain + +// ValidateIngress validates Ingresses on create and update. func ValidateIngress(ingress *networking.Ingress) field.ErrorList { allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata")) allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...) return allErrs } -// ValidateIngressName validates that the given name can be used as an Ingress name. -var ValidateIngressName = apimachineryvalidation.NameIsDNSSubdomain +// ValidateIngressCreate validates Ingresses on create. +func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, ValidateIngress(ingress)...) + annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass] + if annotationIsSet && ingress.Spec.IngressClassName != nil { + annotationPath := field.NewPath("annotations").Child(annotationIngressClass) + allErrs = append(allErrs, field.Invalid(annotationPath, annotationVal, "can not be set when the class field is also set")) + } + return allErrs +} + +// ValidateIngressUpdate validates ingresses on update. +func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList { + allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) + allErrs = append(allErrs, ValidateIngress(ingress)...) + return allErrs +} func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} @@ -220,13 +246,11 @@ func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path) fiel if len(spec.TLS) > 0 { allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"))...) } - return allErrs -} - -// ValidateIngressUpdate tests if required fields in the Ingress are set. -func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...) + if spec.IngressClassName != nil { + for _, msg := range ValidateIngressClassName(*spec.IngressClassName, false) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressClassName"), *spec.IngressClassName, msg)) + } + } return allErrs } @@ -315,3 +339,72 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P allErrs = append(allErrs, apivalidation.ValidatePortNumOrName(backend.ServicePort, fldPath.Child("servicePort"))...) return allErrs } + +// ValidateIngressClassName validates that the given name can be used as an +// IngressClass name. +var ValidateIngressClassName = apimachineryvalidation.NameIsDNSSubdomain + +// ValidateIngressClass ensures that IngressClass resources are valid. +func ValidateIngressClass(ingressClass *networking.IngressClass) field.ErrorList { + allErrs := apivalidation.ValidateObjectMeta(&ingressClass.ObjectMeta, false, ValidateIngressClassName, field.NewPath("metadata")) + allErrs = append(allErrs, validateIngressClassSpec(&ingressClass.Spec, field.NewPath("spec"))...) + return allErrs +} + +// ValidateIngressClassUpdate ensures that IngressClass updates are valid. +func ValidateIngressClassUpdate(newIngressClass, oldIngressClass *networking.IngressClass) field.ErrorList { + allErrs := apivalidation.ValidateObjectMetaUpdate(&newIngressClass.ObjectMeta, &oldIngressClass.ObjectMeta, field.NewPath("metadata")) + allErrs = append(allErrs, validateIngressClassSpecUpdate(&newIngressClass.Spec, &oldIngressClass.Spec, field.NewPath("spec"))...) + allErrs = append(allErrs, ValidateIngressClass(newIngressClass)...) + return allErrs +} + +// validateIngressClassSpec ensures that IngressClassSpec fields are valid. +func validateIngressClassSpec(spec *networking.IngressClassSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if len(spec.Controller) > maxLenIngressClassController { + allErrs = append(allErrs, field.TooLong(fldPath.Child("controller"), spec.Controller, maxLenIngressClassController)) + } + allErrs = append(allErrs, validation.IsDomainPrefixedPath(fldPath.Child("controller"), spec.Controller)...) + allErrs = append(allErrs, validateIngressClassParameters(spec.Parameters, fldPath.Child("parameters"))...) + return allErrs +} + +// validateIngressClassSpecUpdate ensures that IngressClassSpec updates are +// valid. +func validateIngressClassSpecUpdate(newSpec, oldSpec *networking.IngressClassSpec, fldPath *field.Path) field.ErrorList { + return apivalidation.ValidateImmutableField(newSpec.Controller, oldSpec.Controller, fldPath.Child("controller")) +} + +// validateIngressClassParameters ensures that Parameters fields are valid. +func validateIngressClassParameters(params *api.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if params == nil { + return allErrs + } + + if params.APIGroup != nil { + for _, msg := range validation.IsDNS1123Subdomain(*params.APIGroup) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), *params.APIGroup, msg)) + } + } + + if params.Kind == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("kind"), "kind is required")) + } else { + for _, msg := range pathvalidation.IsValidPathSegmentName(params.Kind) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), params.Kind, msg)) + } + } + + if params.Name == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name is required")) + } else { + for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg)) + } + } + + return allErrs +} diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go index 888e78f8a56..e7a9d861339 100644 --- a/pkg/apis/networking/validation/validation_test.go +++ b/pkg/apis/networking/validation/validation_test.go @@ -18,6 +18,9 @@ package validation import ( "fmt" + apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" + "k8s.io/apimachinery/pkg/util/validation/field" + utilpointer "k8s.io/utils/pointer" "strings" "testing" @@ -993,6 +996,313 @@ func TestValidateIngress(t *testing.T) { } } +func TestValidateIngressCreate(t *testing.T) { + baseIngress := networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test123", + Namespace: "test123", + ResourceVersion: "1234", + }, + Spec: networking.IngressSpec{ + Backend: &networking.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + }, + }, + } + + testCases := map[string]struct { + tweakIngress func(ingress *networking.Ingress) + expectedErrs field.ErrorList + }{ + "class field set": { + tweakIngress: func(ingress *networking.Ingress) { + ingress.Spec.IngressClassName = utilpointer.StringPtr("bar") + }, + expectedErrs: field.ErrorList{}, + }, + "class annotation set": { + tweakIngress: func(ingress *networking.Ingress) { + ingress.Annotations = map[string]string{annotationIngressClass: "foo"} + }, + expectedErrs: field.ErrorList{}, + }, + "class field and annotation set": { + tweakIngress: func(ingress *networking.Ingress) { + ingress.Spec.IngressClassName = utilpointer.StringPtr("bar") + ingress.Annotations = map[string]string{annotationIngressClass: "foo"} + }, + expectedErrs: field.ErrorList{field.Invalid(field.NewPath("annotations").Child(annotationIngressClass), "foo", "can not be set when the class field is also set")}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + newIngress := baseIngress.DeepCopy() + testCase.tweakIngress(newIngress) + + errs := ValidateIngressCreate(newIngress) + if len(errs) != len(testCase.expectedErrs) { + t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) + } + + for i, err := range errs { + if err.Error() != testCase.expectedErrs[i].Error() { + t.Fatalf("Expected error: %v, got %v", testCase.expectedErrs[i].Error(), err.Error()) + } + } + }) + } +} + +func TestValidateIngressUpdate(t *testing.T) { + baseIngress := networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test123", + Namespace: "test123", + ResourceVersion: "1234", + }, + Spec: networking.IngressSpec{ + Backend: &networking.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + }, + }, + } + + testCases := map[string]struct { + tweakIngresses func(newIngress, oldIngress *networking.Ingress) + expectedErrs field.ErrorList + }{ + "class field set": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + newIngress.Spec.IngressClassName = utilpointer.StringPtr("bar") + }, + expectedErrs: field.ErrorList{}, + }, + "class annotation set": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + newIngress.Annotations = map[string]string{annotationIngressClass: "foo"} + }, + expectedErrs: field.ErrorList{}, + }, + "class field and annotation set": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + newIngress.Spec.IngressClassName = utilpointer.StringPtr("bar") + newIngress.Annotations = map[string]string{annotationIngressClass: "foo"} + }, + expectedErrs: field.ErrorList{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + newIngress := baseIngress.DeepCopy() + oldIngress := baseIngress.DeepCopy() + testCase.tweakIngresses(newIngress, oldIngress) + + errs := ValidateIngressUpdate(newIngress, oldIngress) + + if len(errs) != len(testCase.expectedErrs) { + t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) + } + + for i, err := range errs { + if err.Error() != testCase.expectedErrs[i].Error() { + t.Fatalf("Expected error: %v, got %v", testCase.expectedErrs[i].Error(), err.Error()) + } + } + }) + } +} + +func TestValidateIngressClass(t *testing.T) { + testCases := map[string]struct { + ingressClass networking.IngressClass + expectedErrs field.ErrorList + }{ + "valid name, valid controller": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + }, + }, + expectedErrs: field.ErrorList{}, + }, + "invalid name, valid controller": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test*123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + }, + }, + expectedErrs: field.ErrorList{field.Invalid(field.NewPath("metadata.name"), "test*123", "a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")}, + }, + "valid name, empty controller": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "", + }, + }, + expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.controller"), "")}, + }, + "valid name, controller max length": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/" + strings.Repeat("a", 243), + }, + }, + expectedErrs: field.ErrorList{}, + }, + "valid name, controller too long": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/" + strings.Repeat("a", 244), + }, + }, + expectedErrs: field.ErrorList{field.TooLong(field.NewPath("spec.controller"), "", 250)}, + }, + "valid name, valid controller, valid params": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + Parameters: &api.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("example.com"), + Kind: "foo", + Name: "bar", + }, + }, + }, + expectedErrs: field.ErrorList{}, + }, + "valid name, valid controller, invalid params (no kind)": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + Parameters: &api.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("example.com"), + Name: "bar", + }, + }, + }, + expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.kind"), "kind is required")}, + }, + "valid name, valid controller, invalid params (no name)": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + Parameters: &api.TypedLocalObjectReference{ + Kind: "foo", + }, + }, + }, + expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.name"), "name is required")}, + }, + "valid name, valid controller, invalid params (bad kind)": { + ingressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + Parameters: &api.TypedLocalObjectReference{ + Kind: "foo/", + Name: "bar", + }, + }, + }, + expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec.parameters.kind"), "foo/", "may not contain '/'")}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + errs := ValidateIngressClass(&testCase.ingressClass) + + if len(errs) != len(testCase.expectedErrs) { + t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) + } + + for i, err := range errs { + if err.Error() != testCase.expectedErrs[i].Error() { + t.Fatalf("Expected error: %v, got %v", testCase.expectedErrs[i].Error(), err.Error()) + } + } + }) + } +} + +func TestValidateIngressClassUpdate(t *testing.T) { + testCases := map[string]struct { + newIngressClass networking.IngressClass + oldIngressClass networking.IngressClass + expectedErrs field.ErrorList + }{ + "name change": { + newIngressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test123", + ResourceVersion: "2", + }, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + }, + }, + oldIngressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/different", + }, + }, + expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("controller"), "foo.co/bar", apimachineryvalidation.FieldImmutableErrorMsg)}, + }, + "parameters change": { + newIngressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test123", + ResourceVersion: "2", + }, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + Parameters: &api.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("v1"), + Kind: "ConfigMap", + Name: "foo", + }, + }, + }, + oldIngressClass: networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + Spec: networking.IngressClassSpec{ + Controller: "foo.co/bar", + }, + }, + expectedErrs: field.ErrorList{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + errs := ValidateIngressClassUpdate(&testCase.newIngressClass, &testCase.oldIngressClass) + + if len(errs) != len(testCase.expectedErrs) { + t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) + } + + for i, err := range errs { + if err.Error() != testCase.expectedErrs[i].Error() { + t.Fatalf("Expected error: %v, got %v", testCase.expectedErrs[i].Error(), err.Error()) + } + } + }) + } +} + func TestValidateIngressTLS(t *testing.T) { defaultBackend := networking.IngressBackend{ ServiceName: "default-backend", diff --git a/pkg/apis/networking/zz_generated.deepcopy.go b/pkg/apis/networking/zz_generated.deepcopy.go index 543eedd8de6..46983ce53e8 100644 --- a/pkg/apis/networking/zz_generated.deepcopy.go +++ b/pkg/apis/networking/zz_generated.deepcopy.go @@ -131,6 +131,87 @@ func (in *IngressBackend) DeepCopy() *IngressBackend { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressClass) DeepCopyInto(out *IngressClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClass. +func (in *IngressClass) DeepCopy() *IngressClass { + if in == nil { + return nil + } + out := new(IngressClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IngressClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressClassList) DeepCopyInto(out *IngressClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IngressClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClassList. +func (in *IngressClassList) DeepCopy() *IngressClassList { + if in == nil { + return nil + } + out := new(IngressClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IngressClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressClassSpec) DeepCopyInto(out *IngressClassSpec) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = new(core.TypedLocalObjectReference) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClassSpec. +func (in *IngressClassSpec) DeepCopy() *IngressClassSpec { + if in == nil { + return nil + } + out := new(IngressClassSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressList) DeepCopyInto(out *IngressList) { *out = *in @@ -205,6 +286,11 @@ func (in *IngressRuleValue) DeepCopy() *IngressRuleValue { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressSpec) DeepCopyInto(out *IngressSpec) { *out = *in + if in.IngressClassName != nil { + in, out := &in.IngressClassName, &out.IngressClassName + *out = new(string) + **out = **in + } if in.Backend != nil { in, out := &in.Backend, &out.Backend *out = new(IngressBackend) diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 4c61068e024..eb9521c027b 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -548,6 +548,12 @@ const ( // Enables a feature to make secrets and configmaps data immutable. ImmutableEphemeralVolumes featuregate.Feature = "ImmutableEphemeralVolumes" + // owner: @robscott + // beta: v1.18 + // + // Enables DefaultIngressClass admission controller. + DefaultIngressClass featuregate.Feature = "DefaultIngressClass" + // owner: @bart0sh // alpha: v1.18 // @@ -641,6 +647,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS ServiceTopology: {Default: false, PreRelease: featuregate.Alpha}, ServiceAppProtocol: {Default: false, PreRelease: featuregate.Alpha}, ImmutableEphemeralVolumes: {Default: false, PreRelease: featuregate.Alpha}, + DefaultIngressClass: {Default: true, PreRelease: featuregate.Beta}, HugePageStorageMediumSize: {Default: false, PreRelease: featuregate.Alpha}, // inherited features from generic apiserver, relisted here to get a conflict if it is changed diff --git a/pkg/kubeapiserver/default_storage_factory_builder.go b/pkg/kubeapiserver/default_storage_factory_builder.go index 9b401721b7a..35e8ef16207 100644 --- a/pkg/kubeapiserver/default_storage_factory_builder.go +++ b/pkg/kubeapiserver/default_storage_factory_builder.go @@ -55,6 +55,7 @@ func NewStorageFactoryConfig() *StorageFactoryConfig { resources := []schema.GroupVersionResource{ batch.Resource("cronjobs").WithVersion("v1beta1"), networking.Resource("ingresses").WithVersion("v1beta1"), + networking.Resource("ingressclasses").WithVersion("v1beta1"), apisstorage.Resource("csidrivers").WithVersion("v1beta1"), } diff --git a/pkg/kubeapiserver/options/BUILD b/pkg/kubeapiserver/options/BUILD index bd0c53bf575..7d8a7d2ac97 100644 --- a/pkg/kubeapiserver/options/BUILD +++ b/pkg/kubeapiserver/options/BUILD @@ -1,10 +1,4 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -18,6 +12,7 @@ go_library( "serving.go", ], importpath = "k8s.io/kubernetes/pkg/kubeapiserver/options", + visibility = ["//visibility:public"], deps = [ "//pkg/features:go_default_library", "//pkg/kubeapiserver/authenticator:go_default_library", @@ -29,6 +24,7 @@ go_library( "//plugin/pkg/admission/certificates/approval:go_default_library", "//plugin/pkg/admission/certificates/signing:go_default_library", "//plugin/pkg/admission/certificates/subjectrestriction:go_default_library", + "//plugin/pkg/admission/defaultingressclass:go_default_library", "//plugin/pkg/admission/defaulttolerationseconds:go_default_library", "//plugin/pkg/admission/deny:go_default_library", "//plugin/pkg/admission/eventratelimit:go_default_library", @@ -73,19 +69,6 @@ go_library( ], ) -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) - go_test( name = "go_default_test", srcs = [ @@ -94,12 +77,7 @@ go_test( "authorization_test.go", "plugins_test.go", ], - data = [ - "testdata/client-expired.pem", - "testdata/client-valid.pem", - "testdata/intermediate.pem", - "testdata/root.pem", - ], + data = glob(["testdata/**"]), embed = [":go_default_library"], deps = [ "//pkg/kubeapiserver/authenticator:go_default_library", @@ -112,3 +90,17 @@ go_test( "//vendor/github.com/google/go-cmp/cmp:go_default_library", ], ) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/kubeapiserver/options/plugins.go b/pkg/kubeapiserver/options/plugins.go index 045bd3fc74c..f3c60636d62 100644 --- a/pkg/kubeapiserver/options/plugins.go +++ b/pkg/kubeapiserver/options/plugins.go @@ -27,6 +27,7 @@ import ( certapproval "k8s.io/kubernetes/plugin/pkg/admission/certificates/approval" certsigning "k8s.io/kubernetes/plugin/pkg/admission/certificates/signing" certsubjectrestriction "k8s.io/kubernetes/plugin/pkg/admission/certificates/subjectrestriction" + "k8s.io/kubernetes/plugin/pkg/admission/defaultingressclass" "k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds" "k8s.io/kubernetes/plugin/pkg/admission/deny" "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit" @@ -93,6 +94,7 @@ var AllOrderedPlugins = []string{ certapproval.PluginName, // CertificateApproval certsigning.PluginName, // CertificateSigning certsubjectrestriction.PluginName, // CertificateSubjectRestriction + defaultingressclass.PluginName, // DefaultIngressClass // new admission plugins should generally be inserted above here // webhook, resourcequota, and deny plugins must go at the end @@ -110,6 +112,7 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) { alwayspullimages.Register(plugins) antiaffinity.Register(plugins) defaulttolerationseconds.Register(plugins) + defaultingressclass.Register(plugins) deny.Register(plugins) // DEPRECATED as no real meaning eventratelimit.Register(plugins) exec.Register(plugins) @@ -158,6 +161,7 @@ func DefaultOffAdmissionPlugins() sets.String { certapproval.PluginName, // CertificateApproval certsigning.PluginName, // CertificateSigning certsubjectrestriction.PluginName, // CertificateSubjectRestriction + defaultingressclass.PluginName, //DefaultIngressClass ) return sets.NewString(AllOrderedPlugins...).Difference(defaultOnPlugins) diff --git a/pkg/kubelet/pod_container_deletor.go b/pkg/kubelet/pod_container_deletor.go index 0a00ac90698..1da02a0bdf6 100644 --- a/pkg/kubelet/pod_container_deletor.go +++ b/pkg/kubelet/pod_container_deletor.go @@ -37,9 +37,11 @@ type podContainerDeletor struct { containersToKeep int } -func (a containerStatusbyCreatedList) Len() int { return len(a) } -func (a containerStatusbyCreatedList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a containerStatusbyCreatedList) Less(i, j int) bool { return a[i].CreatedAt.After(a[j].CreatedAt) } +func (a containerStatusbyCreatedList) Len() int { return len(a) } +func (a containerStatusbyCreatedList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a containerStatusbyCreatedList) Less(i, j int) bool { + return a[i].CreatedAt.After(a[j].CreatedAt) +} func newPodContainerDeletor(runtime kubecontainer.Runtime, containersToKeep int) *podContainerDeletor { buffer := make(chan kubecontainer.ContainerID, containerDeletorBufferLimit) diff --git a/pkg/master/storageversionhashdata/data.go b/pkg/master/storageversionhashdata/data.go index ff87134f61b..72bd0056675 100644 --- a/pkg/master/storageversionhashdata/data.go +++ b/pkg/master/storageversionhashdata/data.go @@ -69,6 +69,7 @@ var GVRToStorageVersionHash = map[string]string{ "extensions/v1beta1/ingresses": "ZOAfGflaKd0=", "networking.k8s.io/v1/networkpolicies": "YpfwF18m1G8=", "networking.k8s.io/v1beta1/ingresses": "ZOAfGflaKd0=", + "networking.k8s.io/v1beta1/ingressclasses": "l/iqIbDgFyQ=", "node.k8s.io/v1beta1/runtimeclasses": "8nMHWqj34s0=", "policy/v1beta1/poddisruptionbudgets": "6BGBu0kpHtk=", "policy/v1beta1/podsecuritypolicies": "khBLobUXkqA=", diff --git a/pkg/printers/internalversion/printers.go b/pkg/printers/internalversion/printers.go index b01714cdd1f..5c9669f3251 100644 --- a/pkg/printers/internalversion/printers.go +++ b/pkg/printers/internalversion/printers.go @@ -198,6 +198,7 @@ func AddHandlers(h printers.PrintHandler) { ingressColumnDefinitions := []metav1.TableColumnDefinition{ {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, + {Name: "Class", Type: "string", Description: "The name of the IngressClass resource that should be used for additional configuration"}, {Name: "Hosts", Type: "string", Description: "Hosts that incoming requests are matched against before the ingress rule"}, {Name: "Address", Type: "string", Description: "Address is a list containing ingress points for the load-balancer"}, {Name: "Ports", Type: "string", Description: "Ports of TLS configurations that open"}, @@ -206,6 +207,15 @@ func AddHandlers(h printers.PrintHandler) { h.TableHandler(ingressColumnDefinitions, printIngress) h.TableHandler(ingressColumnDefinitions, printIngressList) + ingressClassColumnDefinitions := []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, + {Name: "Controller", Type: "string", Description: "Controller that is responsible for handling this class"}, + {Name: "Parameters", Type: "string", Description: "A reference to a resource with additional parameters"}, + {Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]}, + } + h.TableHandler(ingressClassColumnDefinitions, printIngressClass) + h.TableHandler(ingressClassColumnDefinitions, printIngressClassList) + statefulSetColumnDefinitions := []metav1.TableColumnDefinition{ {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, {Name: "Ready", Type: "string", Description: "Number of the pod with ready state"}, @@ -1133,11 +1143,15 @@ func printIngress(obj *networking.Ingress, options printers.GenerateOptions) ([] row := metav1.TableRow{ Object: runtime.RawExtension{Object: obj}, } + className := "" + if obj.Spec.IngressClassName != nil { + className = *obj.Spec.IngressClassName + } hosts := formatHosts(obj.Spec.Rules) address := loadBalancerStatusStringer(obj.Status.LoadBalancer, options.Wide) ports := formatPorts(obj.Spec.TLS) createTime := translateTimestampSince(obj.CreationTimestamp) - row.Cells = append(row.Cells, obj.Name, hosts, address, ports, createTime) + row.Cells = append(row.Cells, obj.Name, className, hosts, address, ports, createTime) return []metav1.TableRow{row}, nil } @@ -1153,6 +1167,35 @@ func printIngressList(list *networking.IngressList, options printers.GenerateOpt return rows, nil } +func printIngressClass(obj *networking.IngressClass, options printers.GenerateOptions) ([]metav1.TableRow, error) { + row := metav1.TableRow{ + Object: runtime.RawExtension{Object: obj}, + } + parameters := "" + if obj.Spec.Parameters != nil { + parameters = obj.Spec.Parameters.Kind + if obj.Spec.Parameters.APIGroup != nil { + parameters = parameters + "." + *obj.Spec.Parameters.APIGroup + } + parameters = parameters + "/" + obj.Spec.Parameters.Name + } + createTime := translateTimestampSince(obj.CreationTimestamp) + row.Cells = append(row.Cells, obj.Name, obj.Spec.Controller, parameters, createTime) + return []metav1.TableRow{row}, nil +} + +func printIngressClassList(list *networking.IngressClassList, options printers.GenerateOptions) ([]metav1.TableRow, error) { + rows := make([]metav1.TableRow, 0, len(list.Items)) + for i := range list.Items { + r, err := printIngressClass(&list.Items[i], options) + if err != nil { + return nil, err + } + rows = append(rows, r...) + } + return rows, nil +} + func printStatefulSet(obj *apps.StatefulSet, options printers.GenerateOptions) ([]metav1.TableRow, error) { row := metav1.TableRow{ Object: runtime.RawExtension{Object: obj}, diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go index f4e3fa2a79d..7201980fabd 100644 --- a/pkg/printers/internalversion/printers_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -844,6 +844,7 @@ func TestPrintIngress(t *testing.T) { CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)}, }, Spec: networking.IngressSpec{ + IngressClassName: utilpointer.StringPtr("foo"), Backend: &networking.IngressBackend{ ServiceName: "svc", ServicePort: intstr.FromInt(93), @@ -861,7 +862,7 @@ func TestPrintIngress(t *testing.T) { }, } // Columns: Name, Hosts, Address, Ports, Age - expected := []metav1.TableRow{{Cells: []interface{}{"test1", "*", "2.3.4.5", "80", "10y"}}} + expected := []metav1.TableRow{{Cells: []interface{}{"test1", "foo", "*", "2.3.4.5", "80", "10y"}}} rows, err := printIngress(&ingress, printers.GenerateOptions{}) if err != nil { @@ -873,6 +874,71 @@ func TestPrintIngress(t *testing.T) { } } +func TestPrintIngressClass(t *testing.T) { + testCases := []struct { + name string + ingressClass *networking.IngressClass + expected []metav1.TableRow + }{{ + name: "example with params", + ingressClass: &networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test1", + CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)}, + }, + Spec: networking.IngressClassSpec{ + Controller: "example.com/controller", + Parameters: &api.TypedLocalObjectReference{Kind: "customgroup", Name: "example"}, + }, + }, + expected: []metav1.TableRow{{Cells: []interface{}{"test1", "example.com/controller", "customgroup/example", "10y"}}}, + }, { + name: "example with params + API Group", + ingressClass: &networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test1", + CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)}, + }, + Spec: networking.IngressClassSpec{ + Controller: "example.com/controller", + Parameters: &api.TypedLocalObjectReference{ + APIGroup: utilpointer.StringPtr("example.com"), + Kind: "customgroup", + Name: "example", + }, + }, + }, + expected: []metav1.TableRow{{Cells: []interface{}{"test1", "example.com/controller", "customgroup.example.com/example", "10y"}}}, + }, { + name: "example without params", + ingressClass: &networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test2", + CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-11, 0, 0)}, + }, + Spec: networking.IngressClassSpec{ + Controller: "example.com/controller2", + }, + }, + expected: []metav1.TableRow{{Cells: []interface{}{"test2", "example.com/controller2", "", "11y"}}}, + }} + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + rows, err := printIngressClass(testCase.ingressClass, printers.GenerateOptions{}) + if err != nil { + t.Fatalf("Error generating table rows for Ingress: %#v", err) + } + for i := range rows { + rows[i].Object.Object = nil + } + if !reflect.DeepEqual(testCase.expected, rows) { + t.Errorf("mismatch: %s", diff.ObjectReflectDiff(testCase.expected, rows)) + } + }) + } +} + func TestPrintServiceLoadBalancer(t *testing.T) { tests := []struct { service api.Service diff --git a/pkg/registry/BUILD b/pkg/registry/BUILD index 104c094931b..78c3a21fd8e 100644 --- a/pkg/registry/BUILD +++ b/pkg/registry/BUILD @@ -78,6 +78,7 @@ filegroup( "//pkg/registry/flowcontrol/prioritylevelconfiguration:all-srcs", "//pkg/registry/flowcontrol/rest:all-srcs", "//pkg/registry/networking/ingress:all-srcs", + "//pkg/registry/networking/ingressclass:all-srcs", "//pkg/registry/networking/networkpolicy:all-srcs", "//pkg/registry/networking/rest:all-srcs", "//pkg/registry/node/rest:all-srcs", diff --git a/pkg/registry/networking/ingressclass/BUILD b/pkg/registry/networking/ingressclass/BUILD new file mode 100644 index 00000000000..bbe684aae9e --- /dev/null +++ b/pkg/registry/networking/ingressclass/BUILD @@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "strategy.go", + ], + importpath = "k8s.io/kubernetes/pkg/registry/networking/ingressclass", + visibility = ["//visibility:public"], + deps = [ + "//pkg/api/legacyscheme:go_default_library", + "//pkg/apis/networking:go_default_library", + "//pkg/apis/networking/validation:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["strategy_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/apis/networking:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//pkg/registry/networking/ingressclass/storage:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/registry/networking/ingressclass/doc.go b/pkg/registry/networking/ingressclass/doc.go new file mode 100644 index 00000000000..37c3d071b23 --- /dev/null +++ b/pkg/registry/networking/ingressclass/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 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 ingressclass // import "k8s.io/kubernetes/pkg/registry/networking/ingressclass" diff --git a/pkg/registry/networking/ingressclass/storage/BUILD b/pkg/registry/networking/ingressclass/storage/BUILD new file mode 100644 index 00000000000..f0a43f29f11 --- /dev/null +++ b/pkg/registry/networking/ingressclass/storage/BUILD @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["storage.go"], + importpath = "k8s.io/kubernetes/pkg/registry/networking/ingressclass/storage", + visibility = ["//visibility:public"], + deps = [ + "//pkg/apis/networking:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/printers/internalversion:go_default_library", + "//pkg/printers/storage:go_default_library", + "//pkg/registry/networking/ingressclass:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/registry/networking/ingressclass/storage/storage.go b/pkg/registry/networking/ingressclass/storage/storage.go new file mode 100644 index 00000000000..187c7317cba --- /dev/null +++ b/pkg/registry/networking/ingressclass/storage/storage.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 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 storage + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/generic" + genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" + "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" + printerstorage "k8s.io/kubernetes/pkg/printers/storage" + "k8s.io/kubernetes/pkg/registry/networking/ingressclass" +) + +// REST implements a RESTStorage for replication controllers +type REST struct { + *genericregistry.Store +} + +// NewREST returns a RESTStorage object that will work against replication controllers. +func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) { + store := &genericregistry.Store{ + NewFunc: func() runtime.Object { return &networking.IngressClass{} }, + NewListFunc: func() runtime.Object { return &networking.IngressClassList{} }, + DefaultQualifiedResource: networking.Resource("ingressclasses"), + + CreateStrategy: ingressclass.Strategy, + UpdateStrategy: ingressclass.Strategy, + DeleteStrategy: ingressclass.Strategy, + + TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, + } + options := &generic.StoreOptions{RESTOptions: optsGetter} + if err := store.CompleteWithOptions(options); err != nil { + return nil, err + } + + return &REST{store}, nil +} diff --git a/pkg/registry/networking/ingressclass/strategy.go b/pkg/registry/networking/ingressclass/strategy.go new file mode 100644 index 00000000000..af00a4e915c --- /dev/null +++ b/pkg/registry/networking/ingressclass/strategy.go @@ -0,0 +1,96 @@ +/* +Copyright 2020 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 ingressclass + +import ( + "context" + + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apiserver/pkg/storage/names" + "k8s.io/kubernetes/pkg/api/legacyscheme" + "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/apis/networking/validation" +) + +// ingressClassStrategy implements verification logic for IngressClass +// resources. +type ingressClassStrategy struct { + runtime.ObjectTyper + names.NameGenerator +} + +// Strategy is the default logic that applies when creating and updating +// IngressClass objects. +var Strategy = ingressClassStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} + +// NamespaceScoped returns false because IngressClass is a non-namespaced +// resource. +func (ingressClassStrategy) NamespaceScoped() bool { + return false +} + +// PrepareForCreate prepares an IngressClass for creation. +func (ingressClassStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { + ingressClass := obj.(*networking.IngressClass) + ingressClass.Generation = 1 +} + +// PrepareForUpdate clears fields that are not allowed to be set by end users on +// update. +func (ingressClassStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { + newIngressClass := obj.(*networking.IngressClass) + oldIngressClass := old.(*networking.IngressClass) + + // Any changes to the spec increment the generation number. + // See metav1.ObjectMeta description for more information on Generation. + if !apiequality.Semantic.DeepEqual(oldIngressClass.Spec, newIngressClass.Spec) { + newIngressClass.Generation = oldIngressClass.Generation + 1 + } + +} + +// Validate validates a new IngressClass. +func (ingressClassStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { + ingressClass := obj.(*networking.IngressClass) + return validation.ValidateIngressClass(ingressClass) +} + +// Canonicalize normalizes the object after validation. +func (ingressClassStrategy) Canonicalize(obj runtime.Object) { +} + +// AllowCreateOnUpdate is false for IngressClass; this means POST is needed to +// create one. +func (ingressClassStrategy) AllowCreateOnUpdate() bool { + return false +} + +// ValidateUpdate is the default update validation for an end user. +func (ingressClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { + newIngressClass := obj.(*networking.IngressClass) + oldIngressClass := old.(*networking.IngressClass) + + return validation.ValidateIngressClassUpdate(newIngressClass, oldIngressClass) +} + +// AllowUnconditionalUpdate is the default update policy for IngressClass +// objects. +func (ingressClassStrategy) AllowUnconditionalUpdate() bool { + return true +} diff --git a/pkg/registry/networking/ingressclass/strategy_test.go b/pkg/registry/networking/ingressclass/strategy_test.go new file mode 100644 index 00000000000..1f504b4ae4e --- /dev/null +++ b/pkg/registry/networking/ingressclass/strategy_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2020 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 ingressclass + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/kubernetes/pkg/apis/networking" +) + +func TestIngressClassStrategy(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + if Strategy.NamespaceScoped() { + t.Errorf("IngressClass must not be namespace scoped") + } + if Strategy.AllowCreateOnUpdate() { + t.Errorf("IngressClass should not allow create on update") + } + + ingressClass := networking.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + ResourceVersion: "1", + }, + Spec: networking.IngressClassSpec{ + Controller: "example.com/controller", + }, + } + + Strategy.PrepareForCreate(ctx, &ingressClass) + if ingressClass.Generation != 1 { + t.Error("IngressClass generation should be 1") + } + errs := Strategy.Validate(ctx, &ingressClass) + if len(errs) != 0 { + t.Errorf("Unexpected error from validation for IngressClass: %v", errs) + } + + newIngressClass := ingressClass.DeepCopy() + Strategy.PrepareForUpdate(ctx, newIngressClass, &ingressClass) + errs = Strategy.ValidateUpdate(ctx, newIngressClass, &ingressClass) + if len(errs) != 0 { + t.Errorf("Unexpected error from update validation for IngressClass: %v", errs) + } + + ingressClass.Name = "invalid/name" + + errs = Strategy.Validate(ctx, &ingressClass) + if len(errs) == 0 { + t.Errorf("Expected error from validation for IngressClass, got none") + } + errs = Strategy.ValidateUpdate(ctx, &ingressClass, &ingressClass) + if len(errs) == 0 { + t.Errorf("Unexpected error from update validation for IngressClass, got none") + } +} diff --git a/pkg/registry/networking/rest/BUILD b/pkg/registry/networking/rest/BUILD index 81632678d72..23fea07fef8 100644 --- a/pkg/registry/networking/rest/BUILD +++ b/pkg/registry/networking/rest/BUILD @@ -13,6 +13,7 @@ go_library( "//pkg/api/legacyscheme:go_default_library", "//pkg/apis/networking:go_default_library", "//pkg/registry/networking/ingress/storage:go_default_library", + "//pkg/registry/networking/ingressclass/storage:go_default_library", "//pkg/registry/networking/networkpolicy/storage:go_default_library", "//staging/src/k8s.io/api/networking/v1:go_default_library", "//staging/src/k8s.io/api/networking/v1beta1:go_default_library", diff --git a/pkg/registry/networking/rest/storage_settings.go b/pkg/registry/networking/rest/storage_settings.go index 21d0de5ee3c..ec1474887bc 100644 --- a/pkg/registry/networking/rest/storage_settings.go +++ b/pkg/registry/networking/rest/storage_settings.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/networking" ingressstore "k8s.io/kubernetes/pkg/registry/networking/ingress/storage" + ingressclassstore "k8s.io/kubernetes/pkg/registry/networking/ingressclass/storage" networkpolicystore "k8s.io/kubernetes/pkg/registry/networking/networkpolicy/storage" ) @@ -76,6 +77,14 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag } storage["ingresses"] = ingressStorage storage["ingresses/status"] = ingressStatusStorage + + // ingressclasses + ingressClassStorage, err := ingressclassstore.NewREST(restOptionsGetter) + if err != nil { + return storage, err + } + storage["ingressclasses"] = ingressClassStorage + return storage, nil } diff --git a/plugin/BUILD b/plugin/BUILD index b64ad30a317..e604729052f 100644 --- a/plugin/BUILD +++ b/plugin/BUILD @@ -15,6 +15,7 @@ filegroup( "//plugin/pkg/admission/alwayspullimages:all-srcs", "//plugin/pkg/admission/antiaffinity:all-srcs", "//plugin/pkg/admission/certificates:all-srcs", + "//plugin/pkg/admission/defaultingressclass:all-srcs", "//plugin/pkg/admission/defaulttolerationseconds:all-srcs", "//plugin/pkg/admission/deny:all-srcs", "//plugin/pkg/admission/eventratelimit:all-srcs", diff --git a/plugin/pkg/admission/defaultingressclass/BUILD b/plugin/pkg/admission/defaultingressclass/BUILD new file mode 100644 index 00000000000..65399b5bb88 --- /dev/null +++ b/plugin/pkg/admission/defaultingressclass/BUILD @@ -0,0 +1,53 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["admission.go"], + importpath = "k8s.io/kubernetes/plugin/pkg/admission/defaultingressclass", + visibility = ["//visibility:public"], + deps = [ + "//pkg/apis/networking:go_default_library", + "//pkg/features:go_default_library", + "//staging/src/k8s.io/api/networking/v1beta1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", + "//staging/src/k8s.io/client-go/informers:go_default_library", + "//staging/src/k8s.io/client-go/listers/networking/v1beta1:go_default_library", + "//staging/src/k8s.io/component-base/featuregate:go_default_library", + "//vendor/k8s.io/klog:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["admission_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/apis/core:go_default_library", + "//pkg/apis/networking:go_default_library", + "//pkg/controller:go_default_library", + "//staging/src/k8s.io/api/networking/v1beta1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library", + "//staging/src/k8s.io/client-go/informers:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/plugin/pkg/admission/defaultingressclass/admission.go b/plugin/pkg/admission/defaultingressclass/admission.go new file mode 100644 index 00000000000..71e830fe55b --- /dev/null +++ b/plugin/pkg/admission/defaultingressclass/admission.go @@ -0,0 +1,173 @@ +/* +Copyright 2020 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 defaultingressclass + +import ( + "context" + "fmt" + "io" + + networkingv1beta1 "k8s.io/api/networking/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apiserver/pkg/admission" + genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" + "k8s.io/client-go/informers" + networkingv1beta1listers "k8s.io/client-go/listers/networking/v1beta1" + "k8s.io/component-base/featuregate" + "k8s.io/klog" + "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/features" +) + +const ( + // PluginName is the name of this admission controller plugin + PluginName = "DefaultIngressClass" +) + +// Register registers a plugin +func Register(plugins *admission.Plugins) { + plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { + plugin := newPlugin() + return plugin, nil + }) +} + +// classDefaulterPlugin holds state for and implements the admission plugin. +type classDefaulterPlugin struct { + *admission.Handler + lister networkingv1beta1listers.IngressClassLister + + inspectedFeatures bool + defaultIngressClassEnabled bool +} + +var _ admission.Interface = &classDefaulterPlugin{} +var _ admission.MutationInterface = &classDefaulterPlugin{} +var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&classDefaulterPlugin{}) + +// newPlugin creates a new admission plugin. +func newPlugin() *classDefaulterPlugin { + return &classDefaulterPlugin{ + Handler: admission.NewHandler(admission.Create), + } +} + +// InspectFeatureGates allows setting bools without taking a dep on a global variable +func (a *classDefaulterPlugin) InspectFeatureGates(featureGates featuregate.FeatureGate) { + a.defaultIngressClassEnabled = featureGates.Enabled(features.DefaultIngressClass) + a.inspectedFeatures = true +} + +// SetExternalKubeInformerFactory sets a lister and readyFunc for this +// classDefaulterPlugin using the provided SharedInformerFactory. +func (a *classDefaulterPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { + if !a.defaultIngressClassEnabled { + return + } + informer := f.Networking().V1beta1().IngressClasses() + a.lister = informer.Lister() + a.SetReadyFunc(informer.Informer().HasSynced) +} + +// ValidateInitialization ensures lister is set. +func (a *classDefaulterPlugin) ValidateInitialization() error { + if !a.inspectedFeatures { + return fmt.Errorf("InspectFeatureGates was not called") + } + if !a.defaultIngressClassEnabled { + return nil + } + if a.lister == nil { + return fmt.Errorf("missing lister") + } + return nil +} + +// Admit sets the default value of a Ingress's class if the user did not specify +// a class. +func (a *classDefaulterPlugin) Admit(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error { + if !a.defaultIngressClassEnabled { + return nil + } + if attr.GetResource().GroupResource() != networkingv1beta1.Resource("ingresses") { + return nil + } + + if len(attr.GetSubresource()) != 0 { + return nil + } + + ingress, ok := attr.GetObject().(*networking.Ingress) + // if we can't convert then we don't handle this object so just return + if !ok { + klog.V(3).Infof("Expected Ingress resource, got: %v", attr.GetKind()) + return errors.NewInternalError(fmt.Errorf("Expected Ingress resource, got: %v", attr.GetKind())) + } + + // IngressClassName field has been set, no need to set a default value. + if ingress.Spec.IngressClassName != nil { + return nil + } + + // Ingress class annotation has been set, no need to set a default value. + if _, ok := ingress.Annotations[networkingv1beta1.AnnotationIngressClass]; ok { + return nil + } + + klog.V(4).Infof("No class specified on Ingress %s", ingress.Name) + + defaultClass, err := getDefaultClass(a.lister) + if err != nil { + return admission.NewForbidden(attr, err) + } + + // No default class specified, no need to set a default value. + if defaultClass == nil { + return nil + } + + klog.V(4).Infof("Defaulting class for Ingress %s to %s", ingress.Name, defaultClass.Name) + ingress.Spec.IngressClassName = &defaultClass.Name + return nil +} + +// getDefaultClass returns the default IngressClass from the store, or nil. +func getDefaultClass(lister networkingv1beta1listers.IngressClassLister) (*networkingv1beta1.IngressClass, error) { + list, err := lister.List(labels.Everything()) + if err != nil { + return nil, err + } + + defaultClasses := []*networkingv1beta1.IngressClass{} + for _, class := range list { + if class.Annotations[networkingv1beta1.AnnotationIsDefaultIngressClass] == "true" { + defaultClasses = append(defaultClasses, class) + } + } + + if len(defaultClasses) == 0 { + return nil, nil + } + + if len(defaultClasses) > 1 { + klog.V(3).Infof("%d default IngressClasses were found, only 1 allowed", len(defaultClasses)) + return nil, errors.NewInternalError(fmt.Errorf("%d default IngressClasses were found, only 1 allowed", len(defaultClasses))) + } + + return defaultClasses[0], nil +} diff --git a/plugin/pkg/admission/defaultingressclass/admission_test.go b/plugin/pkg/admission/defaultingressclass/admission_test.go new file mode 100644 index 00000000000..01a7e271606 --- /dev/null +++ b/plugin/pkg/admission/defaultingressclass/admission_test.go @@ -0,0 +1,198 @@ +/* +Copyright 2020 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 defaultingressclass + +import ( + "context" + "fmt" + "reflect" + "testing" + + networkingv1beta1 "k8s.io/api/networking/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/admission" + admissiontesting "k8s.io/apiserver/pkg/admission/testing" + "k8s.io/client-go/informers" + api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/controller" + utilpointer "k8s.io/utils/pointer" +) + +func TestAdmission(t *testing.T) { + defaultClass1 := &networkingv1beta1.IngressClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "IngressClass", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "default1", + Annotations: map[string]string{ + networkingv1beta1.AnnotationIsDefaultIngressClass: "true", + }, + }, + } + defaultClass2 := &networkingv1beta1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default2", + Annotations: map[string]string{ + networkingv1beta1.AnnotationIsDefaultIngressClass: "true", + }, + }, + } + // Class that has explicit default = false + classWithFalseDefault := &networkingv1beta1.IngressClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "IngressClass", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nondefault1", + Annotations: map[string]string{ + networkingv1beta1.AnnotationIsDefaultIngressClass: "false", + }, + }, + } + // Class with missing default annotation (=non-default) + classWithNoDefault := &networkingv1beta1.IngressClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "IngressClass", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nondefault2", + }, + } + // Class with empty default annotation (=non-default) + classWithEmptyDefault := &networkingv1beta1.IngressClass{ + TypeMeta: metav1.TypeMeta{ + Kind: "IngressClass", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nondefault2", + Annotations: map[string]string{ + networkingv1beta1.AnnotationIsDefaultIngressClass: "", + }, + }, + } + + testCases := []struct { + name string + classes []*networkingv1beta1.IngressClass + classField *string + classAnnotation *string + expectedClass *string + expectedError error + }{ + { + name: "no default, no modification of Ingress", + classes: []*networkingv1beta1.IngressClass{classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: nil, + classAnnotation: nil, + expectedClass: nil, + expectedError: nil, + }, + { + name: "one default, modify Ingress with class=nil", + classes: []*networkingv1beta1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: nil, + classAnnotation: nil, + expectedClass: utilpointer.StringPtr(defaultClass1.Name), + expectedError: nil, + }, + { + name: "one default, no modification of Ingress with class field=''", + classes: []*networkingv1beta1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: utilpointer.StringPtr(""), + classAnnotation: nil, + expectedClass: utilpointer.StringPtr(""), + expectedError: nil, + }, + { + name: "one default, no modification of Ingress with class field='foo'", + classes: []*networkingv1beta1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: utilpointer.StringPtr("foo"), + classAnnotation: nil, + expectedClass: utilpointer.StringPtr("foo"), + expectedError: nil, + }, + { + name: "one default, no modification of Ingress with class annotation='foo'", + classes: []*networkingv1beta1.IngressClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: nil, + classAnnotation: utilpointer.StringPtr("foo"), + expectedClass: nil, + expectedError: nil, + }, + { + name: "two defaults, error with Ingress with class field=nil", + classes: []*networkingv1beta1.IngressClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: nil, + classAnnotation: nil, + expectedClass: nil, + expectedError: errors.NewForbidden(networkingv1beta1.Resource("ingresses"), "testing", errors.NewInternalError(fmt.Errorf("2 default IngressClasses were found, only 1 allowed"))), + }, + { + name: "two defaults, no modification with Ingress with class field=''", + classes: []*networkingv1beta1.IngressClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault}, + classField: utilpointer.StringPtr(""), + classAnnotation: nil, + expectedClass: utilpointer.StringPtr(""), + expectedError: nil, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + ctrl := newPlugin() + ctrl.defaultIngressClassEnabled = true + informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) + ctrl.SetExternalKubeInformerFactory(informerFactory) + for _, c := range testCase.classes { + informerFactory.Networking().V1beta1().IngressClasses().Informer().GetStore().Add(c) + } + + ingress := &networking.Ingress{ObjectMeta: metav1.ObjectMeta{Name: "testing", Namespace: "testing"}} + if testCase.classField != nil { + ingress.Spec.IngressClassName = testCase.classField + } + if testCase.classAnnotation != nil { + ingress.Annotations = map[string]string{networkingv1beta1.AnnotationIngressClass: *testCase.classAnnotation} + } + + attrs := admission.NewAttributesRecord( + ingress, // new object + nil, // old object + api.Kind("Ingress").WithVersion("version"), + ingress.Namespace, + ingress.Name, + networkingv1beta1.Resource("ingresses").WithVersion("version"), + "", // subresource + admission.Create, + &metav1.CreateOptions{}, + false, // dryRun + nil, // userInfo + ) + + err := admissiontesting.WithReinvocationTesting(t, ctrl).Admit(context.TODO(), attrs, nil) + if !reflect.DeepEqual(err, testCase.expectedError) { + t.Errorf("Expected error: %v, got %v", testCase.expectedError, err) + } + if !reflect.DeepEqual(testCase.expectedClass, ingress.Spec.IngressClassName) { + t.Errorf("Expected class name %+v, got %+v", *testCase.expectedClass, ingress.Spec.IngressClassName) + } + }) + } +} diff --git a/staging/src/k8s.io/api/extensions/v1beta1/generated.pb.go b/staging/src/k8s.io/api/extensions/v1beta1/generated.pb.go index 9cc89c7b80f..edd0b622d39 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/generated.pb.go @@ -1683,237 +1683,238 @@ func init() { } var fileDescriptor_cdc93917efc28165 = []byte{ - // 3665 bytes of a gzipped FileDescriptorProto + // 3684 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0x4f, 0x6c, 0x1b, 0x47, 0x77, 0xf7, 0x92, 0x94, 0x48, 0x3d, 0xfd, 0x1f, 0xc9, 0x12, 0x3f, 0xfb, 0xb3, 0xe8, 0x6f, 0x03, 0xb8, 0x4e, 0x6a, 0x93, 0xb1, 0x63, 0xfb, 0x73, 0x6d, 0x34, 0x89, 0x28, 0x59, 0xb6, 0x52, 0xfd, 0x61, 0x86, 0x92, 0x1b, 0x04, 0x4d, 0x9a, 0x15, 0x39, 0xa2, 0xd6, 0x5a, 0xee, 0x6e, 0x76, 0x96, 0x8a, 0x08, 0xf4, 0xd0, 0x43, 0x51, 0xa0, 0x40, 0x8b, 0xf6, 0x92, 0xb6, 0xc7, 0x06, 0x05, 0x7a, - 0x6a, 0xd1, 0xde, 0xda, 0x43, 0x10, 0xa0, 0x40, 0x0a, 0x18, 0x45, 0x5a, 0xe4, 0xd6, 0x9c, 0x84, - 0x46, 0x39, 0x15, 0x3d, 0xf5, 0x56, 0xf8, 0x50, 0x14, 0x33, 0x3b, 0xfb, 0x7f, 0x57, 0x5c, 0x29, - 0xb6, 0xd0, 0x00, 0xbd, 0x89, 0xf3, 0xde, 0xfb, 0xbd, 0x37, 0x33, 0x6f, 0xde, 0x7b, 0x33, 0xfb, - 0x04, 0x2b, 0xfb, 0xf7, 0x69, 0x55, 0x35, 0x6a, 0xfb, 0xbd, 0x1d, 0x62, 0xe9, 0xc4, 0x26, 0xb4, - 0x76, 0x40, 0xf4, 0xb6, 0x61, 0xd5, 0x04, 0x41, 0x31, 0xd5, 0x1a, 0x39, 0xb4, 0x89, 0x4e, 0x55, - 0x43, 0xa7, 0xb5, 0x83, 0x5b, 0x3b, 0xc4, 0x56, 0x6e, 0xd5, 0x3a, 0x44, 0x27, 0x96, 0x62, 0x93, - 0x76, 0xd5, 0xb4, 0x0c, 0xdb, 0x40, 0x57, 0x1c, 0xf6, 0xaa, 0x62, 0xaa, 0x55, 0x9f, 0xbd, 0x2a, - 0xd8, 0x2f, 0xdd, 0xec, 0xa8, 0xf6, 0x5e, 0x6f, 0xa7, 0xda, 0x32, 0xba, 0xb5, 0x8e, 0xd1, 0x31, - 0x6a, 0x5c, 0x6a, 0xa7, 0xb7, 0xcb, 0x7f, 0xf1, 0x1f, 0xfc, 0x2f, 0x07, 0xed, 0x92, 0x1c, 0x50, - 0xde, 0x32, 0x2c, 0x52, 0x3b, 0x88, 0x69, 0xbc, 0x74, 0xc7, 0xe7, 0xe9, 0x2a, 0xad, 0x3d, 0x55, - 0x27, 0x56, 0xbf, 0x66, 0xee, 0x77, 0xd8, 0x00, 0xad, 0x75, 0x89, 0xad, 0x24, 0x49, 0xd5, 0xd2, - 0xa4, 0xac, 0x9e, 0x6e, 0xab, 0x5d, 0x12, 0x13, 0xb8, 0x37, 0x48, 0x80, 0xb6, 0xf6, 0x48, 0x57, - 0x89, 0xc9, 0xbd, 0x95, 0x26, 0xd7, 0xb3, 0x55, 0xad, 0xa6, 0xea, 0x36, 0xb5, 0xad, 0xa8, 0x90, - 0x7c, 0x07, 0xa6, 0x16, 0x35, 0xcd, 0xf8, 0x8c, 0xb4, 0x97, 0x9a, 0xab, 0xcb, 0x96, 0x7a, 0x40, - 0x2c, 0x74, 0x15, 0x0a, 0xba, 0xd2, 0x25, 0x65, 0xe9, 0xaa, 0x74, 0x7d, 0xa4, 0x3e, 0xf6, 0xfc, - 0xa8, 0x72, 0xe1, 0xf8, 0xa8, 0x52, 0xd8, 0x50, 0xba, 0x04, 0x73, 0x8a, 0xfc, 0x10, 0xa6, 0x85, - 0xd4, 0x8a, 0x46, 0x0e, 0x9f, 0x1a, 0x5a, 0xaf, 0x4b, 0xd0, 0x35, 0x18, 0x6e, 0x73, 0x00, 0x21, - 0x38, 0x21, 0x04, 0x87, 0x1d, 0x58, 0x2c, 0xa8, 0x32, 0x85, 0x49, 0x21, 0xfc, 0xc4, 0xa0, 0x76, - 0x43, 0xb1, 0xf7, 0xd0, 0x6d, 0x00, 0x53, 0xb1, 0xf7, 0x1a, 0x16, 0xd9, 0x55, 0x0f, 0x85, 0x38, - 0x12, 0xe2, 0xd0, 0xf0, 0x28, 0x38, 0xc0, 0x85, 0x6e, 0x40, 0xc9, 0x22, 0x4a, 0x7b, 0x53, 0xd7, - 0xfa, 0xe5, 0xdc, 0x55, 0xe9, 0x7a, 0xa9, 0x3e, 0x25, 0x24, 0x4a, 0x58, 0x8c, 0x63, 0x8f, 0x43, - 0xfe, 0x3c, 0x07, 0x23, 0xcb, 0x0a, 0xe9, 0x1a, 0x7a, 0x93, 0xd8, 0xe8, 0x13, 0x28, 0xb1, 0xed, - 0x6a, 0x2b, 0xb6, 0xc2, 0xb5, 0x8d, 0xde, 0x7e, 0xb3, 0xea, 0xbb, 0x93, 0xb7, 0x7a, 0x55, 0x73, - 0xbf, 0xc3, 0x06, 0x68, 0x95, 0x71, 0x57, 0x0f, 0x6e, 0x55, 0x37, 0x77, 0x9e, 0x91, 0x96, 0xbd, - 0x4e, 0x6c, 0xc5, 0xb7, 0xcf, 0x1f, 0xc3, 0x1e, 0x2a, 0xda, 0x80, 0x02, 0x35, 0x49, 0x8b, 0x5b, - 0x36, 0x7a, 0xfb, 0x46, 0xf5, 0x44, 0x67, 0xad, 0x7a, 0x96, 0x35, 0x4d, 0xd2, 0xf2, 0x57, 0x9c, - 0xfd, 0xc2, 0x1c, 0x07, 0x3d, 0x85, 0x61, 0x6a, 0x2b, 0x76, 0x8f, 0x96, 0xf3, 0x1c, 0xb1, 0x9a, - 0x19, 0x91, 0x4b, 0xf9, 0x9b, 0xe1, 0xfc, 0xc6, 0x02, 0x4d, 0xfe, 0x8f, 0x1c, 0x20, 0x8f, 0x77, - 0xc9, 0xd0, 0xdb, 0xaa, 0xad, 0x1a, 0x3a, 0x7a, 0x00, 0x05, 0xbb, 0x6f, 0xba, 0x2e, 0x70, 0xcd, - 0x35, 0x68, 0xab, 0x6f, 0x92, 0x17, 0x47, 0x95, 0xb9, 0xb8, 0x04, 0xa3, 0x60, 0x2e, 0x83, 0xd6, - 0x3c, 0x53, 0x73, 0x5c, 0xfa, 0x4e, 0x58, 0xf5, 0x8b, 0xa3, 0x4a, 0xc2, 0x61, 0xab, 0x7a, 0x48, - 0x61, 0x03, 0xd1, 0x01, 0x20, 0x4d, 0xa1, 0xf6, 0x96, 0xa5, 0xe8, 0xd4, 0xd1, 0xa4, 0x76, 0x89, - 0x58, 0x84, 0x37, 0xb2, 0x6d, 0x1a, 0x93, 0xa8, 0x5f, 0x12, 0x56, 0xa0, 0xb5, 0x18, 0x1a, 0x4e, - 0xd0, 0xc0, 0xbc, 0xd9, 0x22, 0x0a, 0x35, 0xf4, 0x72, 0x21, 0xec, 0xcd, 0x98, 0x8f, 0x62, 0x41, - 0x45, 0xaf, 0x43, 0xb1, 0x4b, 0x28, 0x55, 0x3a, 0xa4, 0x3c, 0xc4, 0x19, 0x27, 0x05, 0x63, 0x71, - 0xdd, 0x19, 0xc6, 0x2e, 0x5d, 0xfe, 0x52, 0x82, 0x71, 0x6f, 0xe5, 0xd6, 0x54, 0x6a, 0xa3, 0xdf, - 0x8a, 0xf9, 0x61, 0x35, 0xdb, 0x94, 0x98, 0x34, 0xf7, 0x42, 0xcf, 0xe7, 0xdd, 0x91, 0x80, 0x0f, - 0xae, 0xc3, 0x90, 0x6a, 0x93, 0x2e, 0xdb, 0x87, 0xfc, 0xf5, 0xd1, 0xdb, 0xd7, 0xb3, 0xba, 0x4c, - 0x7d, 0x5c, 0x80, 0x0e, 0xad, 0x32, 0x71, 0xec, 0xa0, 0xc8, 0x7f, 0x5a, 0x08, 0x98, 0xcf, 0x5c, - 0x13, 0x7d, 0x04, 0x25, 0x4a, 0x34, 0xd2, 0xb2, 0x0d, 0x4b, 0x98, 0xff, 0x56, 0x46, 0xf3, 0x95, - 0x1d, 0xa2, 0x35, 0x85, 0x68, 0x7d, 0x8c, 0xd9, 0xef, 0xfe, 0xc2, 0x1e, 0x24, 0x7a, 0x1f, 0x4a, - 0x36, 0xe9, 0x9a, 0x9a, 0x62, 0x13, 0x71, 0x8e, 0x5e, 0x0b, 0x4e, 0x81, 0x79, 0x0e, 0x03, 0x6b, - 0x18, 0xed, 0x2d, 0xc1, 0xc6, 0x8f, 0x8f, 0xb7, 0x24, 0xee, 0x28, 0xf6, 0x60, 0xd0, 0x01, 0x4c, - 0xf4, 0xcc, 0x36, 0xe3, 0xb4, 0x59, 0x14, 0xec, 0xf4, 0x85, 0x27, 0xdd, 0xcb, 0xba, 0x36, 0xdb, - 0x21, 0xe9, 0xfa, 0x9c, 0xd0, 0x35, 0x11, 0x1e, 0xc7, 0x11, 0x2d, 0x68, 0x11, 0x26, 0xbb, 0xaa, - 0xce, 0xe2, 0x52, 0xbf, 0x49, 0x5a, 0x86, 0xde, 0xa6, 0xdc, 0xad, 0x86, 0xea, 0xf3, 0x02, 0x60, - 0x72, 0x3d, 0x4c, 0xc6, 0x51, 0x7e, 0xf4, 0x1e, 0x20, 0x77, 0x1a, 0x8f, 0x9d, 0x20, 0xae, 0x1a, - 0x3a, 0xf7, 0xb9, 0xbc, 0xef, 0xdc, 0x5b, 0x31, 0x0e, 0x9c, 0x20, 0x85, 0xd6, 0x60, 0xd6, 0x22, - 0x07, 0x2a, 0x9b, 0xe3, 0x13, 0x95, 0xda, 0x86, 0xd5, 0x5f, 0x53, 0xbb, 0xaa, 0x5d, 0x1e, 0xe6, - 0x36, 0x95, 0x8f, 0x8f, 0x2a, 0xb3, 0x38, 0x81, 0x8e, 0x13, 0xa5, 0xe4, 0x3f, 0x1b, 0x86, 0xc9, - 0x48, 0xbc, 0x41, 0x4f, 0x61, 0xae, 0xd5, 0xb3, 0x2c, 0xa2, 0xdb, 0x1b, 0xbd, 0xee, 0x0e, 0xb1, - 0x9a, 0xad, 0x3d, 0xd2, 0xee, 0x69, 0xa4, 0xcd, 0x1d, 0x65, 0xa8, 0xbe, 0x20, 0x2c, 0x9e, 0x5b, - 0x4a, 0xe4, 0xc2, 0x29, 0xd2, 0x6c, 0x15, 0x74, 0x3e, 0xb4, 0xae, 0x52, 0xea, 0x61, 0xe6, 0x38, - 0xa6, 0xb7, 0x0a, 0x1b, 0x31, 0x0e, 0x9c, 0x20, 0xc5, 0x6c, 0x6c, 0x13, 0xaa, 0x5a, 0xa4, 0x1d, - 0xb5, 0x31, 0x1f, 0xb6, 0x71, 0x39, 0x91, 0x0b, 0xa7, 0x48, 0xa3, 0xbb, 0x30, 0xea, 0x68, 0xe3, - 0xfb, 0x27, 0x36, 0x7a, 0x46, 0x80, 0x8d, 0x6e, 0xf8, 0x24, 0x1c, 0xe4, 0x63, 0x53, 0x33, 0x76, - 0x28, 0xb1, 0x0e, 0x48, 0x3b, 0x7d, 0x83, 0x37, 0x63, 0x1c, 0x38, 0x41, 0x8a, 0x4d, 0xcd, 0xf1, - 0xc0, 0xd8, 0xd4, 0x86, 0xc3, 0x53, 0xdb, 0x4e, 0xe4, 0xc2, 0x29, 0xd2, 0xcc, 0x8f, 0x1d, 0x93, - 0x17, 0x0f, 0x14, 0x55, 0x53, 0x76, 0x34, 0x52, 0x2e, 0x86, 0xfd, 0x78, 0x23, 0x4c, 0xc6, 0x51, - 0x7e, 0xf4, 0x18, 0xa6, 0x9d, 0xa1, 0x6d, 0x5d, 0xf1, 0x40, 0x4a, 0x1c, 0xe4, 0x67, 0x02, 0x64, - 0x7a, 0x23, 0xca, 0x80, 0xe3, 0x32, 0xe8, 0x01, 0x4c, 0xb4, 0x0c, 0x4d, 0xe3, 0xfe, 0xb8, 0x64, - 0xf4, 0x74, 0xbb, 0x3c, 0xc2, 0x51, 0x10, 0x3b, 0x8f, 0x4b, 0x21, 0x0a, 0x8e, 0x70, 0x22, 0x02, - 0xd0, 0x72, 0x13, 0x0e, 0x2d, 0x03, 0x8f, 0x8f, 0xb7, 0xb2, 0xc6, 0x00, 0x2f, 0x55, 0xf9, 0x35, - 0x80, 0x37, 0x44, 0x71, 0x00, 0x58, 0xfe, 0x67, 0x09, 0xe6, 0x53, 0x42, 0x07, 0x7a, 0x27, 0x94, - 0x62, 0x7f, 0x35, 0x92, 0x62, 0x2f, 0xa7, 0x88, 0x05, 0xf2, 0xac, 0x0e, 0xe3, 0x16, 0x9b, 0x95, - 0xde, 0x71, 0x58, 0x44, 0x8c, 0xbc, 0x3b, 0x60, 0x1a, 0x38, 0x28, 0xe3, 0xc7, 0xfc, 0xe9, 0xe3, - 0xa3, 0xca, 0x78, 0x88, 0x86, 0xc3, 0xf0, 0xf2, 0x9f, 0xe7, 0x00, 0x96, 0x89, 0xa9, 0x19, 0xfd, - 0x2e, 0xd1, 0xcf, 0xa3, 0x86, 0xda, 0x0c, 0xd5, 0x50, 0x37, 0x07, 0x6d, 0x8f, 0x67, 0x5a, 0x6a, - 0x11, 0xf5, 0x9b, 0x91, 0x22, 0xaa, 0x96, 0x1d, 0xf2, 0xe4, 0x2a, 0xea, 0xdf, 0xf2, 0x30, 0xe3, - 0x33, 0xfb, 0x65, 0xd4, 0xc3, 0xd0, 0x1e, 0xff, 0x4a, 0x64, 0x8f, 0xe7, 0x13, 0x44, 0x5e, 0x59, - 0x1d, 0xf5, 0x0c, 0x26, 0x58, 0x95, 0xe3, 0xec, 0x25, 0xaf, 0xa1, 0x86, 0x4f, 0x5d, 0x43, 0x79, - 0xd9, 0x6e, 0x2d, 0x84, 0x84, 0x23, 0xc8, 0x29, 0x35, 0x5b, 0xf1, 0xa7, 0x58, 0xb3, 0x7d, 0x25, - 0xc1, 0x84, 0xbf, 0x4d, 0xe7, 0x50, 0xb4, 0x6d, 0x84, 0x8b, 0xb6, 0xd7, 0x33, 0xbb, 0x68, 0x4a, - 0xd5, 0xf6, 0xdf, 0xac, 0xc0, 0xf7, 0x98, 0xd8, 0x01, 0xdf, 0x51, 0x5a, 0xfb, 0x83, 0xef, 0x78, - 0xe8, 0x73, 0x09, 0x90, 0xc8, 0x02, 0x8b, 0xba, 0x6e, 0xd8, 0x8a, 0x13, 0x2b, 0x1d, 0xb3, 0x56, - 0x33, 0x9b, 0xe5, 0x6a, 0xac, 0x6e, 0xc7, 0xb0, 0x1e, 0xe9, 0xb6, 0xd5, 0xf7, 0x37, 0x39, 0xce, - 0x80, 0x13, 0x0c, 0x40, 0x0a, 0x80, 0x25, 0x30, 0xb7, 0x0c, 0x71, 0x90, 0x6f, 0x66, 0x88, 0x79, - 0x4c, 0x60, 0xc9, 0xd0, 0x77, 0xd5, 0x8e, 0x1f, 0x76, 0xb0, 0x07, 0x84, 0x03, 0xa0, 0x97, 0x1e, - 0xc1, 0x7c, 0x8a, 0xb5, 0x68, 0x0a, 0xf2, 0xfb, 0xa4, 0xef, 0x2c, 0x1b, 0x66, 0x7f, 0xa2, 0x59, - 0x18, 0x3a, 0x50, 0xb4, 0x9e, 0x13, 0x7e, 0x47, 0xb0, 0xf3, 0xe3, 0x41, 0xee, 0xbe, 0x24, 0x7f, - 0x39, 0x14, 0xf4, 0x1d, 0x5e, 0x31, 0x5f, 0x67, 0x97, 0x56, 0x53, 0x53, 0x5b, 0x0a, 0x15, 0x85, - 0xd0, 0x98, 0x73, 0x61, 0x75, 0xc6, 0xb0, 0x47, 0x0d, 0xd5, 0xd6, 0xb9, 0x57, 0x5b, 0x5b, 0xe7, - 0x5f, 0x4e, 0x6d, 0xfd, 0xdb, 0x50, 0xa2, 0x6e, 0x55, 0x5d, 0xe0, 0x90, 0xb7, 0x4e, 0x11, 0x5f, - 0x45, 0x41, 0xed, 0x29, 0xf0, 0x4a, 0x69, 0x0f, 0x34, 0xa9, 0x88, 0x1e, 0x3a, 0x65, 0x11, 0xfd, - 0x52, 0x0b, 0x5f, 0x16, 0x6f, 0x4c, 0xa5, 0x47, 0x49, 0x9b, 0xc7, 0xb6, 0x92, 0x1f, 0x6f, 0x1a, - 0x7c, 0x14, 0x0b, 0x2a, 0xfa, 0x28, 0xe4, 0xb2, 0xa5, 0xb3, 0xb8, 0xec, 0x44, 0xba, 0xbb, 0xa2, - 0x6d, 0x98, 0x37, 0x2d, 0xa3, 0x63, 0x11, 0x4a, 0x97, 0x89, 0xd2, 0xd6, 0x54, 0x9d, 0xb8, 0xeb, - 0xe3, 0x54, 0x44, 0x97, 0x8f, 0x8f, 0x2a, 0xf3, 0x8d, 0x64, 0x16, 0x9c, 0x26, 0x2b, 0x3f, 0x2f, - 0xc0, 0x54, 0x34, 0x03, 0xa6, 0x14, 0xa9, 0xd2, 0x99, 0x8a, 0xd4, 0x1b, 0x81, 0xc3, 0xe0, 0x54, - 0xf0, 0x81, 0x17, 0x9c, 0xd8, 0x81, 0x58, 0x84, 0x49, 0x11, 0x0d, 0x5c, 0xa2, 0x28, 0xd3, 0xbd, - 0xdd, 0xdf, 0x0e, 0x93, 0x71, 0x94, 0x1f, 0x3d, 0x84, 0x71, 0x8b, 0xd7, 0xdd, 0x2e, 0x80, 0x53, - 0xbb, 0x5e, 0x14, 0x00, 0xe3, 0x38, 0x48, 0xc4, 0x61, 0x5e, 0x56, 0xb7, 0xfa, 0xe5, 0xa8, 0x0b, - 0x50, 0x08, 0xd7, 0xad, 0x8b, 0x51, 0x06, 0x1c, 0x97, 0x41, 0xeb, 0x30, 0xd3, 0xd3, 0xe3, 0x50, - 0x8e, 0x2b, 0x5f, 0x16, 0x50, 0x33, 0xdb, 0x71, 0x16, 0x9c, 0x24, 0x87, 0x76, 0x43, 0xa5, 0xec, - 0x30, 0x0f, 0xcf, 0xb7, 0x33, 0x1f, 0xbc, 0xcc, 0xb5, 0x6c, 0x42, 0xb9, 0x5d, 0xca, 0x5a, 0x6e, - 0xcb, 0xff, 0x28, 0x05, 0x93, 0x90, 0x57, 0x02, 0x0f, 0x7a, 0x65, 0x8a, 0x49, 0x04, 0xaa, 0x23, - 0x23, 0xb9, 0xfa, 0xbd, 0x77, 0xaa, 0xea, 0xd7, 0x4f, 0x9e, 0x83, 0xcb, 0xdf, 0x2f, 0x24, 0x98, - 0x5b, 0x69, 0x3e, 0xb6, 0x8c, 0x9e, 0xe9, 0x9a, 0xb3, 0x69, 0x3a, 0x4b, 0xf3, 0x4b, 0x28, 0x58, - 0x3d, 0xcd, 0x9d, 0xc7, 0x6b, 0xee, 0x3c, 0x70, 0x4f, 0x63, 0xf3, 0x98, 0x89, 0x48, 0x39, 0x93, - 0x60, 0x02, 0x68, 0x03, 0x86, 0x2d, 0x45, 0xef, 0x10, 0x37, 0xad, 0x5e, 0x1b, 0x60, 0xfd, 0xea, - 0x32, 0x66, 0xec, 0x81, 0xc2, 0x86, 0x4b, 0x63, 0x81, 0x22, 0xff, 0x91, 0x04, 0x93, 0x4f, 0xb6, - 0xb6, 0x1a, 0xab, 0x3a, 0x3f, 0xd1, 0xfc, 0x6d, 0xf5, 0x2a, 0x14, 0x4c, 0xc5, 0xde, 0x8b, 0x66, - 0x7a, 0x46, 0xc3, 0x9c, 0x82, 0x3e, 0x80, 0x22, 0x8b, 0x24, 0x44, 0x6f, 0x67, 0x2c, 0xb5, 0x05, - 0x7c, 0xdd, 0x11, 0xf2, 0xab, 0x27, 0x31, 0x80, 0x5d, 0x38, 0x79, 0x1f, 0x66, 0x03, 0xe6, 0xb0, - 0xf5, 0x78, 0xca, 0xb2, 0x23, 0x6a, 0xc2, 0x10, 0xd3, 0xcc, 0x72, 0x60, 0x3e, 0xc3, 0x63, 0x66, - 0x64, 0x4a, 0x7e, 0xa5, 0xc3, 0x7e, 0x51, 0xec, 0x60, 0xc9, 0xeb, 0x30, 0xce, 0x1f, 0x94, 0x0d, - 0xcb, 0xe6, 0xcb, 0x82, 0xae, 0x40, 0xbe, 0xab, 0xea, 0x22, 0xcf, 0x8e, 0x0a, 0x99, 0x3c, 0xcb, - 0x11, 0x6c, 0x9c, 0x93, 0x95, 0x43, 0x11, 0x79, 0x7c, 0xb2, 0x72, 0x88, 0xd9, 0xb8, 0xfc, 0x18, - 0x8a, 0x62, 0xb9, 0x83, 0x40, 0xf9, 0x93, 0x81, 0xf2, 0x09, 0x40, 0x9b, 0x50, 0x5c, 0x6d, 0xd4, - 0x35, 0xc3, 0xa9, 0xba, 0x5a, 0x6a, 0xdb, 0x8a, 0xee, 0xc5, 0xd2, 0xea, 0x32, 0xc6, 0x9c, 0x82, - 0x64, 0x18, 0x26, 0x87, 0x2d, 0x62, 0xda, 0xdc, 0x23, 0x46, 0xea, 0xc0, 0x76, 0xf9, 0x11, 0x1f, - 0xc1, 0x82, 0x22, 0xff, 0x71, 0x0e, 0x8a, 0x62, 0x39, 0xce, 0xe1, 0x16, 0xb6, 0x16, 0xba, 0x85, - 0xbd, 0x91, 0xcd, 0x35, 0x52, 0xaf, 0x60, 0x5b, 0x91, 0x2b, 0xd8, 0x8d, 0x8c, 0x78, 0x27, 0xdf, - 0xbf, 0xfe, 0x4e, 0x82, 0x89, 0xb0, 0x53, 0xa2, 0xbb, 0x30, 0xca, 0x12, 0x8e, 0xda, 0x22, 0x1b, - 0x7e, 0x9d, 0xeb, 0x3d, 0xc2, 0x34, 0x7d, 0x12, 0x0e, 0xf2, 0xa1, 0x8e, 0x27, 0xc6, 0xfc, 0x48, - 0x4c, 0x3a, 0x7d, 0x49, 0x7b, 0xb6, 0xaa, 0x55, 0x9d, 0x4f, 0x2b, 0xd5, 0x55, 0xdd, 0xde, 0xb4, - 0x9a, 0xb6, 0xa5, 0xea, 0x9d, 0x98, 0x22, 0xee, 0x94, 0x41, 0x64, 0xf9, 0x1f, 0x24, 0x18, 0x15, - 0x26, 0x9f, 0xc3, 0xad, 0xe2, 0x37, 0xc2, 0xb7, 0x8a, 0x6b, 0x19, 0x0f, 0x78, 0xf2, 0x95, 0xe2, - 0xaf, 0x7c, 0xd3, 0xd9, 0x91, 0x66, 0x5e, 0xbd, 0x67, 0x50, 0x3b, 0xea, 0xd5, 0xec, 0x30, 0x62, - 0x4e, 0x41, 0x3d, 0x98, 0x52, 0x23, 0x31, 0x40, 0x2c, 0x6d, 0x2d, 0x9b, 0x25, 0x9e, 0x58, 0xbd, - 0x2c, 0xe0, 0xa7, 0xa2, 0x14, 0x1c, 0x53, 0x21, 0x13, 0x88, 0x71, 0xa1, 0xf7, 0xa1, 0xb0, 0x67, - 0xdb, 0x66, 0xc2, 0x7b, 0xf5, 0x80, 0xc8, 0xe3, 0x9b, 0x50, 0xe2, 0xb3, 0xdb, 0xda, 0x6a, 0x60, - 0x0e, 0x25, 0xff, 0x8f, 0xbf, 0x1e, 0x4d, 0xc7, 0xc7, 0xbd, 0x78, 0x2a, 0x9d, 0x25, 0x9e, 0x8e, - 0x26, 0xc5, 0x52, 0xf4, 0x04, 0xf2, 0xb6, 0x96, 0xf5, 0x5a, 0x28, 0x10, 0xb7, 0xd6, 0x9a, 0x7e, - 0x40, 0xda, 0x5a, 0x6b, 0x62, 0x06, 0x81, 0x36, 0x61, 0x88, 0x65, 0x1f, 0x76, 0x04, 0xf3, 0xd9, - 0x8f, 0x34, 0x9b, 0xbf, 0xef, 0x10, 0xec, 0x17, 0xc5, 0x0e, 0x8e, 0xfc, 0x29, 0x8c, 0x87, 0xce, - 0x29, 0xfa, 0x04, 0xc6, 0x34, 0x43, 0x69, 0xd7, 0x15, 0x4d, 0xd1, 0x5b, 0xc4, 0xfd, 0x38, 0x70, - 0x2d, 0xe9, 0x86, 0xb1, 0x16, 0xe0, 0x13, 0xa7, 0x7c, 0x56, 0x28, 0x19, 0x0b, 0xd2, 0x70, 0x08, - 0x51, 0x56, 0x00, 0xfc, 0x39, 0xa2, 0x0a, 0x0c, 0x31, 0x3f, 0x73, 0xf2, 0xc9, 0x48, 0x7d, 0x84, - 0x59, 0xc8, 0xdc, 0x8f, 0x62, 0x67, 0x1c, 0xdd, 0x06, 0xa0, 0xa4, 0x65, 0x11, 0x9b, 0x07, 0x83, - 0x5c, 0xf8, 0x03, 0x63, 0xd3, 0xa3, 0xe0, 0x00, 0x97, 0xfc, 0x4f, 0x12, 0x8c, 0x6f, 0x10, 0xfb, - 0x33, 0xc3, 0xda, 0x6f, 0x18, 0x9a, 0xda, 0xea, 0x9f, 0x43, 0xb0, 0xc5, 0xa1, 0x60, 0xfb, 0xe6, - 0x80, 0x9d, 0x09, 0x59, 0x97, 0x16, 0x72, 0xe5, 0xaf, 0x24, 0x98, 0x0f, 0x71, 0x3e, 0xf2, 0x8f, - 0xee, 0x36, 0x0c, 0x99, 0x86, 0x65, 0xbb, 0x89, 0xf8, 0x54, 0x0a, 0x59, 0x18, 0x0b, 0xa4, 0x62, - 0x06, 0x83, 0x1d, 0x34, 0xb4, 0x06, 0x39, 0xdb, 0x10, 0xae, 0x7a, 0x3a, 0x4c, 0x42, 0xac, 0x3a, - 0x08, 0xcc, 0xdc, 0x96, 0x81, 0x73, 0xb6, 0xc1, 0x36, 0xa2, 0x1c, 0xe2, 0x0a, 0x06, 0x9f, 0x57, - 0x34, 0x03, 0x0c, 0x85, 0x5d, 0xcb, 0xe8, 0x9e, 0x79, 0x0e, 0xde, 0x46, 0xac, 0x58, 0x46, 0x17, - 0x73, 0x2c, 0xf9, 0x6b, 0x09, 0xa6, 0x43, 0x9c, 0xe7, 0x10, 0xf8, 0xdf, 0x0f, 0x07, 0xfe, 0x1b, - 0xa7, 0x99, 0x48, 0x4a, 0xf8, 0xff, 0x3a, 0x17, 0x99, 0x06, 0x9b, 0x30, 0xda, 0x85, 0x51, 0xd3, - 0x68, 0x37, 0x5f, 0xc2, 0xe7, 0xc0, 0x49, 0x96, 0x37, 0x1b, 0x3e, 0x16, 0x0e, 0x02, 0xa3, 0x43, - 0x98, 0xd6, 0x95, 0x2e, 0xa1, 0xa6, 0xd2, 0x22, 0xcd, 0x97, 0xf0, 0x40, 0x72, 0x91, 0x7f, 0x6f, - 0x88, 0x22, 0xe2, 0xb8, 0x12, 0xb4, 0x0e, 0x45, 0xd5, 0xe4, 0x75, 0x9c, 0xa8, 0x5d, 0x06, 0x66, - 0x51, 0xa7, 0xea, 0x73, 0xe2, 0xb9, 0xf8, 0x81, 0x5d, 0x0c, 0xf9, 0xaf, 0xa3, 0xde, 0xc0, 0xfc, - 0x0f, 0x3d, 0x86, 0x12, 0x6f, 0xcc, 0x68, 0x19, 0x9a, 0xfb, 0x65, 0x80, 0xed, 0x6c, 0x43, 0x8c, - 0xbd, 0x38, 0xaa, 0x5c, 0x4e, 0x78, 0xf4, 0x75, 0xc9, 0xd8, 0x13, 0x46, 0x1b, 0x50, 0x30, 0x7f, - 0x4c, 0x05, 0xc3, 0x93, 0x1c, 0x2f, 0x5b, 0x38, 0x8e, 0xfc, 0x7b, 0xf9, 0x88, 0xb9, 0x3c, 0xd5, - 0x3d, 0x7b, 0x69, 0xbb, 0xee, 0x55, 0x4c, 0xa9, 0x3b, 0xbf, 0x03, 0x45, 0x91, 0xe1, 0x85, 0x33, - 0xff, 0xf2, 0x34, 0xce, 0x1c, 0xcc, 0x62, 0xde, 0x85, 0xc5, 0x1d, 0x74, 0x81, 0xd1, 0xc7, 0x30, - 0x4c, 0x1c, 0x15, 0x4e, 0x6e, 0xbc, 0x77, 0x1a, 0x15, 0x7e, 0x5c, 0xf5, 0x0b, 0x55, 0x31, 0x26, - 0x50, 0xd1, 0x3b, 0x6c, 0xbd, 0x18, 0x2f, 0xbb, 0x04, 0xd2, 0x72, 0x81, 0xa7, 0xab, 0x2b, 0xce, - 0xb4, 0xbd, 0xe1, 0x17, 0x47, 0x15, 0xf0, 0x7f, 0xe2, 0xa0, 0x84, 0xfc, 0x2f, 0x12, 0x4c, 0xf3, - 0x15, 0x6a, 0xf5, 0x2c, 0xd5, 0xee, 0x9f, 0x5b, 0x62, 0x7a, 0x1a, 0x4a, 0x4c, 0x77, 0x06, 0x2c, - 0x4b, 0xcc, 0xc2, 0xd4, 0xe4, 0xf4, 0x8d, 0x04, 0x17, 0x63, 0xdc, 0xe7, 0x10, 0x17, 0xb7, 0xc3, - 0x71, 0xf1, 0xcd, 0xd3, 0x4e, 0x28, 0x25, 0x36, 0xfe, 0xd7, 0x74, 0xc2, 0x74, 0xf8, 0x49, 0xb9, - 0x0d, 0x60, 0x5a, 0xea, 0x81, 0xaa, 0x91, 0x8e, 0xf8, 0x08, 0x5e, 0x0a, 0xb4, 0x38, 0x79, 0x14, - 0x1c, 0xe0, 0x42, 0x14, 0xe6, 0xda, 0x64, 0x57, 0xe9, 0x69, 0xf6, 0x62, 0xbb, 0xbd, 0xa4, 0x98, - 0xca, 0x8e, 0xaa, 0xa9, 0xb6, 0x2a, 0x9e, 0x0b, 0x46, 0xea, 0x0f, 0x9d, 0x8f, 0xd3, 0x49, 0x1c, - 0x2f, 0x8e, 0x2a, 0x57, 0x92, 0xbe, 0x0e, 0xb9, 0x2c, 0x7d, 0x9c, 0x02, 0x8d, 0xfa, 0x50, 0xb6, - 0xc8, 0xa7, 0x3d, 0xd5, 0x22, 0xed, 0x65, 0xcb, 0x30, 0x43, 0x6a, 0xf3, 0x5c, 0xed, 0xaf, 0x1f, - 0x1f, 0x55, 0xca, 0x38, 0x85, 0x67, 0xb0, 0xe2, 0x54, 0x78, 0xf4, 0x0c, 0x66, 0x14, 0xd1, 0x8c, - 0x16, 0xd4, 0xea, 0x9c, 0x92, 0xfb, 0xc7, 0x47, 0x95, 0x99, 0xc5, 0x38, 0x79, 0xb0, 0xc2, 0x24, - 0x50, 0x54, 0x83, 0xe2, 0x01, 0xef, 0x5b, 0xa3, 0xe5, 0x21, 0x8e, 0xcf, 0x12, 0x41, 0xd1, 0x69, - 0x65, 0x63, 0x98, 0xc3, 0x2b, 0x4d, 0x7e, 0xfa, 0x5c, 0x2e, 0x76, 0xa1, 0x64, 0xb5, 0xa4, 0x38, - 0xf1, 0xfc, 0xc5, 0xb8, 0xe4, 0x47, 0xad, 0x27, 0x3e, 0x09, 0x07, 0xf9, 0xd0, 0x47, 0x30, 0xb2, - 0x27, 0x5e, 0x25, 0x68, 0xb9, 0x98, 0x29, 0x09, 0x87, 0x5e, 0x31, 0xea, 0xd3, 0x42, 0xc5, 0x88, - 0x3b, 0x4c, 0xb1, 0x8f, 0x88, 0x5e, 0x87, 0x22, 0xff, 0xb1, 0xba, 0xcc, 0x9f, 0xe3, 0x4a, 0x7e, - 0x6c, 0x7b, 0xe2, 0x0c, 0x63, 0x97, 0xee, 0xb2, 0xae, 0x36, 0x96, 0xf8, 0xb3, 0x70, 0x84, 0x75, - 0xb5, 0xb1, 0x84, 0x5d, 0x3a, 0xfa, 0x04, 0x8a, 0x94, 0xac, 0xa9, 0x7a, 0xef, 0xb0, 0x0c, 0x99, - 0x3e, 0x2a, 0x37, 0x1f, 0x71, 0xee, 0xc8, 0xc3, 0x98, 0xaf, 0x41, 0xd0, 0xb1, 0x0b, 0x8b, 0xf6, - 0x60, 0xc4, 0xea, 0xe9, 0x8b, 0x74, 0x9b, 0x12, 0xab, 0x3c, 0xca, 0x75, 0x0c, 0x0a, 0xe7, 0xd8, - 0xe5, 0x8f, 0x6a, 0xf1, 0x56, 0xc8, 0xe3, 0xc0, 0x3e, 0x38, 0xda, 0x03, 0xe0, 0x3f, 0xf8, 0x1b, - 0x5c, 0x79, 0x8e, 0xab, 0xba, 0x9f, 0x45, 0x55, 0xd2, 0x53, 0x9f, 0x78, 0x87, 0xf7, 0xc8, 0x38, - 0x80, 0x8d, 0xfe, 0x50, 0x02, 0x44, 0x7b, 0xa6, 0xa9, 0x91, 0x2e, 0xd1, 0x6d, 0x45, 0xe3, 0xa3, - 0xb4, 0x3c, 0xc6, 0x55, 0xbe, 0x3b, 0x68, 0x05, 0x63, 0x82, 0x51, 0xd5, 0xde, 0xf3, 0x7a, 0x9c, - 0x15, 0x27, 0xe8, 0x65, 0x9b, 0xb8, 0x2b, 0x66, 0x3d, 0x9e, 0x69, 0x13, 0x93, 0x5f, 0x37, 0xfd, - 0x4d, 0x14, 0x74, 0xec, 0xc2, 0xa2, 0xa7, 0x30, 0xe7, 0x36, 0x58, 0x62, 0xc3, 0xb0, 0x57, 0x54, - 0x8d, 0xd0, 0x3e, 0xb5, 0x49, 0xb7, 0x3c, 0xc1, 0x1d, 0xcc, 0xeb, 0x32, 0xc1, 0x89, 0x5c, 0x38, - 0x45, 0x1a, 0x75, 0xa1, 0xe2, 0x06, 0x27, 0x76, 0x72, 0xbd, 0xe8, 0xf8, 0x88, 0xb6, 0x14, 0xcd, - 0xf9, 0xe2, 0x30, 0xc9, 0x15, 0xbc, 0x76, 0x7c, 0x54, 0xa9, 0x2c, 0x9f, 0xcc, 0x8a, 0x07, 0x61, - 0xa1, 0x0f, 0xa0, 0xac, 0xa4, 0xe9, 0x99, 0xe2, 0x7a, 0x7e, 0xce, 0x22, 0x5e, 0xaa, 0x82, 0x54, - 0x69, 0x64, 0xc3, 0x94, 0x12, 0x6e, 0x75, 0xa5, 0xe5, 0xe9, 0x4c, 0x4f, 0x9e, 0x91, 0x0e, 0x59, - 0xff, 0xd9, 0x23, 0x42, 0xa0, 0x38, 0xa6, 0x01, 0xfd, 0x0e, 0x20, 0x25, 0xda, 0x9d, 0x4b, 0xcb, - 0x28, 0x53, 0xa2, 0x8b, 0xb5, 0xf5, 0xfa, 0x6e, 0x17, 0x23, 0x51, 0x9c, 0xa0, 0x87, 0x15, 0xe8, - 0x4a, 0xa4, 0xa3, 0x98, 0x96, 0xe7, 0xb9, 0xf2, 0x5a, 0x36, 0xe5, 0x9e, 0x5c, 0xe0, 0xc3, 0x4a, - 0x14, 0x11, 0xc7, 0x95, 0xa0, 0x35, 0x98, 0x15, 0x83, 0xdb, 0x3a, 0x55, 0x76, 0x49, 0xb3, 0x4f, - 0x5b, 0xb6, 0x46, 0xcb, 0x33, 0x3c, 0xbe, 0xf3, 0x8f, 0x7b, 0x8b, 0x09, 0x74, 0x9c, 0x28, 0x85, - 0xde, 0x85, 0xa9, 0x5d, 0xc3, 0xda, 0x51, 0xdb, 0x6d, 0xa2, 0xbb, 0x48, 0xb3, 0x1c, 0x69, 0x96, - 0xed, 0xc3, 0x4a, 0x84, 0x86, 0x63, 0xdc, 0x88, 0xc2, 0x45, 0x81, 0xdc, 0xb0, 0x8c, 0xd6, 0xba, - 0xd1, 0xd3, 0x6d, 0xa7, 0xec, 0xbb, 0xe8, 0xa5, 0xd1, 0x8b, 0x8b, 0x49, 0x0c, 0x2f, 0x8e, 0x2a, - 0x57, 0x93, 0xab, 0x7c, 0x9f, 0x09, 0x27, 0x63, 0x23, 0x13, 0xc6, 0x44, 0x9f, 0xf8, 0x92, 0xa6, - 0x50, 0x5a, 0x2e, 0xf3, 0xa3, 0xff, 0x60, 0x70, 0xc0, 0xf3, 0x44, 0xa2, 0xe7, 0x7f, 0xea, 0xf8, - 0xa8, 0x32, 0x16, 0x64, 0xc0, 0x21, 0x0d, 0xbc, 0x2f, 0x48, 0x7c, 0x8d, 0x3a, 0x9f, 0xde, 0xea, - 0xd3, 0xf5, 0x05, 0xf9, 0xa6, 0xbd, 0xb4, 0xbe, 0xa0, 0x00, 0xe4, 0xc9, 0xef, 0xd2, 0xff, 0x99, - 0x83, 0x19, 0x9f, 0x39, 0x73, 0x5f, 0x50, 0x82, 0xc8, 0xff, 0xf7, 0x57, 0x67, 0xeb, 0xd5, 0xf1, - 0x97, 0xee, 0xff, 0x5e, 0xaf, 0x8e, 0x6f, 0x5b, 0xca, 0xed, 0xe1, 0x6f, 0x73, 0xc1, 0x09, 0x9c, - 0xb2, 0x61, 0xe4, 0x25, 0xb4, 0x18, 0xff, 0xe4, 0x7a, 0x4e, 0xe4, 0x6f, 0xf2, 0x30, 0x15, 0x3d, - 0x8d, 0xa1, 0xbe, 0x02, 0x69, 0x60, 0x5f, 0x41, 0x03, 0x66, 0x77, 0x7b, 0x9a, 0xd6, 0xe7, 0x73, - 0x08, 0x34, 0x17, 0x38, 0xdf, 0x05, 0x7f, 0x2e, 0x24, 0x67, 0x57, 0x12, 0x78, 0x70, 0xa2, 0x64, - 0xbc, 0xcd, 0xa0, 0xf0, 0x63, 0xdb, 0x0c, 0x86, 0xce, 0xd0, 0x66, 0x90, 0xdc, 0xa9, 0x91, 0x3f, - 0x53, 0xa7, 0xc6, 0x59, 0x7a, 0x0c, 0x12, 0x82, 0xd8, 0xc0, 0x7e, 0xd9, 0xb7, 0x61, 0x22, 0xdc, - 0xf7, 0xe2, 0xec, 0xa5, 0xd3, 0x7a, 0x23, 0xbe, 0xbf, 0x06, 0xf6, 0xd2, 0x19, 0xc7, 0x1e, 0x87, - 0xfc, 0xfb, 0x12, 0xcc, 0x25, 0xf7, 0xb7, 0x22, 0x0d, 0x26, 0xba, 0xca, 0x61, 0xb0, 0xe7, 0x58, - 0x3a, 0xe3, 0xcb, 0x18, 0x6f, 0x78, 0x58, 0x0f, 0x61, 0xe1, 0x08, 0xb6, 0xfc, 0x83, 0x04, 0xf3, - 0x29, 0xad, 0x06, 0xe7, 0x6b, 0x09, 0xfa, 0x10, 0x4a, 0x5d, 0xe5, 0xb0, 0xd9, 0xb3, 0x3a, 0xe4, - 0xcc, 0x6f, 0x81, 0xfc, 0x40, 0xaf, 0x0b, 0x14, 0xec, 0xe1, 0xc9, 0x7f, 0x29, 0xc1, 0xcf, 0x52, - 0xaf, 0x4a, 0xe8, 0x5e, 0xa8, 0x2b, 0x42, 0x8e, 0x74, 0x45, 0xa0, 0xb8, 0xe0, 0x2b, 0x6a, 0x8a, - 0xf8, 0x42, 0x82, 0x72, 0xda, 0xdd, 0x11, 0xdd, 0x0d, 0x19, 0xf9, 0x8b, 0x88, 0x91, 0xd3, 0x31, - 0xb9, 0x57, 0x64, 0xe3, 0xbf, 0x4a, 0x70, 0xf9, 0x84, 0x1a, 0xcc, 0xbb, 0xa2, 0x90, 0x76, 0x90, - 0x8b, 0x3f, 0x5b, 0x8b, 0x6f, 0x5e, 0xfe, 0x15, 0x25, 0x81, 0x07, 0xa7, 0x4a, 0xa3, 0x6d, 0x98, - 0x17, 0xf7, 0xa3, 0x28, 0x4d, 0x94, 0x17, 0xbc, 0x79, 0x6c, 0x39, 0x99, 0x05, 0xa7, 0xc9, 0xca, - 0x7f, 0x23, 0xc1, 0x5c, 0xf2, 0xa3, 0x00, 0x7a, 0x2b, 0xb4, 0xe4, 0x95, 0xc8, 0x92, 0x4f, 0x46, - 0xa4, 0xc4, 0x82, 0x7f, 0x0c, 0x13, 0xe2, 0xe9, 0x40, 0xc0, 0x08, 0x67, 0x96, 0x93, 0x32, 0x88, - 0x80, 0x70, 0x0b, 0x58, 0x7e, 0x4c, 0xc2, 0x63, 0x38, 0x82, 0x26, 0xff, 0x41, 0x0e, 0x86, 0x9a, - 0x2d, 0x45, 0x23, 0xe7, 0x50, 0xbf, 0xbe, 0x17, 0xaa, 0x5f, 0x07, 0xfd, 0x5b, 0x16, 0xb7, 0x2a, - 0xb5, 0x74, 0xc5, 0x91, 0xd2, 0xf5, 0x8d, 0x4c, 0x68, 0x27, 0x57, 0xad, 0xbf, 0x06, 0x23, 0x9e, - 0xd2, 0xd3, 0x25, 0x53, 0xf9, 0x2f, 0x72, 0x30, 0x1a, 0x50, 0x71, 0xca, 0x54, 0xbc, 0x1b, 0xaa, - 0x3f, 0xf2, 0x19, 0x1e, 0x6a, 0x02, 0xba, 0xaa, 0x6e, 0xc5, 0xe1, 0xb4, 0x15, 0xfb, 0x8d, 0xa4, - 0xf1, 0x42, 0xe4, 0x6d, 0x98, 0xb0, 0x15, 0xab, 0x43, 0x6c, 0xef, 0xc3, 0x45, 0x9e, 0xfb, 0xa2, - 0xd7, 0xdf, 0xbe, 0x15, 0xa2, 0xe2, 0x08, 0xf7, 0xa5, 0x87, 0x30, 0x1e, 0x52, 0x76, 0xaa, 0xae, - 0xe0, 0xbf, 0x97, 0xe0, 0x17, 0x03, 0x1f, 0x7b, 0x50, 0x3d, 0x74, 0x48, 0xaa, 0x91, 0x43, 0xb2, - 0x90, 0x0e, 0xf0, 0xea, 0xba, 0xcb, 0xea, 0x37, 0x9f, 0x7f, 0xbf, 0x70, 0xe1, 0xdb, 0xef, 0x17, - 0x2e, 0x7c, 0xf7, 0xfd, 0xc2, 0x85, 0xdf, 0x3d, 0x5e, 0x90, 0x9e, 0x1f, 0x2f, 0x48, 0xdf, 0x1e, - 0x2f, 0x48, 0xdf, 0x1d, 0x2f, 0x48, 0xff, 0x7e, 0xbc, 0x20, 0xfd, 0xc9, 0x0f, 0x0b, 0x17, 0x3e, - 0x2c, 0x0a, 0xb8, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf8, 0xe2, 0x53, 0x87, 0xe9, 0x3d, 0x00, - 0x00, + 0x6a, 0xd1, 0xde, 0xda, 0x43, 0x10, 0xb4, 0x40, 0x0a, 0x18, 0x45, 0x5a, 0xe4, 0xd6, 0x9c, 0x84, + 0x46, 0x39, 0x15, 0x3d, 0xf5, 0x56, 0xf8, 0x54, 0xcc, 0xec, 0xec, 0xff, 0x5d, 0x71, 0xa5, 0xd8, + 0x42, 0x03, 0xf4, 0x26, 0xce, 0x7b, 0xef, 0xf7, 0xde, 0xcc, 0xbc, 0x79, 0xef, 0xcd, 0xec, 0x13, + 0xac, 0xec, 0xdf, 0xa7, 0x55, 0xd5, 0xa8, 0xed, 0xf7, 0x76, 0x88, 0xa5, 0x13, 0x9b, 0xd0, 0xda, + 0x01, 0xd1, 0xdb, 0x86, 0x55, 0x13, 0x04, 0xc5, 0x54, 0x6b, 0xe4, 0xd0, 0x26, 0x3a, 0x55, 0x0d, + 0x9d, 0xd6, 0x0e, 0x6e, 0xed, 0x10, 0x5b, 0xb9, 0x55, 0xeb, 0x10, 0x9d, 0x58, 0x8a, 0x4d, 0xda, + 0x55, 0xd3, 0x32, 0x6c, 0x03, 0x5d, 0x71, 0xd8, 0xab, 0x8a, 0xa9, 0x56, 0x7d, 0xf6, 0xaa, 0x60, + 0xbf, 0x74, 0xb3, 0xa3, 0xda, 0x7b, 0xbd, 0x9d, 0x6a, 0xcb, 0xe8, 0xd6, 0x3a, 0x46, 0xc7, 0xa8, + 0x71, 0xa9, 0x9d, 0xde, 0x2e, 0xff, 0xc5, 0x7f, 0xf0, 0xbf, 0x1c, 0xb4, 0x4b, 0x72, 0x40, 0x79, + 0xcb, 0xb0, 0x48, 0xed, 0x20, 0xa6, 0xf1, 0xd2, 0x1d, 0x9f, 0xa7, 0xab, 0xb4, 0xf6, 0x54, 0x9d, + 0x58, 0xfd, 0x9a, 0xb9, 0xdf, 0x61, 0x03, 0xb4, 0xd6, 0x25, 0xb6, 0x92, 0x24, 0x55, 0x4b, 0x93, + 0xb2, 0x7a, 0xba, 0xad, 0x76, 0x49, 0x4c, 0xe0, 0xde, 0x20, 0x01, 0xda, 0xda, 0x23, 0x5d, 0x25, + 0x26, 0xf7, 0x56, 0x9a, 0x5c, 0xcf, 0x56, 0xb5, 0x9a, 0xaa, 0xdb, 0xd4, 0xb6, 0xa2, 0x42, 0xf2, + 0x1d, 0x98, 0x5a, 0xd4, 0x34, 0xe3, 0x33, 0xd2, 0x5e, 0x6a, 0xae, 0x2e, 0x5b, 0xea, 0x01, 0xb1, + 0xd0, 0x55, 0x28, 0xe8, 0x4a, 0x97, 0x94, 0xa5, 0xab, 0xd2, 0xf5, 0x91, 0xfa, 0xd8, 0xf3, 0xa3, + 0xca, 0x85, 0xe3, 0xa3, 0x4a, 0x61, 0x43, 0xe9, 0x12, 0xcc, 0x29, 0xf2, 0x43, 0x98, 0x16, 0x52, + 0x2b, 0x1a, 0x39, 0x7c, 0x6a, 0x68, 0xbd, 0x2e, 0x41, 0xd7, 0x60, 0xb8, 0xcd, 0x01, 0x84, 0xe0, + 0x84, 0x10, 0x1c, 0x76, 0x60, 0xb1, 0xa0, 0xca, 0x14, 0x26, 0x85, 0xf0, 0x13, 0x83, 0xda, 0x0d, + 0xc5, 0xde, 0x43, 0xb7, 0x01, 0x4c, 0xc5, 0xde, 0x6b, 0x58, 0x64, 0x57, 0x3d, 0x14, 0xe2, 0x48, + 0x88, 0x43, 0xc3, 0xa3, 0xe0, 0x00, 0x17, 0xba, 0x01, 0x25, 0x8b, 0x28, 0xed, 0x4d, 0x5d, 0xeb, + 0x97, 0x73, 0x57, 0xa5, 0xeb, 0xa5, 0xfa, 0x94, 0x90, 0x28, 0x61, 0x31, 0x8e, 0x3d, 0x0e, 0xf9, + 0xf3, 0x1c, 0x8c, 0x2c, 0x2b, 0xa4, 0x6b, 0xe8, 0x4d, 0x62, 0xa3, 0x4f, 0xa0, 0xc4, 0xb6, 0xab, + 0xad, 0xd8, 0x0a, 0xd7, 0x36, 0x7a, 0xfb, 0xcd, 0xaa, 0xef, 0x4e, 0xde, 0xea, 0x55, 0xcd, 0xfd, + 0x0e, 0x1b, 0xa0, 0x55, 0xc6, 0x5d, 0x3d, 0xb8, 0x55, 0xdd, 0xdc, 0x79, 0x46, 0x5a, 0xf6, 0x3a, + 0xb1, 0x15, 0xdf, 0x3e, 0x7f, 0x0c, 0x7b, 0xa8, 0x68, 0x03, 0x0a, 0xd4, 0x24, 0x2d, 0x6e, 0xd9, + 0xe8, 0xed, 0x1b, 0xd5, 0x13, 0x9d, 0xb5, 0xea, 0x59, 0xd6, 0x34, 0x49, 0xcb, 0x5f, 0x71, 0xf6, + 0x0b, 0x73, 0x1c, 0xf4, 0x14, 0x86, 0xa9, 0xad, 0xd8, 0x3d, 0x5a, 0xce, 0x73, 0xc4, 0x6a, 0x66, + 0x44, 0x2e, 0xe5, 0x6f, 0x86, 0xf3, 0x1b, 0x0b, 0x34, 0xf9, 0x3f, 0x73, 0x80, 0x3c, 0xde, 0x25, + 0x43, 0x6f, 0xab, 0xb6, 0x6a, 0xe8, 0xe8, 0x01, 0x14, 0xec, 0xbe, 0xe9, 0xba, 0xc0, 0x35, 0xd7, + 0xa0, 0xad, 0xbe, 0x49, 0x5e, 0x1c, 0x55, 0xe6, 0xe2, 0x12, 0x8c, 0x82, 0xb9, 0x0c, 0x5a, 0xf3, + 0x4c, 0xcd, 0x71, 0xe9, 0x3b, 0x61, 0xd5, 0x2f, 0x8e, 0x2a, 0x09, 0x87, 0xad, 0xea, 0x21, 0x85, + 0x0d, 0x44, 0x07, 0x80, 0x34, 0x85, 0xda, 0x5b, 0x96, 0xa2, 0x53, 0x47, 0x93, 0xda, 0x25, 0x62, + 0x11, 0xde, 0xc8, 0xb6, 0x69, 0x4c, 0xa2, 0x7e, 0x49, 0x58, 0x81, 0xd6, 0x62, 0x68, 0x38, 0x41, + 0x03, 0xf3, 0x66, 0x8b, 0x28, 0xd4, 0xd0, 0xcb, 0x85, 0xb0, 0x37, 0x63, 0x3e, 0x8a, 0x05, 0x15, + 0xbd, 0x0e, 0xc5, 0x2e, 0xa1, 0x54, 0xe9, 0x90, 0xf2, 0x10, 0x67, 0x9c, 0x14, 0x8c, 0xc5, 0x75, + 0x67, 0x18, 0xbb, 0x74, 0xf9, 0x4b, 0x09, 0xc6, 0xbd, 0x95, 0x5b, 0x53, 0xa9, 0x8d, 0x7e, 0x2b, + 0xe6, 0x87, 0xd5, 0x6c, 0x53, 0x62, 0xd2, 0xdc, 0x0b, 0x3d, 0x9f, 0x77, 0x47, 0x02, 0x3e, 0xb8, + 0x0e, 0x43, 0xaa, 0x4d, 0xba, 0x6c, 0x1f, 0xf2, 0xd7, 0x47, 0x6f, 0x5f, 0xcf, 0xea, 0x32, 0xf5, + 0x71, 0x01, 0x3a, 0xb4, 0xca, 0xc4, 0xb1, 0x83, 0x22, 0xff, 0x69, 0x21, 0x60, 0x3e, 0x73, 0x4d, + 0xf4, 0x11, 0x94, 0x28, 0xd1, 0x48, 0xcb, 0x36, 0x2c, 0x61, 0xfe, 0x5b, 0x19, 0xcd, 0x57, 0x76, + 0x88, 0xd6, 0x14, 0xa2, 0xf5, 0x31, 0x66, 0xbf, 0xfb, 0x0b, 0x7b, 0x90, 0xe8, 0x7d, 0x28, 0xd9, + 0xa4, 0x6b, 0x6a, 0x8a, 0x4d, 0xc4, 0x39, 0x7a, 0x2d, 0x38, 0x05, 0xe6, 0x39, 0x0c, 0xac, 0x61, + 0xb4, 0xb7, 0x04, 0x1b, 0x3f, 0x3e, 0xde, 0x92, 0xb8, 0xa3, 0xd8, 0x83, 0x41, 0x07, 0x30, 0xd1, + 0x33, 0xdb, 0x8c, 0xd3, 0x66, 0x51, 0xb0, 0xd3, 0x17, 0x9e, 0x74, 0x2f, 0xeb, 0xda, 0x6c, 0x87, + 0xa4, 0xeb, 0x73, 0x42, 0xd7, 0x44, 0x78, 0x1c, 0x47, 0xb4, 0xa0, 0x45, 0x98, 0xec, 0xaa, 0x3a, + 0x8b, 0x4b, 0xfd, 0x26, 0x69, 0x19, 0x7a, 0x9b, 0x72, 0xb7, 0x1a, 0xaa, 0xcf, 0x0b, 0x80, 0xc9, + 0xf5, 0x30, 0x19, 0x47, 0xf9, 0xd1, 0x7b, 0x80, 0xdc, 0x69, 0x3c, 0x76, 0x82, 0xb8, 0x6a, 0xe8, + 0xdc, 0xe7, 0xf2, 0xbe, 0x73, 0x6f, 0xc5, 0x38, 0x70, 0x82, 0x14, 0x5a, 0x83, 0x59, 0x8b, 0x1c, + 0xa8, 0x6c, 0x8e, 0x4f, 0x54, 0x6a, 0x1b, 0x56, 0x7f, 0x4d, 0xed, 0xaa, 0x76, 0x79, 0x98, 0xdb, + 0x54, 0x3e, 0x3e, 0xaa, 0xcc, 0xe2, 0x04, 0x3a, 0x4e, 0x94, 0x92, 0xff, 0x6c, 0x18, 0x26, 0x23, + 0xf1, 0x06, 0x3d, 0x85, 0xb9, 0x56, 0xcf, 0xb2, 0x88, 0x6e, 0x6f, 0xf4, 0xba, 0x3b, 0xc4, 0x6a, + 0xb6, 0xf6, 0x48, 0xbb, 0xa7, 0x91, 0x36, 0x77, 0x94, 0xa1, 0xfa, 0x82, 0xb0, 0x78, 0x6e, 0x29, + 0x91, 0x0b, 0xa7, 0x48, 0xb3, 0x55, 0xd0, 0xf9, 0xd0, 0xba, 0x4a, 0xa9, 0x87, 0x99, 0xe3, 0x98, + 0xde, 0x2a, 0x6c, 0xc4, 0x38, 0x70, 0x82, 0x14, 0xb3, 0xb1, 0x4d, 0xa8, 0x6a, 0x91, 0x76, 0xd4, + 0xc6, 0x7c, 0xd8, 0xc6, 0xe5, 0x44, 0x2e, 0x9c, 0x22, 0x8d, 0xee, 0xc2, 0xa8, 0xa3, 0x8d, 0xef, + 0x9f, 0xd8, 0xe8, 0x19, 0x01, 0x36, 0xba, 0xe1, 0x93, 0x70, 0x90, 0x8f, 0x4d, 0xcd, 0xd8, 0xa1, + 0xc4, 0x3a, 0x20, 0xed, 0xf4, 0x0d, 0xde, 0x8c, 0x71, 0xe0, 0x04, 0x29, 0x36, 0x35, 0xc7, 0x03, + 0x63, 0x53, 0x1b, 0x0e, 0x4f, 0x6d, 0x3b, 0x91, 0x0b, 0xa7, 0x48, 0x33, 0x3f, 0x76, 0x4c, 0x5e, + 0x3c, 0x50, 0x54, 0x4d, 0xd9, 0xd1, 0x48, 0xb9, 0x18, 0xf6, 0xe3, 0x8d, 0x30, 0x19, 0x47, 0xf9, + 0xd1, 0x63, 0x98, 0x76, 0x86, 0xb6, 0x75, 0xc5, 0x03, 0x29, 0x71, 0x90, 0x9f, 0x09, 0x90, 0xe9, + 0x8d, 0x28, 0x03, 0x8e, 0xcb, 0xa0, 0x07, 0x30, 0xd1, 0x32, 0x34, 0x8d, 0xfb, 0xe3, 0x92, 0xd1, + 0xd3, 0xed, 0xf2, 0x08, 0x47, 0x41, 0xec, 0x3c, 0x2e, 0x85, 0x28, 0x38, 0xc2, 0x89, 0x08, 0x40, + 0xcb, 0x4d, 0x38, 0xb4, 0x0c, 0x3c, 0x3e, 0xde, 0xca, 0x1a, 0x03, 0xbc, 0x54, 0xe5, 0xd7, 0x00, + 0xde, 0x10, 0xc5, 0x01, 0x60, 0xf9, 0x5f, 0x24, 0x98, 0x4f, 0x09, 0x1d, 0xe8, 0x9d, 0x50, 0x8a, + 0xfd, 0xd5, 0x48, 0x8a, 0xbd, 0x9c, 0x22, 0x16, 0xc8, 0xb3, 0x3a, 0x8c, 0x5b, 0x6c, 0x56, 0x7a, + 0xc7, 0x61, 0x11, 0x31, 0xf2, 0xee, 0x80, 0x69, 0xe0, 0xa0, 0x8c, 0x1f, 0xf3, 0xa7, 0x8f, 0x8f, + 0x2a, 0xe3, 0x21, 0x1a, 0x0e, 0xc3, 0xcb, 0x7f, 0x9e, 0x03, 0x58, 0x26, 0xa6, 0x66, 0xf4, 0xbb, + 0x44, 0x3f, 0x8f, 0x1a, 0x6a, 0x33, 0x54, 0x43, 0xdd, 0x1c, 0xb4, 0x3d, 0x9e, 0x69, 0xa9, 0x45, + 0xd4, 0x6f, 0x46, 0x8a, 0xa8, 0x5a, 0x76, 0xc8, 0x93, 0xab, 0xa8, 0x7f, 0xcf, 0xc3, 0x8c, 0xcf, + 0xec, 0x97, 0x51, 0x0f, 0x43, 0x7b, 0xfc, 0x2b, 0x91, 0x3d, 0x9e, 0x4f, 0x10, 0x79, 0x65, 0x75, + 0xd4, 0x33, 0x98, 0x60, 0x55, 0x8e, 0xb3, 0x97, 0xbc, 0x86, 0x1a, 0x3e, 0x75, 0x0d, 0xe5, 0x65, + 0xbb, 0xb5, 0x10, 0x12, 0x8e, 0x20, 0xa7, 0xd4, 0x6c, 0xc5, 0x9f, 0x62, 0xcd, 0xf6, 0x95, 0x04, + 0x13, 0xfe, 0x36, 0x9d, 0x43, 0xd1, 0xb6, 0x11, 0x2e, 0xda, 0x5e, 0xcf, 0xec, 0xa2, 0x29, 0x55, + 0xdb, 0xff, 0xb0, 0x02, 0xdf, 0x63, 0x62, 0x07, 0x7c, 0x47, 0x69, 0xed, 0x0f, 0xbe, 0xe3, 0xa1, + 0xcf, 0x25, 0x40, 0x22, 0x0b, 0x2c, 0xea, 0xba, 0x61, 0x2b, 0x4e, 0xac, 0x74, 0xcc, 0x5a, 0xcd, + 0x6c, 0x96, 0xab, 0xb1, 0xba, 0x1d, 0xc3, 0x7a, 0xa4, 0xdb, 0x56, 0xdf, 0xdf, 0xe4, 0x38, 0x03, + 0x4e, 0x30, 0x00, 0x29, 0x00, 0x96, 0xc0, 0xdc, 0x32, 0xc4, 0x41, 0xbe, 0x99, 0x21, 0xe6, 0x31, + 0x81, 0x25, 0x43, 0xdf, 0x55, 0x3b, 0x7e, 0xd8, 0xc1, 0x1e, 0x10, 0x0e, 0x80, 0x5e, 0x7a, 0x04, + 0xf3, 0x29, 0xd6, 0xa2, 0x29, 0xc8, 0xef, 0x93, 0xbe, 0xb3, 0x6c, 0x98, 0xfd, 0x89, 0x66, 0x61, + 0xe8, 0x40, 0xd1, 0x7a, 0x4e, 0xf8, 0x1d, 0xc1, 0xce, 0x8f, 0x07, 0xb9, 0xfb, 0x92, 0xfc, 0xe5, + 0x50, 0xd0, 0x77, 0x78, 0xc5, 0x7c, 0x9d, 0x5d, 0x5a, 0x4d, 0x4d, 0x6d, 0x29, 0x54, 0x14, 0x42, + 0x63, 0xce, 0x85, 0xd5, 0x19, 0xc3, 0x1e, 0x35, 0x54, 0x5b, 0xe7, 0x5e, 0x6d, 0x6d, 0x9d, 0x7f, + 0x39, 0xb5, 0xf5, 0x6f, 0x43, 0x89, 0xba, 0x55, 0x75, 0x81, 0x43, 0xde, 0x3a, 0x45, 0x7c, 0x15, + 0x05, 0xb5, 0xa7, 0xc0, 0x2b, 0xa5, 0x3d, 0xd0, 0xa4, 0x22, 0x7a, 0xe8, 0x94, 0x45, 0xf4, 0x4b, + 0x2d, 0x7c, 0x59, 0xbc, 0x31, 0x95, 0x1e, 0x25, 0x6d, 0x1e, 0xdb, 0x4a, 0x7e, 0xbc, 0x69, 0xf0, + 0x51, 0x2c, 0xa8, 0xe8, 0xa3, 0x90, 0xcb, 0x96, 0xce, 0xe2, 0xb2, 0x13, 0xe9, 0xee, 0x8a, 0xb6, + 0x61, 0xde, 0xb4, 0x8c, 0x8e, 0x45, 0x28, 0x5d, 0x26, 0x4a, 0x5b, 0x53, 0x75, 0xe2, 0xae, 0x8f, + 0x53, 0x11, 0x5d, 0x3e, 0x3e, 0xaa, 0xcc, 0x37, 0x92, 0x59, 0x70, 0x9a, 0xac, 0xfc, 0xbc, 0x00, + 0x53, 0xd1, 0x0c, 0x98, 0x52, 0xa4, 0x4a, 0x67, 0x2a, 0x52, 0x6f, 0x04, 0x0e, 0x83, 0x53, 0xc1, + 0x07, 0x5e, 0x70, 0x62, 0x07, 0x62, 0x11, 0x26, 0x45, 0x34, 0x70, 0x89, 0xa2, 0x4c, 0xf7, 0x76, + 0x7f, 0x3b, 0x4c, 0xc6, 0x51, 0x7e, 0xf4, 0x10, 0xc6, 0x2d, 0x5e, 0x77, 0xbb, 0x00, 0x4e, 0xed, + 0x7a, 0x51, 0x00, 0x8c, 0xe3, 0x20, 0x11, 0x87, 0x79, 0x59, 0xdd, 0xea, 0x97, 0xa3, 0x2e, 0x40, + 0x21, 0x5c, 0xb7, 0x2e, 0x46, 0x19, 0x70, 0x5c, 0x06, 0xad, 0xc3, 0x4c, 0x4f, 0x8f, 0x43, 0x39, + 0xae, 0x7c, 0x59, 0x40, 0xcd, 0x6c, 0xc7, 0x59, 0x70, 0x92, 0x1c, 0xda, 0x0d, 0x95, 0xb2, 0xc3, + 0x3c, 0x3c, 0xdf, 0xce, 0x7c, 0xf0, 0x32, 0xd7, 0xb2, 0x09, 0xe5, 0x76, 0x29, 0x6b, 0xb9, 0x2d, + 0xff, 0x93, 0x14, 0x4c, 0x42, 0x5e, 0x09, 0x3c, 0xe8, 0x95, 0x29, 0x26, 0x11, 0xa8, 0x8e, 0x8c, + 0xe4, 0xea, 0xf7, 0xde, 0xa9, 0xaa, 0x5f, 0x3f, 0x79, 0x0e, 0x2e, 0x7f, 0xbf, 0x90, 0x60, 0x6e, + 0xa5, 0xf9, 0xd8, 0x32, 0x7a, 0xa6, 0x6b, 0xce, 0xa6, 0xe9, 0x2c, 0xcd, 0x2f, 0xa1, 0x60, 0xf5, + 0x34, 0x77, 0x1e, 0xaf, 0xb9, 0xf3, 0xc0, 0x3d, 0x8d, 0xcd, 0x63, 0x26, 0x22, 0xe5, 0x4c, 0x82, + 0x09, 0xa0, 0x0d, 0x18, 0xb6, 0x14, 0xbd, 0x43, 0xdc, 0xb4, 0x7a, 0x6d, 0x80, 0xf5, 0xab, 0xcb, + 0x98, 0xb1, 0x07, 0x0a, 0x1b, 0x2e, 0x8d, 0x05, 0x8a, 0xfc, 0x47, 0x12, 0x4c, 0x3e, 0xd9, 0xda, + 0x6a, 0xac, 0xea, 0xfc, 0x44, 0xf3, 0xb7, 0xd5, 0xab, 0x50, 0x30, 0x15, 0x7b, 0x2f, 0x9a, 0xe9, + 0x19, 0x0d, 0x73, 0x0a, 0xfa, 0x00, 0x8a, 0x2c, 0x92, 0x10, 0xbd, 0x9d, 0xb1, 0xd4, 0x16, 0xf0, + 0x75, 0x47, 0xc8, 0xaf, 0x9e, 0xc4, 0x00, 0x76, 0xe1, 0xe4, 0x7d, 0x98, 0x0d, 0x98, 0xc3, 0xd6, + 0xe3, 0x29, 0xcb, 0x8e, 0xa8, 0x09, 0x43, 0x4c, 0x33, 0xcb, 0x81, 0xf9, 0x0c, 0x8f, 0x99, 0x91, + 0x29, 0xf9, 0x95, 0x0e, 0xfb, 0x45, 0xb1, 0x83, 0x25, 0xaf, 0xc3, 0x38, 0x7f, 0x50, 0x36, 0x2c, + 0x9b, 0x2f, 0x0b, 0xba, 0x02, 0xf9, 0xae, 0xaa, 0x8b, 0x3c, 0x3b, 0x2a, 0x64, 0xf2, 0x2c, 0x47, + 0xb0, 0x71, 0x4e, 0x56, 0x0e, 0x45, 0xe4, 0xf1, 0xc9, 0xca, 0x21, 0x66, 0xe3, 0xf2, 0x63, 0x28, + 0x8a, 0xe5, 0x0e, 0x02, 0xe5, 0x4f, 0x06, 0xca, 0x27, 0x00, 0x6d, 0x42, 0x71, 0xb5, 0x51, 0xd7, + 0x0c, 0xa7, 0xea, 0x6a, 0xa9, 0x6d, 0x2b, 0xba, 0x17, 0x4b, 0xab, 0xcb, 0x18, 0x73, 0x0a, 0x92, + 0x61, 0x98, 0x1c, 0xb6, 0x88, 0x69, 0x73, 0x8f, 0x18, 0xa9, 0x03, 0xdb, 0xe5, 0x47, 0x7c, 0x04, + 0x0b, 0x8a, 0xfc, 0xc7, 0x39, 0x28, 0x8a, 0xe5, 0x38, 0x87, 0x5b, 0xd8, 0x5a, 0xe8, 0x16, 0xf6, + 0x46, 0x36, 0xd7, 0x48, 0xbd, 0x82, 0x6d, 0x45, 0xae, 0x60, 0x37, 0x32, 0xe2, 0x9d, 0x7c, 0xff, + 0xfa, 0x3b, 0x09, 0x26, 0xc2, 0x4e, 0x89, 0xee, 0xc2, 0x28, 0x4b, 0x38, 0x6a, 0x8b, 0x6c, 0xf8, + 0x75, 0xae, 0xf7, 0x08, 0xd3, 0xf4, 0x49, 0x38, 0xc8, 0x87, 0x3a, 0x9e, 0x18, 0xf3, 0x23, 0x31, + 0xe9, 0xf4, 0x25, 0xed, 0xd9, 0xaa, 0x56, 0x75, 0x3e, 0xad, 0x54, 0x57, 0x75, 0x7b, 0xd3, 0x6a, + 0xda, 0x96, 0xaa, 0x77, 0x62, 0x8a, 0xb8, 0x53, 0x06, 0x91, 0xe5, 0x7f, 0x90, 0x60, 0x54, 0x98, + 0x7c, 0x0e, 0xb7, 0x8a, 0xdf, 0x08, 0xdf, 0x2a, 0xae, 0x65, 0x3c, 0xe0, 0xc9, 0x57, 0x8a, 0xbf, + 0xf2, 0x4d, 0x67, 0x47, 0x9a, 0x79, 0xf5, 0x9e, 0x41, 0xed, 0xa8, 0x57, 0xb3, 0xc3, 0x88, 0x39, + 0x05, 0xf5, 0x60, 0x4a, 0x8d, 0xc4, 0x00, 0xb1, 0xb4, 0xb5, 0x6c, 0x96, 0x78, 0x62, 0xf5, 0xb2, + 0x80, 0x9f, 0x8a, 0x52, 0x70, 0x4c, 0x85, 0x4c, 0x20, 0xc6, 0x85, 0xde, 0x87, 0xc2, 0x9e, 0x6d, + 0x9b, 0x09, 0xef, 0xd5, 0x03, 0x22, 0x8f, 0x6f, 0x42, 0x89, 0xcf, 0x6e, 0x6b, 0xab, 0x81, 0x39, + 0x94, 0xfc, 0x8f, 0x39, 0x6f, 0x3d, 0x78, 0x91, 0xff, 0xae, 0x37, 0xdb, 0x25, 0x4d, 0xa1, 0x94, + 0xfb, 0x9f, 0x73, 0x21, 0x9d, 0x0d, 0x18, 0xee, 0xd1, 0x70, 0x8c, 0x1b, 0x6d, 0xf9, 0x11, 0x59, + 0x3a, 0x4b, 0x44, 0x1e, 0x4d, 0x8a, 0xc6, 0xe8, 0x09, 0xe4, 0x6d, 0x2d, 0xeb, 0xc5, 0x52, 0x20, + 0x6e, 0xad, 0x35, 0xfd, 0x90, 0xb6, 0xb5, 0xd6, 0xc4, 0x0c, 0x02, 0x6d, 0xc2, 0x10, 0xcb, 0x5f, + 0xec, 0x10, 0xe7, 0xb3, 0x07, 0x05, 0xb6, 0x82, 0xbe, 0x4b, 0xb1, 0x5f, 0x14, 0x3b, 0x38, 0xf2, + 0xa7, 0x30, 0x1e, 0x3a, 0xe9, 0xe8, 0x13, 0x18, 0xd3, 0x0c, 0xa5, 0x5d, 0x57, 0x34, 0x45, 0x6f, + 0x11, 0xf7, 0xf3, 0xc2, 0xb5, 0xa4, 0x3b, 0xca, 0x5a, 0x80, 0x4f, 0xc4, 0x89, 0x59, 0xa1, 0x64, + 0x2c, 0x48, 0xc3, 0x21, 0x44, 0x59, 0x01, 0xf0, 0xe7, 0x88, 0x2a, 0x30, 0xc4, 0x3c, 0xd5, 0xc9, + 0x48, 0x23, 0xf5, 0x11, 0x66, 0x21, 0x73, 0x60, 0x8a, 0x9d, 0x71, 0x74, 0x1b, 0x80, 0x92, 0x96, + 0x45, 0x6c, 0xbe, 0x9d, 0xb9, 0xf0, 0x27, 0xca, 0xa6, 0x47, 0xc1, 0x01, 0x2e, 0xf9, 0x9f, 0x25, + 0x18, 0xdf, 0x20, 0xf6, 0x67, 0x86, 0xb5, 0xdf, 0x30, 0x34, 0xb5, 0xd5, 0x3f, 0x87, 0x70, 0x8d, + 0x43, 0xe1, 0xfa, 0xcd, 0x01, 0x3b, 0x13, 0xb2, 0x2e, 0x2d, 0x68, 0xcb, 0x5f, 0x49, 0x30, 0x1f, + 0xe2, 0x7c, 0xe4, 0x1f, 0xfe, 0x6d, 0x18, 0x32, 0x0d, 0xcb, 0x76, 0x53, 0xf9, 0xa9, 0x14, 0xb2, + 0x40, 0x18, 0x48, 0xe6, 0x0c, 0x06, 0x3b, 0x68, 0x68, 0x0d, 0x72, 0xb6, 0x21, 0x5c, 0xf5, 0x74, + 0x98, 0x84, 0x58, 0x75, 0x10, 0x98, 0xb9, 0x2d, 0x03, 0xe7, 0x6c, 0x83, 0x6d, 0x44, 0x39, 0xc4, + 0x15, 0x0c, 0x5f, 0xaf, 0x68, 0x06, 0x18, 0x0a, 0xbb, 0x96, 0xd1, 0x3d, 0xf3, 0x1c, 0xbc, 0x8d, + 0x58, 0xb1, 0x8c, 0x2e, 0xe6, 0x58, 0xf2, 0xd7, 0x12, 0x4c, 0x87, 0x38, 0xcf, 0x21, 0x75, 0xbc, + 0x1f, 0x4e, 0x1d, 0x37, 0x4e, 0x33, 0x91, 0x94, 0x04, 0xf2, 0x75, 0x2e, 0x32, 0x0d, 0x36, 0x61, + 0xb4, 0x0b, 0xa3, 0xa6, 0xd1, 0x6e, 0xbe, 0x84, 0x0f, 0x8a, 0x93, 0x2c, 0xf3, 0x36, 0x7c, 0x2c, + 0x1c, 0x04, 0x46, 0x87, 0x30, 0xad, 0x2b, 0x5d, 0x42, 0x4d, 0xa5, 0x45, 0x9a, 0x2f, 0xe1, 0x89, + 0xe5, 0x22, 0xff, 0x62, 0x11, 0x45, 0xc4, 0x71, 0x25, 0x68, 0x1d, 0x8a, 0xaa, 0xc9, 0x2b, 0x41, + 0x51, 0xfd, 0x0c, 0xcc, 0xc3, 0x4e, 0xdd, 0xe8, 0xc4, 0x73, 0xf1, 0x03, 0xbb, 0x18, 0xf2, 0x5f, + 0x47, 0xbd, 0x81, 0xf9, 0x1f, 0x7a, 0x0c, 0x25, 0xde, 0xda, 0xd1, 0x32, 0x34, 0xf7, 0xdb, 0x02, + 0xdb, 0xd9, 0x86, 0x18, 0x7b, 0x71, 0x54, 0xb9, 0x9c, 0xf0, 0x6c, 0xec, 0x92, 0xb1, 0x27, 0x8c, + 0x36, 0xa0, 0x60, 0xfe, 0x98, 0x1a, 0x88, 0xa7, 0x49, 0x5e, 0xf8, 0x70, 0x1c, 0xf9, 0xf7, 0xf2, + 0x11, 0x73, 0x79, 0xb2, 0x7c, 0xf6, 0xd2, 0x76, 0xdd, 0xab, 0xb9, 0x52, 0x77, 0x7e, 0x07, 0x8a, + 0x22, 0xd5, 0x0a, 0x67, 0xfe, 0xe5, 0x69, 0x9c, 0x39, 0x98, 0xc5, 0xbc, 0x2b, 0x8f, 0x3b, 0xe8, + 0x02, 0xa3, 0x8f, 0x61, 0x98, 0x38, 0x2a, 0x9c, 0xdc, 0x78, 0xef, 0x34, 0x2a, 0xfc, 0xb8, 0xea, + 0x97, 0xba, 0x62, 0x4c, 0xa0, 0xa2, 0x77, 0xd8, 0x7a, 0x31, 0x5e, 0x76, 0x8d, 0xa4, 0xe5, 0x02, + 0x4f, 0x57, 0x57, 0x9c, 0x69, 0x7b, 0xc3, 0x2f, 0x8e, 0x2a, 0xe0, 0xff, 0xc4, 0x41, 0x09, 0xf9, + 0x5f, 0x25, 0x98, 0xe6, 0x2b, 0xd4, 0xea, 0x59, 0xaa, 0xdd, 0x3f, 0xb7, 0xc4, 0xf4, 0x34, 0x94, + 0x98, 0xee, 0x0c, 0x58, 0x96, 0x98, 0x85, 0xa9, 0xc9, 0xe9, 0x1b, 0x09, 0x2e, 0xc6, 0xb8, 0xcf, + 0x21, 0x2e, 0x6e, 0x87, 0xe3, 0xe2, 0x9b, 0xa7, 0x9d, 0x50, 0x4a, 0x6c, 0xfc, 0xef, 0xe9, 0x84, + 0xe9, 0xf0, 0x93, 0x72, 0x1b, 0xc0, 0xb4, 0xd4, 0x03, 0x55, 0x23, 0x1d, 0xf1, 0x19, 0xbd, 0x14, + 0x68, 0x92, 0xf2, 0x28, 0x38, 0xc0, 0x85, 0x28, 0xcc, 0xb5, 0xc9, 0xae, 0xd2, 0xd3, 0xec, 0xc5, + 0x76, 0x7b, 0x49, 0x31, 0x95, 0x1d, 0x55, 0x53, 0x6d, 0x55, 0x3c, 0x38, 0x8c, 0xd4, 0x1f, 0x3a, + 0x9f, 0xb7, 0x93, 0x38, 0x5e, 0x1c, 0x55, 0xae, 0x24, 0x7d, 0x5f, 0x72, 0x59, 0xfa, 0x38, 0x05, + 0x1a, 0xf5, 0xa1, 0x6c, 0x91, 0x4f, 0x7b, 0xaa, 0x45, 0xda, 0xcb, 0x96, 0x61, 0x86, 0xd4, 0xe6, + 0xb9, 0xda, 0x5f, 0x3f, 0x3e, 0xaa, 0x94, 0x71, 0x0a, 0xcf, 0x60, 0xc5, 0xa9, 0xf0, 0xe8, 0x19, + 0xcc, 0x28, 0xa2, 0x9d, 0x2d, 0xa8, 0xd5, 0x39, 0x25, 0xf7, 0x8f, 0x8f, 0x2a, 0x33, 0x8b, 0x71, + 0xf2, 0x60, 0x85, 0x49, 0xa0, 0xa8, 0x06, 0xc5, 0x03, 0xde, 0xf9, 0x46, 0xcb, 0x43, 0x1c, 0x9f, + 0x25, 0x82, 0xa2, 0xd3, 0x0c, 0xc7, 0x30, 0x87, 0x57, 0x9a, 0xfc, 0xf4, 0xb9, 0x5c, 0xec, 0x4a, + 0xca, 0x6a, 0x49, 0x71, 0xe2, 0xf9, 0x9b, 0x73, 0xc9, 0x8f, 0x5a, 0x4f, 0x7c, 0x12, 0x0e, 0xf2, + 0xa1, 0x8f, 0x60, 0x64, 0x4f, 0xbc, 0x6b, 0xd0, 0x72, 0x31, 0x53, 0x12, 0x0e, 0xbd, 0x83, 0xd4, + 0xa7, 0x85, 0x8a, 0x11, 0x77, 0x98, 0x62, 0x1f, 0x11, 0xbd, 0x0e, 0x45, 0xfe, 0x63, 0x75, 0x99, + 0x3f, 0xe8, 0x95, 0xfc, 0xd8, 0xf6, 0xc4, 0x19, 0xc6, 0x2e, 0xdd, 0x65, 0x5d, 0x6d, 0x2c, 0xf1, + 0x87, 0xe5, 0x08, 0xeb, 0x6a, 0x63, 0x09, 0xbb, 0x74, 0xf4, 0x09, 0x14, 0x29, 0x59, 0x53, 0xf5, + 0xde, 0x61, 0x19, 0x32, 0x7d, 0x96, 0x6e, 0x3e, 0xe2, 0xdc, 0x91, 0xa7, 0x35, 0x5f, 0x83, 0xa0, + 0x63, 0x17, 0x16, 0xed, 0xc1, 0x88, 0xd5, 0xd3, 0x17, 0xe9, 0x36, 0x25, 0x56, 0x79, 0x94, 0xeb, + 0x18, 0x14, 0xce, 0xb1, 0xcb, 0x1f, 0xd5, 0xe2, 0xad, 0x90, 0xc7, 0x81, 0x7d, 0x70, 0xb4, 0x07, + 0xc0, 0x7f, 0xf0, 0x57, 0xbc, 0xf2, 0x1c, 0x57, 0x75, 0x3f, 0x8b, 0xaa, 0xa4, 0xc7, 0x42, 0xf1, + 0x92, 0xef, 0x91, 0x71, 0x00, 0x1b, 0xfd, 0xa1, 0x04, 0x88, 0xf6, 0x4c, 0x53, 0x23, 0x5d, 0xa2, + 0xdb, 0x8a, 0xc6, 0x47, 0x69, 0x79, 0x8c, 0xab, 0x7c, 0x77, 0xd0, 0x0a, 0xc6, 0x04, 0xa3, 0xaa, + 0xbd, 0x07, 0xfa, 0x38, 0x2b, 0x4e, 0xd0, 0xcb, 0x36, 0x71, 0x57, 0xcc, 0x7a, 0x3c, 0xd3, 0x26, + 0x26, 0xbf, 0x8f, 0xfa, 0x9b, 0x28, 0xe8, 0xd8, 0x85, 0x45, 0x4f, 0x61, 0xce, 0x6d, 0xd1, 0xc4, + 0x86, 0x61, 0xaf, 0xa8, 0x1a, 0xa1, 0x7d, 0x6a, 0x93, 0x6e, 0x79, 0x82, 0x3b, 0x98, 0xd7, 0xa7, + 0x82, 0x13, 0xb9, 0x70, 0x8a, 0x34, 0xea, 0x42, 0xc5, 0x0d, 0x4e, 0xec, 0xe4, 0x7a, 0xd1, 0xf1, + 0x11, 0x6d, 0x29, 0x9a, 0xf3, 0xcd, 0x62, 0x92, 0x2b, 0x78, 0xed, 0xf8, 0xa8, 0x52, 0x59, 0x3e, + 0x99, 0x15, 0x0f, 0xc2, 0x42, 0x1f, 0x40, 0x59, 0x49, 0xd3, 0x33, 0xc5, 0xf5, 0xfc, 0x9c, 0x45, + 0xbc, 0x54, 0x05, 0xa9, 0xd2, 0xc8, 0x86, 0x29, 0x25, 0xdc, 0x2c, 0x4b, 0xcb, 0xd3, 0x99, 0x1e, + 0x4d, 0x23, 0x3d, 0xb6, 0xfe, 0xc3, 0x49, 0x84, 0x40, 0x71, 0x4c, 0x03, 0xfa, 0x1d, 0x40, 0x4a, + 0xb4, 0xbf, 0x97, 0x96, 0x51, 0xa6, 0x44, 0x17, 0x6b, 0x0c, 0xf6, 0xdd, 0x2e, 0x46, 0xa2, 0x38, + 0x41, 0x0f, 0x2b, 0xd0, 0x95, 0x48, 0x4f, 0x32, 0x2d, 0xcf, 0x73, 0xe5, 0xb5, 0x6c, 0xca, 0x3d, + 0xb9, 0xc0, 0xa7, 0x99, 0x28, 0x22, 0x8e, 0x2b, 0x41, 0x6b, 0x30, 0x2b, 0x06, 0xb7, 0x75, 0xaa, + 0xec, 0x92, 0x66, 0x9f, 0xb6, 0x6c, 0x8d, 0x96, 0x67, 0x78, 0x7c, 0xe7, 0x9f, 0x07, 0x17, 0x13, + 0xe8, 0x38, 0x51, 0x0a, 0xbd, 0x0b, 0x53, 0xbb, 0x86, 0xb5, 0xa3, 0xb6, 0xdb, 0x44, 0x77, 0x91, + 0x66, 0x39, 0x12, 0x7f, 0x07, 0x5a, 0x89, 0xd0, 0x70, 0x8c, 0x1b, 0x51, 0xb8, 0x28, 0x90, 0x1b, + 0x96, 0xd1, 0x5a, 0x37, 0x7a, 0xba, 0xed, 0x94, 0x7d, 0x17, 0xbd, 0x34, 0x7a, 0x71, 0x31, 0x89, + 0xe1, 0xc5, 0x51, 0xe5, 0x6a, 0x72, 0x95, 0xef, 0x33, 0xe1, 0x64, 0x6c, 0x64, 0xc2, 0x98, 0xe8, + 0x34, 0xe7, 0x0f, 0x52, 0xe5, 0x32, 0x3f, 0xfa, 0x0f, 0x06, 0x07, 0x3c, 0x4f, 0x24, 0x7a, 0xfe, + 0xa7, 0x8e, 0x8f, 0x2a, 0x63, 0x41, 0x06, 0x1c, 0xd2, 0xc0, 0x3b, 0x8b, 0xc4, 0xf7, 0xac, 0xf3, + 0xe9, 0xce, 0x3e, 0x5d, 0x67, 0x91, 0x6f, 0xda, 0x4b, 0xeb, 0x2c, 0x0a, 0x40, 0x9e, 0xfc, 0xb2, + 0xfd, 0x5f, 0x39, 0x98, 0xf1, 0x99, 0x33, 0x77, 0x16, 0x25, 0x88, 0xfc, 0x7f, 0x87, 0x76, 0xb6, + 0x6e, 0x1f, 0x7f, 0xe9, 0xfe, 0xef, 0x75, 0xfb, 0xf8, 0xb6, 0xa5, 0xdc, 0x1e, 0xfe, 0x36, 0x17, + 0x9c, 0xc0, 0x29, 0x5b, 0x4e, 0x5e, 0x42, 0x93, 0xf2, 0x4f, 0xae, 0x6b, 0x45, 0xfe, 0x26, 0x0f, + 0x53, 0xd1, 0xd3, 0x18, 0xea, 0x4c, 0x90, 0x06, 0x76, 0x26, 0x34, 0x60, 0x76, 0xb7, 0xa7, 0x69, + 0x7d, 0x3e, 0x87, 0x40, 0x7b, 0x82, 0xf3, 0x65, 0xf1, 0xe7, 0x42, 0x72, 0x76, 0x25, 0x81, 0x07, + 0x27, 0x4a, 0xc6, 0x1b, 0x15, 0x0a, 0x3f, 0xb6, 0x51, 0x61, 0xe8, 0x0c, 0x8d, 0x0a, 0xc9, 0xbd, + 0x1e, 0xf9, 0x33, 0xf5, 0x7a, 0x9c, 0xa5, 0x4b, 0x21, 0x21, 0x88, 0x0d, 0xec, 0xb8, 0x7d, 0x1b, + 0x26, 0xc2, 0x9d, 0x33, 0xce, 0x5e, 0x3a, 0xcd, 0x3b, 0xe2, 0x0b, 0x6e, 0x60, 0x2f, 0x9d, 0x71, + 0xec, 0x71, 0xc8, 0xbf, 0x2f, 0xc1, 0x5c, 0x72, 0x87, 0x2c, 0xd2, 0x60, 0xa2, 0xab, 0x1c, 0x06, + 0xbb, 0x96, 0xa5, 0x33, 0xbe, 0x8c, 0xf1, 0x96, 0x89, 0xf5, 0x10, 0x16, 0x8e, 0x60, 0xcb, 0x3f, + 0x48, 0x30, 0x9f, 0xd2, 0xac, 0x70, 0xbe, 0x96, 0xa0, 0x0f, 0xa1, 0xd4, 0x55, 0x0e, 0x9b, 0x3d, + 0xab, 0x43, 0xce, 0xfc, 0x16, 0xc8, 0x0f, 0xf4, 0xba, 0x40, 0xc1, 0x1e, 0x9e, 0xfc, 0x97, 0x12, + 0xfc, 0x2c, 0xf5, 0xaa, 0x84, 0xee, 0x85, 0xfa, 0x2a, 0xe4, 0x48, 0x5f, 0x05, 0x8a, 0x0b, 0xbe, + 0xa2, 0xb6, 0x8a, 0x2f, 0x24, 0x28, 0xa7, 0xdd, 0x1d, 0xd1, 0xdd, 0x90, 0x91, 0xbf, 0x88, 0x18, + 0x39, 0x1d, 0x93, 0x7b, 0x45, 0x36, 0xfe, 0x9b, 0x04, 0x97, 0x4f, 0xa8, 0xc1, 0xbc, 0x2b, 0x0a, + 0x69, 0x07, 0xb9, 0xf8, 0xb3, 0xb5, 0xf8, 0xe6, 0xe5, 0x5f, 0x51, 0x12, 0x78, 0x70, 0xaa, 0x34, + 0xda, 0x86, 0x79, 0x71, 0x3f, 0x8a, 0xd2, 0x44, 0x79, 0xc1, 0xdb, 0xcf, 0x96, 0x93, 0x59, 0x70, + 0x9a, 0xac, 0xfc, 0x37, 0x12, 0xcc, 0x25, 0x3f, 0x0a, 0xa0, 0xb7, 0x42, 0x4b, 0x5e, 0x89, 0x2c, + 0xf9, 0x64, 0x44, 0x4a, 0x2c, 0xf8, 0xc7, 0x30, 0x21, 0x9e, 0x0e, 0x04, 0x8c, 0x70, 0x66, 0x39, + 0x29, 0x83, 0x08, 0x08, 0xb7, 0x80, 0xe5, 0xc7, 0x24, 0x3c, 0x86, 0x23, 0x68, 0xf2, 0x1f, 0xe4, + 0x60, 0xa8, 0xd9, 0x52, 0x34, 0x72, 0x0e, 0xf5, 0xeb, 0x7b, 0xa1, 0xfa, 0x75, 0xd0, 0x3f, 0x76, + 0x71, 0xab, 0x52, 0x4b, 0x57, 0x1c, 0x29, 0x5d, 0xdf, 0xc8, 0x84, 0x76, 0x72, 0xd5, 0xfa, 0x6b, + 0x30, 0xe2, 0x29, 0x3d, 0x5d, 0x32, 0x95, 0xff, 0x22, 0x07, 0xa3, 0x01, 0x15, 0xa7, 0x4c, 0xc5, + 0xbb, 0xa1, 0xfa, 0x23, 0x9f, 0xe1, 0xa1, 0x26, 0xa0, 0xab, 0xea, 0x56, 0x1c, 0x4e, 0x63, 0xb2, + 0xdf, 0x8a, 0x1a, 0x2f, 0x44, 0xde, 0x86, 0x09, 0x5b, 0xb1, 0x3a, 0xc4, 0xf6, 0x3e, 0x5c, 0xe4, + 0xb9, 0x2f, 0x7a, 0x1d, 0xf2, 0x5b, 0x21, 0x2a, 0x8e, 0x70, 0x5f, 0x7a, 0x08, 0xe3, 0x21, 0x65, + 0xa7, 0xea, 0x2b, 0xfe, 0x7b, 0x09, 0x7e, 0x31, 0xf0, 0xb1, 0x07, 0xd5, 0x43, 0x87, 0xa4, 0x1a, + 0x39, 0x24, 0x0b, 0xe9, 0x00, 0xaf, 0xae, 0x3f, 0xad, 0x7e, 0xf3, 0xf9, 0xf7, 0x0b, 0x17, 0xbe, + 0xfd, 0x7e, 0xe1, 0xc2, 0x77, 0xdf, 0x2f, 0x5c, 0xf8, 0xdd, 0xe3, 0x05, 0xe9, 0xf9, 0xf1, 0x82, + 0xf4, 0xed, 0xf1, 0x82, 0xf4, 0xdd, 0xf1, 0x82, 0xf4, 0x1f, 0xc7, 0x0b, 0xd2, 0x9f, 0xfc, 0xb0, + 0x70, 0xe1, 0xc3, 0xa2, 0x80, 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x27, 0x4b, 0x93, + 0x2b, 0x3e, 0x00, 0x00, } func (m *AllowedCSIDriver) Marshal() (dAtA []byte, err error) { @@ -3194,6 +3195,13 @@ func (m *IngressSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.IngressClassName != nil { + i -= len(*m.IngressClassName) + copy(dAtA[i:], *m.IngressClassName) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.IngressClassName))) + i-- + dAtA[i] = 0x22 + } if len(m.Rules) > 0 { for iNdEx := len(m.Rules) - 1; iNdEx >= 0; iNdEx-- { { @@ -5230,6 +5238,10 @@ func (m *IngressSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if m.IngressClassName != nil { + l = len(*m.IngressClassName) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -6190,6 +6202,7 @@ func (this *IngressSpec) String() string { `Backend:` + strings.Replace(this.Backend.String(), "IngressBackend", "IngressBackend", 1) + `,`, `TLS:` + repeatedStringForTLS + `,`, `Rules:` + repeatedStringForRules + `,`, + `IngressClassName:` + valueToStringGenerated(this.IngressClassName) + `,`, `}`, }, "") return s @@ -10741,6 +10754,39 @@ func (m *IngressSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IngressClassName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.IngressClassName = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/extensions/v1beta1/generated.proto b/staging/src/k8s.io/api/extensions/v1beta1/generated.proto index 56b08a11028..a92cc5668f1 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/extensions/v1beta1/generated.proto @@ -554,6 +554,19 @@ message IngressRuleValue { // IngressSpec describes the Ingress the user wishes to exist. message IngressSpec { + // IngressClassName is the name of the IngressClass cluster resource. The + // associated IngressClass defines which controller will implement the + // resource. This replaces the deprecated `kubernetes.io/ingress.class` + // annotation. For backwards compatibility, when that annotation is set, it + // must be given precedence over this field. The controller may emit a + // warning if the field and annotation have different values. + // Implementations of this API should ignore Ingresses without a class + // specified. An IngressClass resource may be marked as default, which can + // be used to set a default value for this field. For more information, + // refer to the IngressClass documentation. + // +optional + optional string ingressClassName = 4; + // A default backend capable of servicing requests that don't match any // rule. At least one of 'backend' or 'rules' must be specified. This field // is optional to allow the loadbalancer controller or defaulting logic to diff --git a/staging/src/k8s.io/api/extensions/v1beta1/types.go b/staging/src/k8s.io/api/extensions/v1beta1/types.go index effd4b84478..3f1cb4fa6d0 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/types.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/types.go @@ -569,6 +569,19 @@ type IngressList struct { // IngressSpec describes the Ingress the user wishes to exist. type IngressSpec struct { + // IngressClassName is the name of the IngressClass cluster resource. The + // associated IngressClass defines which controller will implement the + // resource. This replaces the deprecated `kubernetes.io/ingress.class` + // annotation. For backwards compatibility, when that annotation is set, it + // must be given precedence over this field. The controller may emit a + // warning if the field and annotation have different values. + // Implementations of this API should ignore Ingresses without a class + // specified. An IngressClass resource may be marked as default, which can + // be used to set a default value for this field. For more information, + // refer to the IngressClass documentation. + // +optional + IngressClassName *string `json:"ingressClassName,omitempty" protobuf:"bytes,4,opt,name=ingressClassName"` + // A default backend capable of servicing requests that don't match any // rule. At least one of 'backend' or 'rules' must be specified. This field // is optional to allow the loadbalancer controller or defaulting logic to diff --git a/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go index cde7884d7b3..5e8f5d160a6 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go @@ -327,10 +327,11 @@ func (IngressRuleValue) SwaggerDoc() map[string]string { } var map_IngressSpec = map[string]string{ - "": "IngressSpec describes the Ingress the user wishes to exist.", - "backend": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default.", - "tls": "TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", - "rules": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", + "": "IngressSpec describes the Ingress the user wishes to exist.", + "ingressClassName": "IngressClassName is the name of the IngressClass cluster resource. The associated IngressClass defines which controller will implement the resource. This replaces the deprecated `kubernetes.io/ingress.class` annotation. For backwards compatibility, when that annotation is set, it must be given precedence over this field. The controller may emit a warning if the field and annotation have different values. Implementations of this API should ignore Ingresses without a class specified. An IngressClass resource may be marked as default, which can be used to set a default value for this field. For more information, refer to the IngressClass documentation.", + "backend": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default.", + "tls": "TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", + "rules": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", } func (IngressSpec) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go index 0e8b6a65bb0..5750d707cdd 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go @@ -665,6 +665,11 @@ func (in *IngressRuleValue) DeepCopy() *IngressRuleValue { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressSpec) DeepCopyInto(out *IngressSpec) { *out = *in + if in.IngressClassName != nil { + in, out := &in.IngressClassName, &out.IngressClassName + *out = new(string) + **out = **in + } if in.Backend != nil { in, out := &in.Backend, &out.Backend *out = new(IngressBackend) diff --git a/staging/src/k8s.io/api/networking/v1beta1/BUILD b/staging/src/k8s.io/api/networking/v1beta1/BUILD index bdfc736dc1d..b2bf5091fb3 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/BUILD +++ b/staging/src/k8s.io/api/networking/v1beta1/BUILD @@ -13,6 +13,7 @@ go_library( "register.go", "types.go", "types_swagger_doc_generated.go", + "well_known_annotations.go", "zz_generated.deepcopy.go", ], importmap = "k8s.io/kubernetes/vendor/k8s.io/api/networking/v1beta1", diff --git a/staging/src/k8s.io/api/networking/v1beta1/generated.pb.go b/staging/src/k8s.io/api/networking/v1beta1/generated.pb.go index c4f0b7fa409..86e3eeb3792 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/networking/v1beta1/generated.pb.go @@ -25,6 +25,7 @@ import ( io "io" proto "github.com/gogo/protobuf/proto" + v11 "k8s.io/api/core/v1" math "math" math_bits "math/bits" @@ -155,10 +156,94 @@ func (m *IngressBackend) XXX_DiscardUnknown() { var xxx_messageInfo_IngressBackend proto.InternalMessageInfo +func (m *IngressClass) Reset() { *m = IngressClass{} } +func (*IngressClass) ProtoMessage() {} +func (*IngressClass) Descriptor() ([]byte, []int) { + return fileDescriptor_5bea11de0ceb8f53, []int{4} +} +func (m *IngressClass) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IngressClass) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *IngressClass) XXX_Merge(src proto.Message) { + xxx_messageInfo_IngressClass.Merge(m, src) +} +func (m *IngressClass) XXX_Size() int { + return m.Size() +} +func (m *IngressClass) XXX_DiscardUnknown() { + xxx_messageInfo_IngressClass.DiscardUnknown(m) +} + +var xxx_messageInfo_IngressClass proto.InternalMessageInfo + +func (m *IngressClassList) Reset() { *m = IngressClassList{} } +func (*IngressClassList) ProtoMessage() {} +func (*IngressClassList) Descriptor() ([]byte, []int) { + return fileDescriptor_5bea11de0ceb8f53, []int{5} +} +func (m *IngressClassList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IngressClassList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *IngressClassList) XXX_Merge(src proto.Message) { + xxx_messageInfo_IngressClassList.Merge(m, src) +} +func (m *IngressClassList) XXX_Size() int { + return m.Size() +} +func (m *IngressClassList) XXX_DiscardUnknown() { + xxx_messageInfo_IngressClassList.DiscardUnknown(m) +} + +var xxx_messageInfo_IngressClassList proto.InternalMessageInfo + +func (m *IngressClassSpec) Reset() { *m = IngressClassSpec{} } +func (*IngressClassSpec) ProtoMessage() {} +func (*IngressClassSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_5bea11de0ceb8f53, []int{6} +} +func (m *IngressClassSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IngressClassSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *IngressClassSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_IngressClassSpec.Merge(m, src) +} +func (m *IngressClassSpec) XXX_Size() int { + return m.Size() +} +func (m *IngressClassSpec) XXX_DiscardUnknown() { + xxx_messageInfo_IngressClassSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_IngressClassSpec proto.InternalMessageInfo + func (m *IngressList) Reset() { *m = IngressList{} } func (*IngressList) ProtoMessage() {} func (*IngressList) Descriptor() ([]byte, []int) { - return fileDescriptor_5bea11de0ceb8f53, []int{4} + return fileDescriptor_5bea11de0ceb8f53, []int{7} } func (m *IngressList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -186,7 +271,7 @@ var xxx_messageInfo_IngressList proto.InternalMessageInfo func (m *IngressRule) Reset() { *m = IngressRule{} } func (*IngressRule) ProtoMessage() {} func (*IngressRule) Descriptor() ([]byte, []int) { - return fileDescriptor_5bea11de0ceb8f53, []int{5} + return fileDescriptor_5bea11de0ceb8f53, []int{8} } func (m *IngressRule) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -214,7 +299,7 @@ var xxx_messageInfo_IngressRule proto.InternalMessageInfo func (m *IngressRuleValue) Reset() { *m = IngressRuleValue{} } func (*IngressRuleValue) ProtoMessage() {} func (*IngressRuleValue) Descriptor() ([]byte, []int) { - return fileDescriptor_5bea11de0ceb8f53, []int{6} + return fileDescriptor_5bea11de0ceb8f53, []int{9} } func (m *IngressRuleValue) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -242,7 +327,7 @@ var xxx_messageInfo_IngressRuleValue proto.InternalMessageInfo func (m *IngressSpec) Reset() { *m = IngressSpec{} } func (*IngressSpec) ProtoMessage() {} func (*IngressSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_5bea11de0ceb8f53, []int{7} + return fileDescriptor_5bea11de0ceb8f53, []int{10} } func (m *IngressSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -270,7 +355,7 @@ var xxx_messageInfo_IngressSpec proto.InternalMessageInfo func (m *IngressStatus) Reset() { *m = IngressStatus{} } func (*IngressStatus) ProtoMessage() {} func (*IngressStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_5bea11de0ceb8f53, []int{8} + return fileDescriptor_5bea11de0ceb8f53, []int{11} } func (m *IngressStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -298,7 +383,7 @@ var xxx_messageInfo_IngressStatus proto.InternalMessageInfo func (m *IngressTLS) Reset() { *m = IngressTLS{} } func (*IngressTLS) ProtoMessage() {} func (*IngressTLS) Descriptor() ([]byte, []int) { - return fileDescriptor_5bea11de0ceb8f53, []int{9} + return fileDescriptor_5bea11de0ceb8f53, []int{12} } func (m *IngressTLS) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -328,6 +413,9 @@ func init() { proto.RegisterType((*HTTPIngressRuleValue)(nil), "k8s.io.api.networking.v1beta1.HTTPIngressRuleValue") proto.RegisterType((*Ingress)(nil), "k8s.io.api.networking.v1beta1.Ingress") proto.RegisterType((*IngressBackend)(nil), "k8s.io.api.networking.v1beta1.IngressBackend") + proto.RegisterType((*IngressClass)(nil), "k8s.io.api.networking.v1beta1.IngressClass") + proto.RegisterType((*IngressClassList)(nil), "k8s.io.api.networking.v1beta1.IngressClassList") + proto.RegisterType((*IngressClassSpec)(nil), "k8s.io.api.networking.v1beta1.IngressClassSpec") proto.RegisterType((*IngressList)(nil), "k8s.io.api.networking.v1beta1.IngressList") proto.RegisterType((*IngressRule)(nil), "k8s.io.api.networking.v1beta1.IngressRule") proto.RegisterType((*IngressRuleValue)(nil), "k8s.io.api.networking.v1beta1.IngressRuleValue") @@ -341,58 +429,66 @@ func init() { } var fileDescriptor_5bea11de0ceb8f53 = []byte{ - // 812 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcf, 0x6e, 0xfb, 0x44, - 0x10, 0x8e, 0xf3, 0xa7, 0x69, 0xd7, 0xfd, 0xa7, 0xa5, 0x87, 0xa8, 0x12, 0x6e, 0xe4, 0x03, 0x2a, - 0x88, 0xae, 0x69, 0x0a, 0x88, 0xb3, 0x0f, 0xa8, 0x15, 0x81, 0x86, 0x75, 0x84, 0x10, 0xe2, 0xd0, - 0x8d, 0xb3, 0x38, 0x26, 0x89, 0x6d, 0x76, 0xd7, 0x41, 0xdc, 0x78, 0x01, 0x04, 0x4f, 0xc1, 0x99, - 0x23, 0x8f, 0xd0, 0x63, 0x8f, 0x3d, 0x55, 0x34, 0xbc, 0x07, 0x42, 0xbb, 0xde, 0xda, 0x4e, 0xd2, - 0xfe, 0x6a, 0xfd, 0x6e, 0xde, 0x9d, 0xf9, 0xbe, 0xd9, 0x99, 0xf9, 0x66, 0x0c, 0x3e, 0x9f, 0x7e, - 0xc6, 0x51, 0x18, 0x3b, 0xd3, 0x74, 0x44, 0x59, 0x44, 0x05, 0xe5, 0xce, 0x82, 0x46, 0xe3, 0x98, - 0x39, 0xda, 0x40, 0x92, 0xd0, 0x89, 0xa8, 0xf8, 0x39, 0x66, 0xd3, 0x30, 0x0a, 0x9c, 0xc5, 0xf9, - 0x88, 0x0a, 0x72, 0xee, 0x04, 0x34, 0xa2, 0x8c, 0x08, 0x3a, 0x46, 0x09, 0x8b, 0x45, 0x0c, 0xdf, - 0xcd, 0xdc, 0x11, 0x49, 0x42, 0x54, 0xb8, 0x23, 0xed, 0x7e, 0x7c, 0x16, 0x84, 0x62, 0x92, 0x8e, - 0x90, 0x1f, 0xcf, 0x9d, 0x20, 0x0e, 0x62, 0x47, 0xa1, 0x46, 0xe9, 0x0f, 0xea, 0xa4, 0x0e, 0xea, - 0x2b, 0x63, 0x3b, 0xb6, 0x4b, 0xc1, 0xfd, 0x98, 0x51, 0x67, 0xb1, 0x11, 0xf1, 0xf8, 0xe3, 0xc2, - 0x67, 0x4e, 0xfc, 0x49, 0x18, 0x51, 0xf6, 0x8b, 0x93, 0x4c, 0x03, 0x79, 0xc1, 0x9d, 0x39, 0x15, - 0xe4, 0x39, 0x94, 0xf3, 0x12, 0x8a, 0xa5, 0x91, 0x08, 0xe7, 0x74, 0x03, 0xf0, 0xe9, 0x6b, 0x00, - 0xee, 0x4f, 0xe8, 0x9c, 0x6c, 0xe0, 0x2e, 0x5e, 0xc2, 0xa5, 0x22, 0x9c, 0x39, 0x61, 0x24, 0xb8, - 0x60, 0xeb, 0x20, 0xfb, 0x37, 0x03, 0x1c, 0x5c, 0x0e, 0x87, 0x83, 0xab, 0x28, 0x60, 0x94, 0xf3, - 0x01, 0x11, 0x13, 0xd8, 0x05, 0xcd, 0x84, 0x88, 0x49, 0xc7, 0xe8, 0x1a, 0xa7, 0x3b, 0xee, 0xee, - 0xed, 0xc3, 0x49, 0x6d, 0xf9, 0x70, 0xd2, 0x94, 0x36, 0xac, 0x2c, 0xf0, 0x5b, 0xd0, 0x1e, 0x11, - 0x7f, 0x4a, 0xa3, 0x71, 0xa7, 0xde, 0x35, 0x4e, 0xcd, 0xde, 0x19, 0x7a, 0x63, 0x37, 0x90, 0xa6, - 0x77, 0x33, 0x90, 0x7b, 0xa0, 0x39, 0xdb, 0xfa, 0x02, 0x3f, 0xd1, 0xd9, 0x53, 0x70, 0x54, 0x7a, - 0x0e, 0x4e, 0x67, 0xf4, 0x1b, 0x32, 0x4b, 0x29, 0xf4, 0x40, 0x4b, 0x46, 0xe6, 0x1d, 0xa3, 0xdb, - 0x38, 0x35, 0x7b, 0xe8, 0x95, 0x78, 0x6b, 0x29, 0xb9, 0x7b, 0x3a, 0x60, 0x4b, 0x9e, 0x38, 0xce, - 0xb8, 0xec, 0xdf, 0xeb, 0xa0, 0xad, 0xbd, 0xe0, 0x0d, 0xd8, 0x96, 0x1d, 0x1c, 0x13, 0x41, 0x54, - 0xe2, 0x66, 0xef, 0xa3, 0x52, 0x8c, 0xbc, 0xa0, 0x28, 0x99, 0x06, 0xf2, 0x82, 0x23, 0xe9, 0x8d, - 0x16, 0xe7, 0xe8, 0x7a, 0xf4, 0x23, 0xf5, 0xc5, 0x97, 0x54, 0x10, 0x17, 0xea, 0x28, 0xa0, 0xb8, - 0xc3, 0x39, 0x2b, 0xec, 0x83, 0x26, 0x4f, 0xa8, 0xaf, 0x2b, 0xf6, 0x41, 0xb5, 0x8a, 0x79, 0x09, - 0xf5, 0x8b, 0x16, 0xc8, 0x13, 0x56, 0x2c, 0x70, 0x08, 0xb6, 0xb8, 0x20, 0x22, 0xe5, 0x9d, 0x86, - 0xe2, 0xfb, 0xb0, 0x22, 0x9f, 0xc2, 0xb8, 0xfb, 0x9a, 0x71, 0x2b, 0x3b, 0x63, 0xcd, 0x65, 0xff, - 0x65, 0x80, 0xfd, 0xd5, 0x5e, 0xc1, 0x4f, 0x80, 0xc9, 0x29, 0x5b, 0x84, 0x3e, 0xfd, 0x8a, 0xcc, - 0xa9, 0x16, 0xc5, 0x3b, 0x1a, 0x6f, 0x7a, 0x85, 0x09, 0x97, 0xfd, 0x60, 0x90, 0xc3, 0x06, 0x31, - 0x13, 0x3a, 0xe9, 0x97, 0x4b, 0x2a, 0x35, 0x8a, 0x32, 0x8d, 0xa2, 0xab, 0x48, 0x5c, 0x33, 0x4f, - 0xb0, 0x30, 0x0a, 0x36, 0x02, 0x49, 0x32, 0x5c, 0x66, 0xb6, 0xff, 0x36, 0x80, 0xa9, 0x9f, 0xdc, - 0x0f, 0xb9, 0x80, 0xdf, 0x6f, 0x34, 0x12, 0x55, 0x6b, 0xa4, 0x44, 0xab, 0x36, 0x1e, 0xea, 0x98, - 0xdb, 0x4f, 0x37, 0xa5, 0x26, 0x7e, 0x01, 0x5a, 0xa1, 0xa0, 0x73, 0xde, 0xa9, 0x2b, 0x1d, 0xbe, - 0x57, 0x51, 0xf7, 0xb9, 0xfe, 0xae, 0x24, 0x18, 0x67, 0x1c, 0xf6, 0x9f, 0xc5, 0xd3, 0xa5, 0xd2, - 0xe5, 0xe0, 0x4d, 0x62, 0x2e, 0xd6, 0x07, 0xef, 0x32, 0xe6, 0x02, 0x2b, 0x0b, 0x4c, 0xc1, 0x61, - 0xb8, 0x36, 0x1a, 0xba, 0xb4, 0x4e, 0xb5, 0x97, 0xe4, 0x30, 0xb7, 0xa3, 0xe9, 0x0f, 0xd7, 0x2d, - 0x78, 0x23, 0x84, 0x4d, 0xc1, 0x86, 0x17, 0xfc, 0x1a, 0x34, 0x27, 0x42, 0x24, 0xba, 0xc6, 0x17, - 0xd5, 0x07, 0xb2, 0x78, 0xc2, 0xb6, 0xca, 0x6e, 0x38, 0x1c, 0x60, 0x45, 0x65, 0xff, 0x57, 0xd4, - 0xc3, 0xcb, 0x34, 0x9e, 0xaf, 0x19, 0xe3, 0x6d, 0xd6, 0x8c, 0xf9, 0xdc, 0x8a, 0x81, 0x97, 0xa0, - 0x21, 0x66, 0x4f, 0x0d, 0x7c, 0xbf, 0x1a, 0xe3, 0xb0, 0xef, 0xb9, 0xa6, 0x2e, 0x58, 0x63, 0xd8, - 0xf7, 0xb0, 0xa4, 0x80, 0xd7, 0xa0, 0xc5, 0xd2, 0x19, 0x95, 0x23, 0xd8, 0xa8, 0x3e, 0xd2, 0x32, - 0xff, 0x42, 0x10, 0xf2, 0xc4, 0x71, 0xc6, 0x63, 0xff, 0x04, 0xf6, 0x56, 0xe6, 0x14, 0xde, 0x80, - 0xdd, 0x59, 0x4c, 0xc6, 0x2e, 0x99, 0x91, 0xc8, 0xa7, 0x4c, 0x97, 0x61, 0x45, 0x75, 0xf2, 0x6f, - 0xa5, 0xe4, 0x5b, 0xf2, 0xd3, 0x53, 0x7e, 0xa4, 0x83, 0xec, 0x96, 0x6d, 0x78, 0x85, 0xd1, 0x26, - 0x00, 0x14, 0x39, 0xc2, 0x13, 0xd0, 0x92, 0x3a, 0xcb, 0xd6, 0xec, 0x8e, 0xbb, 0x23, 0x5f, 0x28, - 0xe5, 0xc7, 0x71, 0x76, 0x0f, 0x7b, 0x00, 0x70, 0xea, 0x33, 0x2a, 0xd4, 0x32, 0xa8, 0x2b, 0xa1, - 0xe6, 0x6b, 0xcf, 0xcb, 0x2d, 0xb8, 0xe4, 0xe5, 0x9e, 0xdd, 0x3e, 0x5a, 0xb5, 0xbb, 0x47, 0xab, - 0x76, 0xff, 0x68, 0xd5, 0x7e, 0x5d, 0x5a, 0xc6, 0xed, 0xd2, 0x32, 0xee, 0x96, 0x96, 0x71, 0xbf, - 0xb4, 0x8c, 0x7f, 0x96, 0x96, 0xf1, 0xc7, 0xbf, 0x56, 0xed, 0xbb, 0xb6, 0x2e, 0xd3, 0xff, 0x01, - 0x00, 0x00, 0xff, 0xff, 0xdb, 0x8a, 0xe4, 0xd8, 0x21, 0x08, 0x00, 0x00, + // 941 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0x8f, 0x93, 0x66, 0xdb, 0x4e, 0xba, 0xdd, 0x6a, 0xe8, 0x21, 0xaa, 0x84, 0x5b, 0xf9, 0x80, + 0xca, 0x9f, 0xda, 0xb4, 0x0b, 0x88, 0x23, 0xf2, 0x4a, 0xa8, 0x15, 0x81, 0x86, 0x89, 0x85, 0x10, + 0x02, 0x69, 0x27, 0xce, 0x5b, 0xc7, 0xc4, 0xb1, 0xcd, 0xcc, 0x38, 0x68, 0x6f, 0x7c, 0x01, 0x04, + 0x5f, 0x02, 0xce, 0x1c, 0x39, 0x22, 0xb8, 0xf4, 0xb8, 0xc7, 0x3d, 0xad, 0x68, 0xf8, 0x22, 0x68, + 0xc6, 0x53, 0xdb, 0x49, 0x1a, 0xd6, 0xcb, 0xa1, 0x37, 0xcf, 0xbc, 0xf7, 0x7e, 0xef, 0xfd, 0x7e, + 0xef, 0xf9, 0x0d, 0xfa, 0x78, 0xf2, 0x21, 0xb7, 0xc3, 0xc4, 0x99, 0x64, 0x43, 0x60, 0x31, 0x08, + 0xe0, 0xce, 0x0c, 0xe2, 0x51, 0xc2, 0x1c, 0x6d, 0xa0, 0x69, 0xe8, 0xc4, 0x20, 0xbe, 0x4f, 0xd8, + 0x24, 0x8c, 0x03, 0x67, 0x76, 0x3a, 0x04, 0x41, 0x4f, 0x9d, 0x00, 0x62, 0x60, 0x54, 0xc0, 0xc8, + 0x4e, 0x59, 0x22, 0x12, 0xfc, 0x7a, 0xee, 0x6e, 0xd3, 0x34, 0xb4, 0x4b, 0x77, 0x5b, 0xbb, 0x1f, + 0x9c, 0x04, 0xa1, 0x18, 0x67, 0x43, 0xdb, 0x4f, 0xa6, 0x4e, 0x90, 0x04, 0x89, 0xa3, 0xa2, 0x86, + 0xd9, 0x13, 0x75, 0x52, 0x07, 0xf5, 0x95, 0xa3, 0x1d, 0x58, 0x95, 0xe4, 0x7e, 0xc2, 0xc0, 0x99, + 0xad, 0x64, 0x3c, 0x78, 0xaf, 0xf4, 0x99, 0x52, 0x7f, 0x1c, 0xc6, 0xc0, 0x9e, 0x3a, 0xe9, 0x24, + 0x90, 0x17, 0xdc, 0x99, 0x82, 0xa0, 0xb7, 0x45, 0x39, 0xeb, 0xa2, 0x58, 0x16, 0x8b, 0x70, 0x0a, + 0x2b, 0x01, 0x1f, 0xbc, 0x2c, 0x80, 0xfb, 0x63, 0x98, 0xd2, 0x95, 0xb8, 0x87, 0xeb, 0xe2, 0x32, + 0x11, 0x46, 0x4e, 0x18, 0x0b, 0x2e, 0xd8, 0x72, 0x90, 0xf5, 0xa3, 0x81, 0x1e, 0x9c, 0x7b, 0x5e, + 0xff, 0x22, 0x0e, 0x18, 0x70, 0xde, 0xa7, 0x62, 0x8c, 0x8f, 0xd0, 0x46, 0x4a, 0xc5, 0xb8, 0x6b, + 0x1c, 0x19, 0xc7, 0xdb, 0xee, 0xce, 0xd5, 0x8b, 0xc3, 0xc6, 0xfc, 0xc5, 0xe1, 0x86, 0xb4, 0x11, + 0x65, 0xc1, 0x5f, 0xa2, 0xcd, 0x21, 0xf5, 0x27, 0x10, 0x8f, 0xba, 0xcd, 0x23, 0xe3, 0xb8, 0x73, + 0x76, 0x62, 0xff, 0x67, 0x37, 0x6c, 0x0d, 0xef, 0xe6, 0x41, 0xee, 0x03, 0x8d, 0xb9, 0xa9, 0x2f, + 0xc8, 0x0d, 0x9c, 0x35, 0x41, 0xfb, 0x95, 0x72, 0x48, 0x16, 0xc1, 0x17, 0x34, 0xca, 0x00, 0x0f, + 0x50, 0x5b, 0x66, 0xe6, 0x5d, 0xe3, 0xa8, 0x75, 0xdc, 0x39, 0xb3, 0x5f, 0x92, 0x6f, 0x89, 0x92, + 0x7b, 0x5f, 0x27, 0x6c, 0xcb, 0x13, 0x27, 0x39, 0x96, 0xf5, 0x53, 0x13, 0x6d, 0x6a, 0x2f, 0xfc, + 0x18, 0x6d, 0xc9, 0x0e, 0x8e, 0xa8, 0xa0, 0x8a, 0x78, 0xe7, 0xec, 0xdd, 0x4a, 0x8e, 0x42, 0x50, + 0x3b, 0x9d, 0x04, 0xf2, 0x82, 0xdb, 0xd2, 0xdb, 0x9e, 0x9d, 0xda, 0x97, 0xc3, 0x6f, 0xc1, 0x17, + 0x9f, 0x82, 0xa0, 0x2e, 0xd6, 0x59, 0x50, 0x79, 0x47, 0x0a, 0x54, 0xdc, 0x43, 0x1b, 0x3c, 0x05, + 0x5f, 0x2b, 0xf6, 0x56, 0x3d, 0xc5, 0x06, 0x29, 0xf8, 0x65, 0x0b, 0xe4, 0x89, 0x28, 0x14, 0xec, + 0xa1, 0x7b, 0x5c, 0x50, 0x91, 0xf1, 0x6e, 0x4b, 0xe1, 0xbd, 0x53, 0x13, 0x4f, 0xc5, 0xb8, 0xbb, + 0x1a, 0xf1, 0x5e, 0x7e, 0x26, 0x1a, 0xcb, 0xfa, 0xcd, 0x40, 0xbb, 0x8b, 0xbd, 0xc2, 0xef, 0xa3, + 0x0e, 0x07, 0x36, 0x0b, 0x7d, 0xf8, 0x8c, 0x4e, 0x41, 0x0f, 0xc5, 0x6b, 0x3a, 0xbe, 0x33, 0x28, + 0x4d, 0xa4, 0xea, 0x87, 0x83, 0x22, 0xac, 0x9f, 0x30, 0xa1, 0x49, 0xaf, 0x97, 0x54, 0xce, 0xa8, + 0x9d, 0xcf, 0xa8, 0x7d, 0x11, 0x8b, 0x4b, 0x36, 0x10, 0x2c, 0x8c, 0x83, 0x95, 0x44, 0x12, 0x8c, + 0x54, 0x91, 0xad, 0xbf, 0x0c, 0xb4, 0xa3, 0x4b, 0x7e, 0x14, 0xd1, 0x3b, 0xe9, 0xe4, 0xe7, 0x0b, + 0x9d, 0x74, 0xea, 0x29, 0xaf, 0x8a, 0x5b, 0xd7, 0x4e, 0xeb, 0x4f, 0x03, 0xed, 0x55, 0x1d, 0x7b, + 0x21, 0x17, 0xf8, 0xeb, 0x15, 0x26, 0x76, 0x3d, 0x26, 0x32, 0x5a, 0xf1, 0xd8, 0xd3, 0xa9, 0xb6, + 0x6e, 0x6e, 0x2a, 0x2c, 0xfa, 0xa8, 0x1d, 0x0a, 0x98, 0xf2, 0x6e, 0x53, 0xfd, 0x52, 0x6f, 0xbf, + 0x02, 0x8d, 0xf2, 0x7f, 0xba, 0x90, 0x08, 0x24, 0x07, 0xb2, 0x7e, 0x59, 0x22, 0x21, 0xf9, 0xe1, + 0x33, 0x84, 0xfc, 0x24, 0x16, 0x2c, 0x89, 0x22, 0x60, 0x7a, 0x7c, 0x0a, 0x79, 0x1f, 0x15, 0x16, + 0x52, 0xf1, 0xc2, 0xdf, 0x20, 0x94, 0x52, 0x46, 0xa7, 0x20, 0x80, 0xf1, 0xdb, 0x56, 0x8c, 0x5c, + 0xd1, 0x92, 0xa8, 0xf7, 0x34, 0x85, 0x51, 0x2f, 0xf1, 0x69, 0x94, 0x37, 0x8a, 0xc0, 0x13, 0x60, + 0x10, 0xfb, 0xe0, 0xee, 0x4a, 0xf8, 0x7e, 0x01, 0x42, 0x2a, 0x80, 0xd6, 0xef, 0x06, 0xea, 0xe8, + 0x3a, 0xef, 0x40, 0xe7, 0x4f, 0x16, 0x75, 0x7e, 0xa3, 0xe6, 0xaa, 0xbc, 0x5d, 0xe2, 0x5f, 0xcb, + 0xd2, 0xe5, 0x72, 0x94, 0xbb, 0x7a, 0x9c, 0x70, 0xb1, 0xbc, 0xab, 0xcf, 0x13, 0x2e, 0x88, 0xb2, + 0xe0, 0x0c, 0xed, 0x85, 0x4b, 0xdb, 0xf4, 0xd5, 0x06, 0xb7, 0x08, 0x73, 0xbb, 0x1a, 0x7e, 0x6f, + 0xd9, 0x42, 0x56, 0x52, 0x58, 0x80, 0x56, 0xbc, 0xe4, 0x7f, 0x33, 0x16, 0x22, 0xd5, 0x1a, 0x3f, + 0xac, 0xbf, 0xc3, 0xcb, 0x12, 0xb6, 0x14, 0x3b, 0xcf, 0xeb, 0x13, 0x05, 0x65, 0xfd, 0xd1, 0x2c, + 0xf4, 0x50, 0xd3, 0xf6, 0x51, 0xc1, 0x56, 0x4d, 0xa0, 0x5a, 0x59, 0x1b, 0x4a, 0x9b, 0xfd, 0x4a, + 0xe1, 0x85, 0x8d, 0xac, 0x78, 0x63, 0xaf, 0x7c, 0xdb, 0x8c, 0xff, 0xf3, 0xb6, 0x75, 0x6e, 0x7b, + 0xd7, 0xf0, 0x39, 0x6a, 0x89, 0xe8, 0x66, 0x04, 0xde, 0xac, 0x87, 0xe8, 0xf5, 0x06, 0x6e, 0x47, + 0x4b, 0xde, 0xf2, 0x7a, 0x03, 0x22, 0x21, 0xf0, 0x25, 0x6a, 0xb3, 0x2c, 0x02, 0xb9, 0xf7, 0x5b, + 0xf5, 0xdf, 0x11, 0xa9, 0x60, 0x39, 0x52, 0xf2, 0xc4, 0x49, 0x8e, 0x63, 0x7d, 0x87, 0xee, 0x2f, + 0x3c, 0x0e, 0xf8, 0x31, 0xda, 0x89, 0x12, 0x3a, 0x72, 0x69, 0x44, 0x63, 0x5f, 0xff, 0xb3, 0x4b, + 0x73, 0x7b, 0xf3, 0xff, 0xf5, 0x2a, 0x7e, 0xfa, 0x69, 0xd9, 0xd7, 0x49, 0x76, 0xaa, 0x36, 0xb2, + 0x80, 0x68, 0x51, 0x84, 0x4a, 0x8e, 0xf8, 0x10, 0xb5, 0xe5, 0xa4, 0xe6, 0x6f, 0xfb, 0xb6, 0xbb, + 0x2d, 0x2b, 0x94, 0x03, 0xcc, 0x49, 0x7e, 0x2f, 0x57, 0x08, 0x07, 0x9f, 0x81, 0x50, 0xed, 0x6c, + 0x2e, 0xae, 0x90, 0x41, 0x61, 0x21, 0x15, 0x2f, 0xf7, 0xe4, 0xea, 0xda, 0x6c, 0x3c, 0xbb, 0x36, + 0x1b, 0xcf, 0xaf, 0xcd, 0xc6, 0x0f, 0x73, 0xd3, 0xb8, 0x9a, 0x9b, 0xc6, 0xb3, 0xb9, 0x69, 0x3c, + 0x9f, 0x9b, 0xc6, 0xdf, 0x73, 0xd3, 0xf8, 0xf9, 0x1f, 0xb3, 0xf1, 0xd5, 0xa6, 0x96, 0xe9, 0xdf, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x8a, 0x2a, 0x50, 0x9d, 0x96, 0x0a, 0x00, 0x00, } func (m *HTTPIngressPath) Marshal() (dAtA []byte, err error) { @@ -561,6 +657,136 @@ func (m *IngressBackend) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *IngressClass) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IngressClass) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IngressClass) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *IngressClassList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IngressClassList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IngressClassList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *IngressClassSpec) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IngressClassSpec) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IngressClassSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Parameters != nil { + { + size, err := m.Parameters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i -= len(m.Controller) + copy(dAtA[i:], m.Controller) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Controller))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *IngressList) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -701,6 +927,13 @@ func (m *IngressSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.IngressClassName != nil { + i -= len(*m.IngressClassName) + copy(dAtA[i:], *m.IngressClassName) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.IngressClassName))) + i-- + dAtA[i] = 0x22 + } if len(m.Rules) > 0 { for iNdEx := len(m.Rules) - 1; iNdEx >= 0; iNdEx-- { { @@ -881,6 +1114,51 @@ func (m *IngressBackend) Size() (n int) { return n } +func (m *IngressClass) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Spec.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *IngressClassList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *IngressClassSpec) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Controller) + n += 1 + l + sovGenerated(uint64(l)) + if m.Parameters != nil { + l = m.Parameters.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + func (m *IngressList) Size() (n int) { if m == nil { return 0 @@ -946,6 +1224,10 @@ func (m *IngressSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if m.IngressClassName != nil { + l = len(*m.IngressClassName) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -1032,6 +1314,44 @@ func (this *IngressBackend) String() string { }, "") return s } +func (this *IngressClass) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&IngressClass{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "IngressClassSpec", "IngressClassSpec", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *IngressClassList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]IngressClass{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "IngressClass", "IngressClass", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&IngressClassList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *IngressClassSpec) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&IngressClassSpec{`, + `Controller:` + fmt.Sprintf("%v", this.Controller) + `,`, + `Parameters:` + strings.Replace(fmt.Sprintf("%v", this.Parameters), "TypedLocalObjectReference", "v11.TypedLocalObjectReference", 1) + `,`, + `}`, + }, "") + return s +} func (this *IngressList) String() string { if this == nil { return "nil" @@ -1087,6 +1407,7 @@ func (this *IngressSpec) String() string { `Backend:` + strings.Replace(this.Backend.String(), "IngressBackend", "IngressBackend", 1) + `,`, `TLS:` + repeatedStringForTLS + `,`, `Rules:` + repeatedStringForRules + `,`, + `IngressClassName:` + valueToStringGenerated(this.IngressClassName) + `,`, `}`, }, "") return s @@ -1595,6 +1916,366 @@ func (m *IngressBackend) Unmarshal(dAtA []byte) error { } return nil } +func (m *IngressClass) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IngressClass: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IngressClass: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", 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 < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", 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 < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IngressClassList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IngressClassList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IngressClassList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", 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 < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", 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 < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, IngressClass{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IngressClassSpec) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IngressClassSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IngressClassSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Controller", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Controller = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parameters", 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 < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Parameters == nil { + m.Parameters = &v11.TypedLocalObjectReference{} + } + if err := m.Parameters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *IngressList) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2055,6 +2736,39 @@ func (m *IngressSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IngressClassName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.IngressClassName = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/networking/v1beta1/generated.proto b/staging/src/k8s.io/api/networking/v1beta1/generated.proto index a72545c8134..30a96c31d81 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/networking/v1beta1/generated.proto @@ -88,6 +88,52 @@ message IngressBackend { optional k8s.io.apimachinery.pkg.util.intstr.IntOrString servicePort = 2; } +// IngressClass represents the class of the Ingress, referenced by the Ingress +// Spec. The `ingressclass.kubernetes.io/is-default-class` annotation can be +// used to indicate that an IngressClass should be considered default. When a +// single IngressClass resource has this annotation set to true, new Ingress +// resources without a class specified will be assigned this default class. +message IngressClass { + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + // Spec is the desired state of the IngressClass. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + // +optional + optional IngressClassSpec spec = 2; +} + +// IngressClassList is a collection of IngressClasses. +message IngressClassList { + // Standard list metadata. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + + // Items is the list of IngressClasses. + // +listType=set + repeated IngressClass items = 2; +} + +// IngressClassSpec provides information about the class of an Ingress. +message IngressClassSpec { + // Controller refers to the name of the controller that should handle this + // class. This allows for different "flavors" that are controlled by the + // same controller. For example, you may have different Parameters for the + // same implementing controller. This should be specified as a + // domain-prefixed path no more than 250 characters in length, e.g. + // "acme.io/ingress-controller". This field is immutable. + optional string controller = 1; + + // Parameters is a link to a resource containing additional configuration + // for the controller. This is optional if the controller does not require + // extra parameters. Example configuration resources include + // `core.ConfigMap` or a controller specific Custom Resource. + // +optional + optional k8s.io.api.core.v1.TypedLocalObjectReference parameters = 2; +} + // IngressList is a collection of Ingress. message IngressList { // Standard object's metadata. @@ -138,6 +184,19 @@ message IngressRuleValue { // IngressSpec describes the Ingress the user wishes to exist. message IngressSpec { + // IngressClassName is the name of the IngressClass cluster resource. The + // associated IngressClass defines which controller will implement the + // resource. This replaces the deprecated `kubernetes.io/ingress.class` + // annotation. For backwards compatibility, when that annotation is set, it + // must be given precedence over this field. The controller may emit a + // warning if the field and annotation have different values. + // Implementations of this API should ignore Ingresses without a class + // specified. An IngressClass resource may be marked as default, which can + // be used to set a default value for this field. For more information, + // refer to the IngressClass documentation. + // +optional + optional string ingressClassName = 4; + // A default backend capable of servicing requests that don't match any // rule. At least one of 'backend' or 'rules' must be specified. This field // is optional to allow the loadbalancer controller or defaulting logic to @@ -175,11 +234,11 @@ message IngressTLS { // +optional repeated string hosts = 1; - // SecretName is the name of the secret used to terminate SSL traffic on 443. - // Field is left optional to allow SSL routing based on SNI hostname alone. - // If the SNI host in a listener conflicts with the "Host" header field used - // by an IngressRule, the SNI host is used for termination and value of the - // Host header is used for routing. + // SecretName is the name of the secret used to terminate TLS traffic on + // port 443. Field is left optional to allow TLS routing based on SNI + // hostname alone. If the SNI host in a listener conflicts with the "Host" + // header field used by an IngressRule, the SNI host is used for termination + // and value of the Host header is used for routing. // +optional optional string secretName = 2; } diff --git a/staging/src/k8s.io/api/networking/v1beta1/register.go b/staging/src/k8s.io/api/networking/v1beta1/register.go index c046c49012c..04234953e61 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/register.go +++ b/staging/src/k8s.io/api/networking/v1beta1/register.go @@ -49,6 +49,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Ingress{}, &IngressList{}, + &IngressClass{}, + &IngressClassList{}, ) // Add the watch version that applies metav1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/staging/src/k8s.io/api/networking/v1beta1/types.go b/staging/src/k8s.io/api/networking/v1beta1/types.go index 37277bf8169..d66d10ed029 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/types.go +++ b/staging/src/k8s.io/api/networking/v1beta1/types.go @@ -63,6 +63,19 @@ type IngressList struct { // IngressSpec describes the Ingress the user wishes to exist. type IngressSpec struct { + // IngressClassName is the name of the IngressClass cluster resource. The + // associated IngressClass defines which controller will implement the + // resource. This replaces the deprecated `kubernetes.io/ingress.class` + // annotation. For backwards compatibility, when that annotation is set, it + // must be given precedence over this field. The controller may emit a + // warning if the field and annotation have different values. + // Implementations of this API should ignore Ingresses without a class + // specified. An IngressClass resource may be marked as default, which can + // be used to set a default value for this field. For more information, + // refer to the IngressClass documentation. + // +optional + IngressClassName *string `json:"ingressClassName,omitempty" protobuf:"bytes,4,opt,name=ingressClassName"` + // A default backend capable of servicing requests that don't match any // rule. At least one of 'backend' or 'rules' must be specified. This field // is optional to allow the loadbalancer controller or defaulting logic to @@ -93,11 +106,11 @@ type IngressTLS struct { // Ingress, if left unspecified. // +optional Hosts []string `json:"hosts,omitempty" protobuf:"bytes,1,rep,name=hosts"` - // SecretName is the name of the secret used to terminate SSL traffic on 443. - // Field is left optional to allow SSL routing based on SNI hostname alone. - // If the SNI host in a listener conflicts with the "Host" header field used - // by an IngressRule, the SNI host is used for termination and value of the - // Host header is used for routing. + // SecretName is the name of the secret used to terminate TLS traffic on + // port 443. Field is left optional to allow TLS routing based on SNI + // hostname alone. If the SNI host in a listener conflicts with the "Host" + // header field used by an IngressRule, the SNI host is used for termination + // and value of the Host header is used for routing. // +optional SecretName string `json:"secretName,omitempty" protobuf:"bytes,2,opt,name=secretName"` // TODO: Consider specifying different modes of termination, protocols etc. @@ -190,3 +203,57 @@ type IngressBackend struct { // Specifies the port of the referenced service. ServicePort intstr.IntOrString `json:"servicePort" protobuf:"bytes,2,opt,name=servicePort"` } + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IngressClass represents the class of the Ingress, referenced by the Ingress +// Spec. The `ingressclass.kubernetes.io/is-default-class` annotation can be +// used to indicate that an IngressClass should be considered default. When a +// single IngressClass resource has this annotation set to true, new Ingress +// resources without a class specified will be assigned this default class. +type IngressClass struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Spec is the desired state of the IngressClass. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + // +optional + Spec IngressClassSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` +} + +// IngressClassSpec provides information about the class of an Ingress. +type IngressClassSpec struct { + // Controller refers to the name of the controller that should handle this + // class. This allows for different "flavors" that are controlled by the + // same controller. For example, you may have different Parameters for the + // same implementing controller. This should be specified as a + // domain-prefixed path no more than 250 characters in length, e.g. + // "acme.io/ingress-controller". This field is immutable. + Controller string `json:"controller,omitempty" protobuf:"bytes,1,opt,name=controller"` + + // Parameters is a link to a resource containing additional configuration + // for the controller. This is optional if the controller does not require + // extra parameters. Example configuration resources include + // `core.ConfigMap` or a controller specific Custom Resource. + // +optional + Parameters *v1.TypedLocalObjectReference `json:"parameters,omitempty" protobuf:"bytes,2,opt,name=parameters"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IngressClassList is a collection of IngressClasses. +type IngressClassList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is the list of IngressClasses. + // +listType=set + Items []IngressClass `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go index 4ae5e32d014..a32a19a8703 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go @@ -67,6 +67,36 @@ func (IngressBackend) SwaggerDoc() map[string]string { return map_IngressBackend } +var map_IngressClass = map[string]string{ + "": "IngressClass represents the class of the Ingress, referenced by the Ingress Spec. The `ingressclass.kubernetes.io/is-default-class` annotation can be used to indicate that an IngressClass should be considered default. When a single IngressClass resource has this annotation set to true, new Ingress resources without a class specified will be assigned this default class.", + "metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "spec": "Spec is the desired state of the IngressClass. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", +} + +func (IngressClass) SwaggerDoc() map[string]string { + return map_IngressClass +} + +var map_IngressClassList = map[string]string{ + "": "IngressClassList is a collection of IngressClasses.", + "metadata": "Standard list metadata.", + "items": "Items is the list of IngressClasses.", +} + +func (IngressClassList) SwaggerDoc() map[string]string { + return map_IngressClassList +} + +var map_IngressClassSpec = map[string]string{ + "": "IngressClassSpec provides information about the class of an Ingress.", + "controller": "Controller refers to the name of the controller that should handle this class. This allows for different \"flavors\" that are controlled by the same controller. For example, you may have different Parameters for the same implementing controller. This should be specified as a domain-prefixed path no more than 250 characters in length, e.g. \"acme.io/ingress-controller\". This field is immutable.", + "parameters": "Parameters is a link to a resource containing additional configuration for the controller. This is optional if the controller does not require extra parameters. Example configuration resources include `core.ConfigMap` or a controller specific Custom Resource.", +} + +func (IngressClassSpec) SwaggerDoc() map[string]string { + return map_IngressClassSpec +} + var map_IngressList = map[string]string{ "": "IngressList is a collection of Ingress.", "metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", @@ -95,10 +125,11 @@ func (IngressRuleValue) SwaggerDoc() map[string]string { } var map_IngressSpec = map[string]string{ - "": "IngressSpec describes the Ingress the user wishes to exist.", - "backend": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default.", - "tls": "TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", - "rules": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", + "": "IngressSpec describes the Ingress the user wishes to exist.", + "ingressClassName": "IngressClassName is the name of the IngressClass cluster resource. The associated IngressClass defines which controller will implement the resource. This replaces the deprecated `kubernetes.io/ingress.class` annotation. For backwards compatibility, when that annotation is set, it must be given precedence over this field. The controller may emit a warning if the field and annotation have different values. Implementations of this API should ignore Ingresses without a class specified. An IngressClass resource may be marked as default, which can be used to set a default value for this field. For more information, refer to the IngressClass documentation.", + "backend": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default.", + "tls": "TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.", + "rules": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", } func (IngressSpec) SwaggerDoc() map[string]string { @@ -117,7 +148,7 @@ func (IngressStatus) SwaggerDoc() map[string]string { var map_IngressTLS = map[string]string{ "": "IngressTLS describes the transport layer security associated with an Ingress.", "hosts": "Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.", - "secretName": "SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.", + "secretName": "SecretName is the name of the secret used to terminate TLS traffic on port 443. Field is left optional to allow TLS routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.", } func (IngressTLS) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/networking/v1beta1/well_known_annotations.go b/staging/src/k8s.io/api/networking/v1beta1/well_known_annotations.go new file mode 100644 index 00000000000..1629b5d5aa6 --- /dev/null +++ b/staging/src/k8s.io/api/networking/v1beta1/well_known_annotations.go @@ -0,0 +1,32 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +const ( + // AnnotationIsDefaultIngressClass can be used to indicate that an + // IngressClass should be considered default. When a single IngressClass + // resource has this annotation set to true, new Ingress resources without a + // class specified will be assigned this default class. + AnnotationIsDefaultIngressClass = "ingressclass.kubernetes.io/is-default-class" + + // AnnotationIngressClass indicates the class of an Ingress to be used when + // determining which controller should implement the Ingress. Use of this + // annotation is deprecated. The Ingress class field should be used instead + // of this annotation. + // +deprecated + AnnotationIngressClass = "kubernetes.io/ingress.class" +) diff --git a/staging/src/k8s.io/api/networking/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/networking/v1beta1/zz_generated.deepcopy.go index 16ac936aea2..ca282c594f2 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/networking/v1beta1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1beta1 import ( + v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -107,6 +108,87 @@ func (in *IngressBackend) DeepCopy() *IngressBackend { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressClass) DeepCopyInto(out *IngressClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClass. +func (in *IngressClass) DeepCopy() *IngressClass { + if in == nil { + return nil + } + out := new(IngressClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IngressClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressClassList) DeepCopyInto(out *IngressClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IngressClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClassList. +func (in *IngressClassList) DeepCopy() *IngressClassList { + if in == nil { + return nil + } + out := new(IngressClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IngressClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressClassSpec) DeepCopyInto(out *IngressClassSpec) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = new(v1.TypedLocalObjectReference) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClassSpec. +func (in *IngressClassSpec) DeepCopy() *IngressClassSpec { + if in == nil { + return nil + } + out := new(IngressClassSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressList) DeepCopyInto(out *IngressList) { *out = *in @@ -181,6 +263,11 @@ func (in *IngressRuleValue) DeepCopy() *IngressRuleValue { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressSpec) DeepCopyInto(out *IngressSpec) { *out = *in + if in.IngressClassName != nil { + in, out := &in.IngressClassName, &out.IngressClassName + *out = new(string) + **out = **in + } if in.Backend != nil { in, out := &in.Backend, &out.Backend *out = new(IngressBackend) diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Ingress.json b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Ingress.json index 9bb4848ea84..155f4e0ece5 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Ingress.json +++ b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Ingress.json @@ -41,28 +41,29 @@ ] }, "spec": { + "ingressClassName": "19", "backend": { - "serviceName": "19", - "servicePort": "20" + "serviceName": "20", + "servicePort": "21" }, "tls": [ { "hosts": [ - "21" + "22" ], - "secretName": "22" + "secretName": "23" } ], "rules": [ { - "host": "23", + "host": "24", "http": { "paths": [ { - "path": "24", + "path": "25", "backend": { - "serviceName": "25", - "servicePort": "26" + "serviceName": "26", + "servicePort": "27" } } ] @@ -74,8 +75,8 @@ "loadBalancer": { "ingress": [ { - "ip": "27", - "hostname": "28" + "ip": "28", + "hostname": "29" } ] } diff --git a/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Ingress.pb b/staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Ingress.pb index ecb437de39c3e220ab4201db0d51bd5b54b8ad5b..9d28c04a029a248c49369f60e13e7c345621b34f 100644 GIT binary patch delta 93 zcmZ3$w1jDb3gfSds_O#nxcIo3j0}W0I2Z*Oq?n8hg*bqGBOxXuV<|Zx%S1?mON>hd h$TAfIsWgMDG*@CWw3OoE;^G47umI|?lwwd~006=-3<3ZE delta 89 zcmZ3&w18=X3gfqls_T5MxcIo33@wE?I2Z*Oq?n8hggCgEj0}aCjEtn@fGlGn2`(`% f5g^M%2&mG?6t2=tiieAf3#8f{sMhd h$TAfIsWgMDG*@CWw3OoE;^G47umI|?lwwd~007JD3?2Xg delta 89 zcmZ3@w47;zHsiO6I_rF`xcIo33@wE?I2Z*Oq?n8hggCgEj0}aCjEtn@fGlGn2`(`% f5g^M%2&mG?6t2=tiieAf3#8f{sMd#K^?J)Wp)lz{1?jz|6?N*y8rPrmKzJa~cbr z7y{OIY;A6xe{Rlh#wa!}Msp!XizId~MoS?kLxU^@E+#`GDJDZ>B|ev`r@N<|Xs<3l z+I6hIK#K`zE`tSQ5tE@w6_cT97o$E +Annotations: +Controller: example.com/controller +Parameters: + APIGroup: v1 + Kind: ConfigMap + Name: example-parameters` + "\n" + + if out != expectedOut { + t.Logf(out) + t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out) + } +} + func TestDescribeNetworkPolicies(t *testing.T) { expectedTime, err := time.Parse("2006-01-02 15:04:05 Z0700 MST", "2017-06-04 21:45:56 -0700 PDT") if err != nil { diff --git a/test/e2e/network/BUILD b/test/e2e/network/BUILD index 3679866a9b9..2a908dd2202 100644 --- a/test/e2e/network/BUILD +++ b/test/e2e/network/BUILD @@ -21,6 +21,7 @@ go_library( "framework.go", "ingress.go", "ingress_scale.go", + "ingressclass.go", "kube_proxy.go", "network_policy.go", "network_tiers.go", @@ -41,6 +42,7 @@ go_library( "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/discovery/v1beta1:go_default_library", "//staging/src/k8s.io/api/networking/v1:go_default_library", + "//staging/src/k8s.io/api/networking/v1beta1:go_default_library", "//staging/src/k8s.io/api/rbac/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/test/e2e/network/ingressclass.go b/test/e2e/network/ingressclass.go new file mode 100644 index 00000000000..e2e4e51ed88 --- /dev/null +++ b/test/e2e/network/ingressclass.go @@ -0,0 +1,123 @@ +/* +Copyright 2020 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 network + +import ( + "context" + "strings" + + networkingv1beta1 "k8s.io/api/networking/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" + + "github.com/onsi/ginkgo" +) + +var _ = SIGDescribe("IngressClass [Feature:Ingress]", func() { + f := framework.NewDefaultFramework("ingressclass") + var cs clientset.Interface + ginkgo.BeforeEach(func() { + cs = f.ClientSet + }) + + ginkgo.It("should set default value on new IngressClass", func() { + ingressClass1, err := createIngressClass(cs, "ingressclass1", true) + framework.ExpectNoError(err) + defer deleteIngressClass(cs, ingressClass1.Name) + + ingress, err := createBasicIngress(cs, f.Namespace.Name) + framework.ExpectNoError(err) + + if ingress.Spec.IngressClassName == nil { + framework.Failf("Expected IngressClassName to be set by Admission Controller") + } else if *ingress.Spec.IngressClassName != ingressClass1.Name { + framework.Failf("Expected IngressClassName to be %s, got %s", ingressClass1.Name, *ingress.Spec.IngressClassName) + } + }) + + ginkgo.It("should not set default value if no default IngressClass", func() { + ingressClass1, err := createIngressClass(cs, "ingressclass1", false) + framework.ExpectNoError(err) + defer deleteIngressClass(cs, ingressClass1.Name) + + ingress, err := createBasicIngress(cs, f.Namespace.Name) + framework.ExpectNoError(err) + + if ingress.Spec.IngressClassName != nil { + framework.Failf("Expected IngressClassName to be nil, got %s", *ingress.Spec.IngressClassName) + } + }) + + ginkgo.It("should prevent Ingress creation if more than 1 IngressClass marked as default", func() { + ingressClass1, err := createIngressClass(cs, "ingressclass1", true) + framework.ExpectNoError(err) + defer deleteIngressClass(cs, ingressClass1.Name) + + ingressClass2, err := createIngressClass(cs, "ingressclass2", true) + framework.ExpectNoError(err) + defer deleteIngressClass(cs, ingressClass2.Name) + + _, err = createBasicIngress(cs, f.Namespace.Name) + if err == nil { + framework.Failf("Expected error creating Ingress") + } + + expectedErr := "2 default IngressClasses were found, only 1 allowed" + if !strings.Contains(err.Error(), expectedErr) { + framework.Failf("Expected error to contain %s, got %s", expectedErr, err.Error()) + } + }) + +}) + +func createIngressClass(cs clientset.Interface, name string, isDefault bool) (*networkingv1beta1.IngressClass, error) { + ingressClass := &networkingv1beta1.IngressClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: networkingv1beta1.IngressClassSpec{ + Controller: "example.com/controller", + }, + } + + if isDefault { + ingressClass.Annotations = map[string]string{networkingv1beta1.AnnotationIsDefaultIngressClass: "true"} + } + + return cs.NetworkingV1beta1().IngressClasses().Create(context.TODO(), ingressClass, metav1.CreateOptions{}) +} + +func createBasicIngress(cs clientset.Interface, namespace string) (*networkingv1beta1.Ingress, error) { + return cs.NetworkingV1beta1().Ingresses(namespace).Create(context.TODO(), &networkingv1beta1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ingress1", + }, + Spec: networkingv1beta1.IngressSpec{ + Backend: &networkingv1beta1.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + }, + }, + }, metav1.CreateOptions{}) +} + +func deleteIngressClass(cs clientset.Interface, name string) { + err := cs.NetworkingV1beta1().IngressClasses().Delete(context.TODO(), name, &metav1.DeleteOptions{}) + framework.ExpectNoError(err) +} diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go index 525a8da73fa..56e4888128d 100644 --- a/test/integration/etcd/data.go +++ b/test/integration/etcd/data.go @@ -219,6 +219,10 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes Stub: `{"metadata": {"name": "ingress2"}, "spec": {"backend": {"serviceName": "service", "servicePort": 5000}}}`, ExpectedEtcdPath: "/registry/ingress/" + namespace + "/ingress2", }, + gvr("networking.k8s.io", "v1beta1", "ingressclasses"): { + Stub: `{"metadata": {"name": "ingressclass2"}, "spec": {"controller": "example.com/controller"}}`, + ExpectedEtcdPath: "/registry/ingressclasses/ingressclass2", + }, // -- // k8s.io/kubernetes/pkg/apis/networking/v1