Move internal Ingress type from extensions to networking

pull/564/head
Jordan Liggitt 2019-02-14 00:29:03 -05:00
parent a2a5bd03fd
commit 47cb9559be
20 changed files with 654 additions and 708 deletions

View File

@ -303,8 +303,6 @@ pkg/registry/core/service/storage
pkg/registry/core/serviceaccount/storage
pkg/registry/events/rest
pkg/registry/extensions/controller/storage
pkg/registry/extensions/ingress
pkg/registry/extensions/ingress/storage
pkg/registry/extensions/rest
pkg/registry/networking/networkpolicy/storage
pkg/registry/networking/rest

View File

@ -56,8 +56,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ReplicationControllerDummy{},
&apps.DaemonSetList{},
&apps.DaemonSet{},
&Ingress{},
&IngressList{},
&networking.Ingress{},
&networking.IngressList{},
&apps.ReplicaSet{},
&apps.ReplicaSetList{},
&policy.PodSecurityPolicy{},

View File

@ -30,8 +30,6 @@ package extensions
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
api "k8s.io/kubernetes/pkg/apis/core"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@ -40,172 +38,3 @@ import (
type ReplicationControllerDummy struct {
metav1.TypeMeta
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Ingress is a collection of rules that allow inbound connections to reach the
// endpoints defined by a backend. An Ingress can be configured to give services
// externally-reachable urls, load balance traffic, terminate SSL, offer name
// based virtual hosting etc.
type Ingress struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Spec is the desired state of the Ingress.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// +optional
Spec IngressSpec
// Status is the current state of the Ingress.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// +optional
Status IngressStatus
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// IngressList is a collection of Ingress.
type IngressList struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ListMeta
// Items is the list of Ingress.
Items []Ingress
}
// 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.
// +optional
Backend *IngressBackend
// 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.
// +optional
TLS []IngressTLS
// A list of host rules used to configure the Ingress. If unspecified, or
// no rule matches, all traffic is sent to the default backend.
// +optional
Rules []IngressRule
// TODO: Add the ability to specify load-balancer IP through claims
}
// 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
// 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.
// +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.
// +optional
SecretName string
// TODO: Consider specifying different modes of termination, protocols etc.
}
// IngressStatus describe the current state of the Ingress.
type IngressStatus struct {
// LoadBalancer contains the current status of the load-balancer.
// +optional
LoadBalancer api.LoadBalancerStatus
}
// IngressRule represents the rules mapping the paths under a specified host to
// the related backend services. Incoming requests are first evaluated for a host
// match, then routed to the backend associated with the matching IngressRuleValue.
type IngressRule struct {
// Host is the fully qualified domain name of a network host, as defined
// by RFC 3986. Note the following deviations from the "host" part of the
// URI as defined in the RFC:
// 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the
// IP in the Spec of the parent Ingress.
// 2. The `:` delimiter is not respected because ports are not allowed.
// Currently the port of an Ingress is implicitly :80 for http and
// :443 for https.
// Both these may change in the future.
// Incoming requests are matched against the host before the IngressRuleValue.
// If the host is unspecified, the Ingress routes all traffic based on the
// specified IngressRuleValue.
// +optional
Host string
// IngressRuleValue represents a rule to route requests for this IngressRule.
// If unspecified, the rule defaults to a http catch-all. Whether that sends
// just traffic matching the host to the default backend or all traffic to the
// default backend, is left to the controller fulfilling the Ingress. Http is
// currently the only supported IngressRuleValue.
// +optional
IngressRuleValue
}
// IngressRuleValue represents a rule to apply against incoming requests. If the
// rule is satisfied, the request is routed to the specified backend. Currently
// mixing different types of rules in a single Ingress is disallowed, so exactly
// one of the following must be set.
type IngressRuleValue struct {
//TODO:
// 1. Consider renaming this resource and the associated rules so they
// aren't tied to Ingress. They can be used to route intra-cluster traffic.
// 2. Consider adding fields for ingress-type specific global options
// usable by a loadbalancer, like http keep-alive.
// +optional
HTTP *HTTPIngressRuleValue
}
// HTTPIngressRuleValue is a list of http selectors pointing to backends.
// In the example: http://<host>/<path>?<searchpart> -> backend where
// where parts of the url correspond to RFC 3986, this resource will be used
// to match against everything after the last '/' and before the first '?'
// or '#'.
type HTTPIngressRuleValue struct {
// A collection of paths that map requests to backends.
Paths []HTTPIngressPath
// TODO: Consider adding fields for ingress-type specific global
// options usable by a loadbalancer, like http keep-alive.
}
// HTTPIngressPath associates a path regex with a backend. Incoming urls matching
// the path are forwarded to the backend.
type HTTPIngressPath struct {
// Path is an extended POSIX regex as defined by IEEE Std 1003.1,
// (i.e this follows the egrep/unix syntax, not the perl syntax)
// matched against the path of an incoming request. Currently it can
// contain characters disallowed from the conventional "path"
// part of a URL as defined by RFC 3986. Paths must begin with
// a '/'. If unspecified, the path defaults to a catch all sending
// traffic to the backend.
// +optional
Path string
// Backend defines the referenced service endpoint to which the traffic
// will be forwarded to.
Backend IngressBackend
}
// IngressBackend describes all endpoints for a given service and port.
type IngressBackend struct {
// Specifies the name of the referenced service.
ServiceName string
// Specifies the port of the referenced service.
ServicePort intstr.IntOrString
}

View File

@ -16,6 +16,7 @@ limitations under the License.
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/apps
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/policy
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/networking
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/extensions
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/autoscaling
// +k8s:conversion-gen-external-types=k8s.io/api/extensions/v1beta1

View File

@ -1,171 +0,0 @@
/*
Copyright 2015 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 validation
import (
"net"
"regexp"
"strings"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/extensions"
)
// ValidateIngress tests if required fields in the Ingress are set.
func ValidateIngress(ingress *extensions.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
func validateIngressTLS(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// TODO: Perform a more thorough validation of spec.TLS.Hosts that takes
// the wildcard spec from RFC 6125 into account.
for _, itls := range spec.TLS {
for i, host := range itls.Hosts {
if strings.Contains(host, "*") {
for _, msg := range validation.IsWildcardDNS1123Subdomain(host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
}
continue
}
for _, msg := range validation.IsDNS1123Subdomain(host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
}
}
}
return allErrs
}
// ValidateIngressSpec tests if required fields in the IngressSpec are set.
func ValidateIngressSpec(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// TODO: Is a default backend mandatory?
if spec.Backend != nil {
allErrs = append(allErrs, validateIngressBackend(spec.Backend, fldPath.Child("backend"))...)
} else if len(spec.Rules) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, "either `backend` or `rules` must be specified"))
}
if len(spec.Rules) > 0 {
allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...)
}
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 *extensions.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status.
func ValidateIngressStatusUpdate(ingress, oldIngress *extensions.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, apivalidation.ValidateLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
return allErrs
}
func validateIngressRules(ingressRules []extensions.IngressRule, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(ingressRules) == 0 {
return append(allErrs, field.Required(fldPath, ""))
}
for i, ih := range ingressRules {
if len(ih.Host) > 0 {
if isIP := (net.ParseIP(ih.Host) != nil); isIP {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address"))
}
// TODO: Ports and ips are allowed in the host part of a url
// according to RFC 3986, consider allowing them.
if strings.Contains(ih.Host, "*") {
for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
}
continue
}
for _, msg := range validation.IsDNS1123Subdomain(ih.Host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
}
}
allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...)
}
return allErrs
}
func validateIngressRuleValue(ingressRule *extensions.IngressRuleValue, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if ingressRule.HTTP != nil {
allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"))...)
}
return allErrs
}
func validateHTTPIngressRuleValue(httpIngressRuleValue *extensions.HTTPIngressRuleValue, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(httpIngressRuleValue.Paths) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
}
for i, rule := range httpIngressRuleValue.Paths {
if len(rule.Path) > 0 {
if !strings.HasPrefix(rule.Path, "/") {
allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be an absolute path"))
}
// TODO: More draconian path regex validation.
// Path must be a valid regex. This is the basic requirement.
// In addition to this any characters not allowed in a path per
// RFC 3986 section-3.3 cannot appear as a literal in the regex.
// Consider the example: http://host/valid?#bar, everything after
// the last '/' is a valid regex that matches valid#bar, which
// isn't a valid path, because the path terminates at the first ?
// or #. A more sophisticated form of validation would detect that
// the user is confusing url regexes with path regexes.
_, err := regexp.CompilePOSIX(rule.Path)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be a valid regex"))
}
}
allErrs = append(allErrs, validateIngressBackend(&rule.Backend, fldPath.Child("backend"))...)
}
return allErrs
}
// validateIngressBackend tests if a given backend is valid.
func validateIngressBackend(backend *extensions.IngressBackend, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// All backends must reference a single local service by name, and a single service port by name or number.
if len(backend.ServiceName) == 0 {
return append(allErrs, field.Required(fldPath.Child("serviceName"), ""))
}
for _, msg := range apivalidation.ValidateServiceName(backend.ServiceName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, msg))
}
allErrs = append(allErrs, apivalidation.ValidatePortNumOrName(backend.ServicePort, fldPath.Child("servicePort"))...)
return allErrs
}

View File

@ -1,294 +0,0 @@
/*
Copyright 2014 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 validation
import (
"fmt"
"strings"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestValidateIngress(t *testing.T) {
defaultBackend := extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
newValid := func() extensions.Ingress {
return extensions.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []extensions.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
Status: extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1"},
},
},
},
}
}
servicelessBackend := newValid()
servicelessBackend.Spec.Backend.ServiceName = ""
invalidNameBackend := newValid()
invalidNameBackend.Spec.Backend.ServiceName = "defaultBackend"
noPortBackend := newValid()
noPortBackend.Spec.Backend = &extensions.IngressBackend{ServiceName: defaultBackend.ServiceName}
noForwardSlashPath := newValid()
noForwardSlashPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{
{
Path: "invalid",
Backend: defaultBackend,
},
}
noPaths := newValid()
noPaths.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{}
badHost := newValid()
badHost.Spec.Rules[0].Host = "foobar:80"
badRegexPath := newValid()
badPathExpr := "/invalid["
badRegexPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{
{
Path: badPathExpr,
Backend: defaultBackend,
},
}
badPathErr := fmt.Sprintf("spec.rules[0].http.paths[0].path: Invalid value: '%v'", badPathExpr)
hostIP := "127.0.0.1"
badHostIP := newValid()
badHostIP.Spec.Rules[0].Host = hostIP
badHostIPErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", hostIP)
errorCases := map[string]extensions.Ingress{
"spec.backend.serviceName: Required value": servicelessBackend,
"spec.backend.serviceName: Invalid value": invalidNameBackend,
"spec.backend.servicePort: Invalid value": noPortBackend,
"spec.rules[0].host: Invalid value": badHost,
"spec.rules[0].http.paths: Required value": noPaths,
"spec.rules[0].http.paths[0].path: Invalid value": noForwardSlashPath,
}
errorCases[badPathErr] = badRegexPath
errorCases[badHostIPErr] = badHostIP
wildcardHost := "foo.*.bar.com"
badWildcard := newValid()
badWildcard.Spec.Rules[0].Host = wildcardHost
badWildcardErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", wildcardHost)
errorCases[badWildcardErr] = badWildcard
for k, v := range errorCases {
errs := ValidateIngress(&v)
if len(errs) == 0 {
t.Errorf("expected failure for %q", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %q, expected: %q", err, k)
}
}
}
}
func TestValidateIngressTLS(t *testing.T) {
defaultBackend := extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
newValid := func() extensions.Ingress {
return extensions.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []extensions.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
Status: extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1"},
},
},
},
}
}
errorCases := map[string]extensions.Ingress{}
wildcardHost := "foo.*.bar.com"
badWildcardTLS := newValid()
badWildcardTLS.Spec.Rules[0].Host = "*.foo.bar.com"
badWildcardTLS.Spec.TLS = []extensions.IngressTLS{
{
Hosts: []string{wildcardHost},
},
}
badWildcardTLSErr := fmt.Sprintf("spec.tls[0].hosts: Invalid value: '%v'", wildcardHost)
errorCases[badWildcardTLSErr] = badWildcardTLS
for k, v := range errorCases {
errs := ValidateIngress(&v)
if len(errs) == 0 {
t.Errorf("expected failure for %q", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %q, expected: %q", err, k)
}
}
}
}
func TestValidateIngressStatusUpdate(t *testing.T) {
defaultBackend := extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
newValid := func() extensions.Ingress {
return extensions.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
ResourceVersion: "9",
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []extensions.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
Status: extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1", Hostname: "foo.bar.com"},
},
},
},
}
}
oldValue := newValid()
newValue := newValid()
newValue.Status = extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.2", Hostname: "foo.com"},
},
},
}
invalidIP := newValid()
invalidIP.Status = extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "abcd", Hostname: "foo.com"},
},
},
}
invalidHostname := newValid()
invalidHostname.Status = extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1", Hostname: "127.0.0.1"},
},
},
}
errs := ValidateIngressStatusUpdate(&newValue, &oldValue)
if len(errs) != 0 {
t.Errorf("Unexpected error %v", errs)
}
errorCases := map[string]extensions.Ingress{
"status.loadBalancer.ingress[0].ip: Invalid value": invalidIP,
"status.loadBalancer.ingress[0].hostname: Invalid value": invalidHostname,
}
for k, v := range errorCases {
errs := ValidateIngressStatusUpdate(&v, &oldValue)
if len(errs) == 0 {
t.Errorf("expected failure for %s", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %q, expected: %q", err, k)
}
}
}
}

View File

@ -195,3 +195,172 @@ type NetworkPolicyList struct {
Items []NetworkPolicy
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Ingress is a collection of rules that allow inbound connections to reach the
// endpoints defined by a backend. An Ingress can be configured to give services
// externally-reachable urls, load balance traffic, terminate SSL, offer name
// based virtual hosting etc.
type Ingress struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Spec is the desired state of the Ingress.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// +optional
Spec IngressSpec
// Status is the current state of the Ingress.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// +optional
Status IngressStatus
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// IngressList is a collection of Ingress.
type IngressList struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ListMeta
// Items is the list of Ingress.
Items []Ingress
}
// 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.
// +optional
Backend *IngressBackend
// 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.
// +optional
TLS []IngressTLS
// A list of host rules used to configure the Ingress. If unspecified, or
// no rule matches, all traffic is sent to the default backend.
// +optional
Rules []IngressRule
// TODO: Add the ability to specify load-balancer IP through claims
}
// 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
// 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.
// +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.
// +optional
SecretName string
// TODO: Consider specifying different modes of termination, protocols etc.
}
// IngressStatus describe the current state of the Ingress.
type IngressStatus struct {
// LoadBalancer contains the current status of the load-balancer.
// +optional
LoadBalancer api.LoadBalancerStatus
}
// IngressRule represents the rules mapping the paths under a specified host to
// the related backend services. Incoming requests are first evaluated for a host
// match, then routed to the backend associated with the matching IngressRuleValue.
type IngressRule struct {
// Host is the fully qualified domain name of a network host, as defined
// by RFC 3986. Note the following deviations from the "host" part of the
// URI as defined in the RFC:
// 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the
// IP in the Spec of the parent Ingress.
// 2. The `:` delimiter is not respected because ports are not allowed.
// Currently the port of an Ingress is implicitly :80 for http and
// :443 for https.
// Both these may change in the future.
// Incoming requests are matched against the host before the IngressRuleValue.
// If the host is unspecified, the Ingress routes all traffic based on the
// specified IngressRuleValue.
// +optional
Host string
// IngressRuleValue represents a rule to route requests for this IngressRule.
// If unspecified, the rule defaults to a http catch-all. Whether that sends
// just traffic matching the host to the default backend or all traffic to the
// default backend, is left to the controller fulfilling the Ingress. Http is
// currently the only supported IngressRuleValue.
// +optional
IngressRuleValue
}
// IngressRuleValue represents a rule to apply against incoming requests. If the
// rule is satisfied, the request is routed to the specified backend. Currently
// mixing different types of rules in a single Ingress is disallowed, so exactly
// one of the following must be set.
type IngressRuleValue struct {
//TODO:
// 1. Consider renaming this resource and the associated rules so they
// aren't tied to Ingress. They can be used to route intra-cluster traffic.
// 2. Consider adding fields for ingress-type specific global options
// usable by a loadbalancer, like http keep-alive.
// +optional
HTTP *HTTPIngressRuleValue
}
// HTTPIngressRuleValue is a list of http selectors pointing to backends.
// In the example: http://<host>/<path>?<searchpart> -> backend where
// where parts of the url correspond to RFC 3986, this resource will be used
// to match against everything after the last '/' and before the first '?'
// or '#'.
type HTTPIngressRuleValue struct {
// A collection of paths that map requests to backends.
Paths []HTTPIngressPath
// TODO: Consider adding fields for ingress-type specific global
// options usable by a loadbalancer, like http keep-alive.
}
// HTTPIngressPath associates a path regex with a backend. Incoming urls matching
// the path are forwarded to the backend.
type HTTPIngressPath struct {
// Path is an extended POSIX regex as defined by IEEE Std 1003.1,
// (i.e this follows the egrep/unix syntax, not the perl syntax)
// matched against the path of an incoming request. Currently it can
// contain characters disallowed from the conventional "path"
// part of a URL as defined by RFC 3986. Paths must begin with
// a '/'. If unspecified, the path defaults to a catch all sending
// traffic to the backend.
// +optional
Path string
// Backend defines the referenced service endpoint to which the traffic
// will be forwarded to.
Backend IngressBackend
}
// IngressBackend describes all endpoints for a given service and port.
type IngressBackend struct {
// Specifies the name of the referenced service.
ServiceName string
// Specifies the port of the referenced service.
ServicePort intstr.IntOrString
}

View File

@ -17,6 +17,10 @@ limitations under the License.
package validation
import (
"net"
"regexp"
"strings"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/intstr"
@ -169,3 +173,145 @@ func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorLi
}
return allErrs
}
// ValidateIngress tests if required fields in the Ingress are set.
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
func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// TODO: Perform a more thorough validation of spec.TLS.Hosts that takes
// the wildcard spec from RFC 6125 into account.
for _, itls := range spec.TLS {
for i, host := range itls.Hosts {
if strings.Contains(host, "*") {
for _, msg := range validation.IsWildcardDNS1123Subdomain(host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
}
continue
}
for _, msg := range validation.IsDNS1123Subdomain(host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
}
}
}
return allErrs
}
// ValidateIngressSpec tests if required fields in the IngressSpec are set.
func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// TODO: Is a default backend mandatory?
if spec.Backend != nil {
allErrs = append(allErrs, validateIngressBackend(spec.Backend, fldPath.Child("backend"))...)
} else if len(spec.Rules) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, "either `backend` or `rules` must be specified"))
}
if len(spec.Rules) > 0 {
allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...)
}
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"))...)
return allErrs
}
// ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status.
func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, apivalidation.ValidateLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
return allErrs
}
func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(ingressRules) == 0 {
return append(allErrs, field.Required(fldPath, ""))
}
for i, ih := range ingressRules {
if len(ih.Host) > 0 {
if isIP := (net.ParseIP(ih.Host) != nil); isIP {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address"))
}
// TODO: Ports and ips are allowed in the host part of a url
// according to RFC 3986, consider allowing them.
if strings.Contains(ih.Host, "*") {
for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
}
continue
}
for _, msg := range validation.IsDNS1123Subdomain(ih.Host) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
}
}
allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...)
}
return allErrs
}
func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if ingressRule.HTTP != nil {
allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"))...)
}
return allErrs
}
func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(httpIngressRuleValue.Paths) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
}
for i, rule := range httpIngressRuleValue.Paths {
if len(rule.Path) > 0 {
if !strings.HasPrefix(rule.Path, "/") {
allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be an absolute path"))
}
// TODO: More draconian path regex validation.
// Path must be a valid regex. This is the basic requirement.
// In addition to this any characters not allowed in a path per
// RFC 3986 section-3.3 cannot appear as a literal in the regex.
// Consider the example: http://host/valid?#bar, everything after
// the last '/' is a valid regex that matches valid#bar, which
// isn't a valid path, because the path terminates at the first ?
// or #. A more sophisticated form of validation would detect that
// the user is confusing url regexes with path regexes.
_, err := regexp.CompilePOSIX(rule.Path)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be a valid regex"))
}
}
allErrs = append(allErrs, validateIngressBackend(&rule.Backend, fldPath.Child("backend"))...)
}
return allErrs
}
// validateIngressBackend tests if a given backend is valid.
func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// All backends must reference a single local service by name, and a single service port by name or number.
if len(backend.ServiceName) == 0 {
return append(allErrs, field.Required(fldPath.Child("serviceName"), ""))
}
for _, msg := range apivalidation.ValidateServiceName(backend.ServiceName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, msg))
}
allErrs = append(allErrs, apivalidation.ValidatePortNumOrName(backend.ServicePort, fldPath.Child("servicePort"))...)
return allErrs
}

View File

@ -17,6 +17,8 @@ limitations under the License.
package validation
import (
"fmt"
"strings"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -733,3 +735,269 @@ func TestValidateNetworkPolicyUpdate(t *testing.T) {
}
}
}
func TestValidateIngress(t *testing.T) {
defaultBackend := networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
newValid := func() networking.Ingress {
return networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []networking.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
Status: networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1"},
},
},
},
}
}
servicelessBackend := newValid()
servicelessBackend.Spec.Backend.ServiceName = ""
invalidNameBackend := newValid()
invalidNameBackend.Spec.Backend.ServiceName = "defaultBackend"
noPortBackend := newValid()
noPortBackend.Spec.Backend = &networking.IngressBackend{ServiceName: defaultBackend.ServiceName}
noForwardSlashPath := newValid()
noForwardSlashPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []networking.HTTPIngressPath{
{
Path: "invalid",
Backend: defaultBackend,
},
}
noPaths := newValid()
noPaths.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []networking.HTTPIngressPath{}
badHost := newValid()
badHost.Spec.Rules[0].Host = "foobar:80"
badRegexPath := newValid()
badPathExpr := "/invalid["
badRegexPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []networking.HTTPIngressPath{
{
Path: badPathExpr,
Backend: defaultBackend,
},
}
badPathErr := fmt.Sprintf("spec.rules[0].http.paths[0].path: Invalid value: '%v'", badPathExpr)
hostIP := "127.0.0.1"
badHostIP := newValid()
badHostIP.Spec.Rules[0].Host = hostIP
badHostIPErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", hostIP)
errorCases := map[string]networking.Ingress{
"spec.backend.serviceName: Required value": servicelessBackend,
"spec.backend.serviceName: Invalid value": invalidNameBackend,
"spec.backend.servicePort: Invalid value": noPortBackend,
"spec.rules[0].host: Invalid value": badHost,
"spec.rules[0].http.paths: Required value": noPaths,
"spec.rules[0].http.paths[0].path: Invalid value": noForwardSlashPath,
}
errorCases[badPathErr] = badRegexPath
errorCases[badHostIPErr] = badHostIP
wildcardHost := "foo.*.bar.com"
badWildcard := newValid()
badWildcard.Spec.Rules[0].Host = wildcardHost
badWildcardErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", wildcardHost)
errorCases[badWildcardErr] = badWildcard
for k, v := range errorCases {
errs := ValidateIngress(&v)
if len(errs) == 0 {
t.Errorf("expected failure for %q", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %q, expected: %q", err, k)
}
}
}
}
func TestValidateIngressTLS(t *testing.T) {
defaultBackend := networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
newValid := func() networking.Ingress {
return networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []networking.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
Status: networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1"},
},
},
},
}
}
errorCases := map[string]networking.Ingress{}
wildcardHost := "foo.*.bar.com"
badWildcardTLS := newValid()
badWildcardTLS.Spec.Rules[0].Host = "*.foo.bar.com"
badWildcardTLS.Spec.TLS = []networking.IngressTLS{
{
Hosts: []string{wildcardHost},
},
}
badWildcardTLSErr := fmt.Sprintf("spec.tls[0].hosts: Invalid value: '%v'", wildcardHost)
errorCases[badWildcardTLSErr] = badWildcardTLS
for k, v := range errorCases {
errs := ValidateIngress(&v)
if len(errs) == 0 {
t.Errorf("expected failure for %q", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %q, expected: %q", err, k)
}
}
}
}
func TestValidateIngressStatusUpdate(t *testing.T) {
defaultBackend := networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
newValid := func() networking.Ingress {
return networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
ResourceVersion: "9",
},
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []networking.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
Status: networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1", Hostname: "foo.bar.com"},
},
},
},
}
}
oldValue := newValid()
newValue := newValid()
newValue.Status = networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.2", Hostname: "foo.com"},
},
},
}
invalidIP := newValid()
invalidIP.Status = networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "abcd", Hostname: "foo.com"},
},
},
}
invalidHostname := newValid()
invalidHostname.Status = networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1", Hostname: "127.0.0.1"},
},
},
}
errs := ValidateIngressStatusUpdate(&newValue, &oldValue)
if len(errs) != 0 {
t.Errorf("Unexpected error %v", errs)
}
errorCases := map[string]networking.Ingress{
"status.loadBalancer.ingress[0].ip: Invalid value": invalidIP,
"status.loadBalancer.ingress[0].hostname: Invalid value": invalidHostname,
}
for k, v := range errorCases {
errs := ValidateIngressStatusUpdate(&v, &oldValue)
if len(errs) == 0 {
t.Errorf("expected failure for %s", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %q, expected: %q", err, k)
}
}
}
}

View File

@ -52,7 +52,6 @@ import (
"k8s.io/kubernetes/pkg/apis/coordination"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
@ -992,14 +991,14 @@ func printServiceList(list *api.ServiceList, options printers.PrintOptions) ([]m
}
// backendStringer behaves just like a string interface and converts the given backend to a string.
func backendStringer(backend *extensions.IngressBackend) string {
func backendStringer(backend *networking.IngressBackend) string {
if backend == nil {
return ""
}
return fmt.Sprintf("%v:%v", backend.ServiceName, backend.ServicePort.String())
}
func formatHosts(rules []extensions.IngressRule) string {
func formatHosts(rules []networking.IngressRule) string {
list := []string{}
max := 3
more := false
@ -1021,14 +1020,14 @@ func formatHosts(rules []extensions.IngressRule) string {
return ret
}
func formatPorts(tls []extensions.IngressTLS) string {
func formatPorts(tls []networking.IngressTLS) string {
if len(tls) != 0 {
return "80, 443"
}
return "80"
}
func printIngress(obj *extensions.Ingress, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
func printIngress(obj *networking.Ingress, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
row := metav1beta1.TableRow{
Object: runtime.RawExtension{Object: obj},
}
@ -1040,7 +1039,7 @@ func printIngress(obj *extensions.Ingress, options printers.PrintOptions) ([]met
return []metav1beta1.TableRow{row}, nil
}
func printIngressList(list *extensions.IngressList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
func printIngressList(list *networking.IngressList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
rows := make([]metav1beta1.TableRow, 0, len(list.Items))
for i := range list.Items {
r, err := printIngress(&list.Items[i], options)

View File

@ -50,7 +50,7 @@ import (
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/coordination"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/scheduling"
"k8s.io/kubernetes/pkg/apis/storage"
@ -1069,7 +1069,7 @@ func contains(fields []string, field string) bool {
}
func TestPrintHunmanReadableIngressWithColumnLabels(t *testing.T) {
ingress := extensions.Ingress{
ingress := networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
@ -1077,13 +1077,13 @@ func TestPrintHunmanReadableIngressWithColumnLabels(t *testing.T) {
"app_name": "kubectl_test_ingress",
},
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
ServiceName: "svc",
ServicePort: intstr.FromInt(93),
},
},
Status: extensions.IngressStatus{
Status: networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{

View File

@ -28,7 +28,7 @@ import (
deploymentstore "k8s.io/kubernetes/pkg/registry/apps/deployment/storage"
replicasetstore "k8s.io/kubernetes/pkg/registry/apps/replicaset/storage"
expcontrollerstore "k8s.io/kubernetes/pkg/registry/extensions/controller/storage"
ingressstore "k8s.io/kubernetes/pkg/registry/extensions/ingress/storage"
ingressstore "k8s.io/kubernetes/pkg/registry/networking/ingress/storage"
networkpolicystore "k8s.io/kubernetes/pkg/registry/networking/networkpolicy/storage"
pspstore "k8s.io/kubernetes/pkg/registry/policy/podsecuritypolicy/storage"
)

View File

@ -14,4 +14,4 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package ingress // import "k8s.io/kubernetes/pkg/registry/extensions/ingress"
package ingress // import "k8s.io/kubernetes/pkg/registry/networking/ingress"

View File

@ -25,13 +25,14 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/extensions"
"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/extensions/ingress"
"k8s.io/kubernetes/pkg/registry/networking/ingress"
)
// rest implements a RESTStorage for replication controllers
// REST implements a RESTStorage for replication controllers
type REST struct {
*genericregistry.Store
}
@ -39,8 +40,8 @@ type REST struct {
// NewREST returns a RESTStorage object that will work against replication controllers.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &extensions.Ingress{} },
NewListFunc: func() runtime.Object { return &extensions.IngressList{} },
NewFunc: func() runtime.Object { return &networking.Ingress{} },
NewListFunc: func() runtime.Object { return &networking.IngressList{} },
DefaultQualifiedResource: extensions.Resource("ingresses"),
CreateStrategy: ingress.Strategy,
@ -72,8 +73,9 @@ type StatusREST struct {
store *genericregistry.Store
}
// New creates an instance of the StatusREST object
func (r *StatusREST) New() runtime.Object {
return &extensions.Ingress{}
return &networking.Ingress{}
}
// Get retrieves the object from the storage. It is required to support Patch.

View File

@ -29,6 +29,7 @@ import (
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
@ -53,19 +54,19 @@ var (
defaultLoadBalancer = "127.0.0.1"
defaultPath = "/foo"
defaultPathMap = map[string]string{defaultPath: defaultBackendName}
defaultTLS = []extensions.IngressTLS{
defaultTLS = []networking.IngressTLS{
{Hosts: []string{"foo.bar.com", "*.bar.com"}, SecretName: "fooSecret"},
}
)
type IngressRuleValues map[string]string
func toHTTPIngressPaths(pathMap map[string]string) []extensions.HTTPIngressPath {
httpPaths := []extensions.HTTPIngressPath{}
func toHTTPIngressPaths(pathMap map[string]string) []networking.HTTPIngressPath {
httpPaths := []networking.HTTPIngressPath{}
for path, backend := range pathMap {
httpPaths = append(httpPaths, extensions.HTTPIngressPath{
httpPaths = append(httpPaths, networking.HTTPIngressPath{
Path: path,
Backend: extensions.IngressBackend{
Backend: networking.IngressBackend{
ServiceName: backend,
ServicePort: defaultBackendPort,
},
@ -74,13 +75,13 @@ func toHTTPIngressPaths(pathMap map[string]string) []extensions.HTTPIngressPath
return httpPaths
}
func toIngressRules(hostRules map[string]IngressRuleValues) []extensions.IngressRule {
rules := []extensions.IngressRule{}
func toIngressRules(hostRules map[string]IngressRuleValues) []networking.IngressRule {
rules := []networking.IngressRule{}
for host, pathMap := range hostRules {
rules = append(rules, extensions.IngressRule{
rules = append(rules, networking.IngressRule{
Host: host,
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: toHTTPIngressPaths(pathMap),
},
},
@ -89,14 +90,14 @@ func toIngressRules(hostRules map[string]IngressRuleValues) []extensions.Ingress
return rules
}
func newIngress(pathMap map[string]string) *extensions.Ingress {
return &extensions.Ingress{
func newIngress(pathMap map[string]string) *networking.Ingress {
return &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
ServiceName: defaultBackendName,
ServicePort: defaultBackendPort,
},
@ -105,7 +106,7 @@ func newIngress(pathMap map[string]string) *extensions.Ingress {
}),
TLS: defaultTLS,
},
Status: extensions.IngressStatus{
Status: networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: defaultLoadBalancer},
@ -115,7 +116,7 @@ func newIngress(pathMap map[string]string) *extensions.Ingress {
}
}
func validIngress() *extensions.Ingress {
func validIngress() *networking.Ingress {
return newIngress(defaultPathMap)
}
@ -126,8 +127,8 @@ func TestCreate(t *testing.T) {
test := genericregistrytest.New(t, storage.Store)
ingress := validIngress()
noDefaultBackendAndRules := validIngress()
noDefaultBackendAndRules.Spec.Backend = &extensions.IngressBackend{}
noDefaultBackendAndRules.Spec.Rules = []extensions.IngressRule{}
noDefaultBackendAndRules.Spec.Backend = &networking.IngressBackend{}
noDefaultBackendAndRules.Spec.Rules = []networking.IngressRule{}
badPath := validIngress()
badPath.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
"foo.bar.com": {"/invalid[": "svc"}})
@ -149,11 +150,11 @@ func TestUpdate(t *testing.T) {
validIngress(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Ingress)
object := obj.(*networking.Ingress)
object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
"bar.foo.com": {"/bar": defaultBackendName},
})
object.Spec.TLS = append(object.Spec.TLS, extensions.IngressTLS{
object.Spec.TLS = append(object.Spec.TLS, networking.IngressTLS{
Hosts: []string{"*.google.com"},
SecretName: "googleSecret",
})
@ -161,13 +162,13 @@ func TestUpdate(t *testing.T) {
},
// invalid updateFunc: ObjeceMeta is not to be tampered with.
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Ingress)
object := obj.(*networking.Ingress)
object.Name = ""
return object
},
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Ingress)
object := obj.(*networking.Ingress)
object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
"foo.bar.com": {"/invalid[": "svc"}})
return object

View File

@ -24,8 +24,8 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/apis/networking/validation"
)
// ingressStrategy implements verification logic for Replication Ingresss.
@ -44,17 +44,17 @@ func (ingressStrategy) NamespaceScoped() bool {
// PrepareForCreate clears the status of an Ingress before creation.
func (ingressStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
ingress := obj.(*extensions.Ingress)
ingress := obj.(*networking.Ingress)
// create cannot set status
ingress.Status = extensions.IngressStatus{}
ingress.Status = networking.IngressStatus{}
ingress.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (ingressStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIngress := obj.(*extensions.Ingress)
oldIngress := old.(*extensions.Ingress)
newIngress := obj.(*networking.Ingress)
oldIngress := old.(*networking.Ingress)
// Update is not allowed to set status
newIngress.Status = oldIngress.Status
@ -69,7 +69,7 @@ func (ingressStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
// Validate validates a new Ingress.
func (ingressStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
ingress := obj.(*extensions.Ingress)
ingress := obj.(*networking.Ingress)
err := validation.ValidateIngress(ingress)
return err
}
@ -85,8 +85,8 @@ func (ingressStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate is the default update validation for an end user.
func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateIngress(obj.(*extensions.Ingress))
updateErrorList := validation.ValidateIngressUpdate(obj.(*extensions.Ingress), old.(*extensions.Ingress))
validationErrorList := validation.ValidateIngress(obj.(*networking.Ingress))
updateErrorList := validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress))
return append(validationErrorList, updateErrorList...)
}
@ -99,17 +99,18 @@ type ingressStatusStrategy struct {
ingressStrategy
}
// StatusStrategy implements logic used to validate and prepare for updates of the status subresource
var StatusStrategy = ingressStatusStrategy{Strategy}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (ingressStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIngress := obj.(*extensions.Ingress)
oldIngress := old.(*extensions.Ingress)
newIngress := obj.(*networking.Ingress)
oldIngress := old.(*networking.Ingress)
// status changes are not allowed to update spec
newIngress.Spec = oldIngress.Spec
}
// ValidateUpdate is the default update validation for an end user updating status
func (ingressStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateIngressStatusUpdate(obj.(*extensions.Ingress), old.(*extensions.Ingress))
return validation.ValidateIngressStatusUpdate(obj.(*networking.Ingress), old.(*networking.Ingress))
}

View File

@ -23,30 +23,30 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/networking"
)
func newIngress() extensions.Ingress {
defaultBackend := extensions.IngressBackend{
func newIngress() networking.Ingress {
defaultBackend := networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}
return extensions.Ingress{
return networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []extensions.IngressRule{
Rules: []networking.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
@ -57,7 +57,7 @@ func newIngress() extensions.Ingress {
},
},
},
Status: extensions.IngressStatus{
Status: networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.1"},
@ -87,7 +87,7 @@ func TestIngressStrategy(t *testing.T) {
}
invalidIngress := newIngress()
invalidIngress.ResourceVersion = "4"
invalidIngress.Spec = extensions.IngressSpec{}
invalidIngress.Spec = networking.IngressSpec{}
Strategy.PrepareForUpdate(ctx, &invalidIngress, &ingress)
errs = Strategy.ValidateUpdate(ctx, &invalidIngress, &ingress)
if len(errs) == 0 {
@ -111,7 +111,7 @@ func TestIngressStatusStrategy(t *testing.T) {
oldIngress.ResourceVersion = "4"
newIngress.ResourceVersion = "4"
newIngress.Spec.Backend.ServiceName = "ignore"
newIngress.Status = extensions.IngressStatus{
newIngress.Status = networking.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{
Ingress: []api.LoadBalancerIngress{
{IP: "127.0.0.2"},

View File

@ -569,7 +569,6 @@ k8s.io/kubernetes/pkg/apis/batch/validation,erictune,0,
k8s.io/kubernetes/pkg/apis/componentconfig,jbeda,1,
k8s.io/kubernetes/pkg/apis/extensions,bgrant0607,1,
k8s.io/kubernetes/pkg/apis/extensions/v1beta1,madhusudancs,1,
k8s.io/kubernetes/pkg/apis/extensions/validation,nikhiljindal,1,
k8s.io/kubernetes/pkg/apis/policy/validation,deads2k,1,
k8s.io/kubernetes/pkg/apis/rbac/v1alpha1,liggitt,0,
k8s.io/kubernetes/pkg/apis/rbac/validation,erictune,0,
@ -745,8 +744,6 @@ k8s.io/kubernetes/pkg/registry/extensions/daemonset,nikhiljindal,1,
k8s.io/kubernetes/pkg/registry/extensions/daemonset/storage,kevin-wangzefeng,1,
k8s.io/kubernetes/pkg/registry/extensions/deployment,dchen1107,1,
k8s.io/kubernetes/pkg/registry/extensions/deployment/storage,timothysc,1,
k8s.io/kubernetes/pkg/registry/extensions/ingress,apelisse,1,
k8s.io/kubernetes/pkg/registry/extensions/ingress/storage,luxas,1,
k8s.io/kubernetes/pkg/registry/extensions/replicaset,rrati,0,
k8s.io/kubernetes/pkg/registry/extensions/replicaset/storage,wojtek-t,1,
k8s.io/kubernetes/pkg/registry/extensions/rest,rrati,0,

1 name owner auto-assigned sig
569 k8s.io/kubernetes/pkg/apis/componentconfig jbeda 1
570 k8s.io/kubernetes/pkg/apis/extensions bgrant0607 1
571 k8s.io/kubernetes/pkg/apis/extensions/v1beta1 madhusudancs 1
k8s.io/kubernetes/pkg/apis/extensions/validation nikhiljindal 1
572 k8s.io/kubernetes/pkg/apis/policy/validation deads2k 1
573 k8s.io/kubernetes/pkg/apis/rbac/v1alpha1 liggitt 0
574 k8s.io/kubernetes/pkg/apis/rbac/validation erictune 0
744 k8s.io/kubernetes/pkg/registry/extensions/daemonset/storage kevin-wangzefeng 1
745 k8s.io/kubernetes/pkg/registry/extensions/deployment dchen1107 1
746 k8s.io/kubernetes/pkg/registry/extensions/deployment/storage timothysc 1
k8s.io/kubernetes/pkg/registry/extensions/ingress apelisse 1
k8s.io/kubernetes/pkg/registry/extensions/ingress/storage luxas 1
747 k8s.io/kubernetes/pkg/registry/extensions/replicaset rrati 0
748 k8s.io/kubernetes/pkg/registry/extensions/replicaset/storage wojtek-t 1
749 k8s.io/kubernetes/pkg/registry/extensions/rest rrati 0