Merge pull request #14459 from bprashanth/l7_ingress_resource_refactor
Auto commit by PR queue bot
This commit is contained in:
@@ -292,6 +292,7 @@ _kubectl_get()
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("ingress")
|
||||
must_have_one_noun+=("job")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
@@ -462,6 +463,7 @@ _kubectl_delete()
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("ingress")
|
||||
must_have_one_noun+=("job")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
@@ -864,6 +866,7 @@ _kubectl_label()
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("ingress")
|
||||
must_have_one_noun+=("job")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
|
@@ -966,6 +966,28 @@ func deepCopy_experimental_DeploymentStrategy(in DeploymentStrategy, out *Deploy
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_experimental_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error {
|
||||
out.Path = in.Path
|
||||
if err := deepCopy_experimental_IngressBackend(in.Backend, &out.Backend, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_experimental_HTTPIngressRuleValue(in HTTPIngressRuleValue, out *HTTPIngressRuleValue, c *conversion.Cloner) error {
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]HTTPIngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := deepCopy_experimental_HTTPIngressPath(in.Paths[i], &out.Paths[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_experimental_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error {
|
||||
if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
|
||||
return err
|
||||
@@ -1058,13 +1080,10 @@ func deepCopy_experimental_Ingress(in Ingress, out *Ingress, c *conversion.Clone
|
||||
}
|
||||
|
||||
func deepCopy_experimental_IngressBackend(in IngressBackend, out *IngressBackend, c *conversion.Cloner) error {
|
||||
if err := deepCopy_api_LocalObjectReference(in.ServiceRef, &out.ServiceRef, c); err != nil {
|
||||
return err
|
||||
}
|
||||
out.ServiceName = in.ServiceName
|
||||
if err := deepCopy_util_IntOrString(in.ServicePort, &out.ServicePort, c); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Protocol = in.Protocol
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1088,30 +1107,35 @@ func deepCopy_experimental_IngressList(in IngressList, out *IngressList, c *conv
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_experimental_IngressPath(in IngressPath, out *IngressPath, c *conversion.Cloner) error {
|
||||
out.Path = in.Path
|
||||
if err := deepCopy_experimental_IngressBackend(in.Backend, &out.Backend, c); err != nil {
|
||||
func deepCopy_experimental_IngressRule(in IngressRule, out *IngressRule, c *conversion.Cloner) error {
|
||||
out.Host = in.Host
|
||||
if err := deepCopy_experimental_IngressRuleValue(in.IngressRuleValue, &out.IngressRuleValue, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_experimental_IngressRule(in IngressRule, out *IngressRule, c *conversion.Cloner) error {
|
||||
out.Host = in.Host
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]IngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := deepCopy_experimental_IngressPath(in.Paths[i], &out.Paths[i], c); err != nil {
|
||||
func deepCopy_experimental_IngressRuleValue(in IngressRuleValue, out *IngressRuleValue, c *conversion.Cloner) error {
|
||||
if in.HTTP != nil {
|
||||
out.HTTP = new(HTTPIngressRuleValue)
|
||||
if err := deepCopy_experimental_HTTPIngressRuleValue(*in.HTTP, out.HTTP, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
out.HTTP = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_experimental_IngressSpec(in IngressSpec, out *IngressSpec, c *conversion.Cloner) error {
|
||||
if in.Backend != nil {
|
||||
out.Backend = new(IngressBackend)
|
||||
if err := deepCopy_experimental_IngressBackend(*in.Backend, out.Backend, c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Backend = nil
|
||||
}
|
||||
if in.Rules != nil {
|
||||
out.Rules = make([]IngressRule, len(in.Rules))
|
||||
for i := range in.Rules {
|
||||
@@ -1458,6 +1482,8 @@ func init() {
|
||||
deepCopy_experimental_DeploymentSpec,
|
||||
deepCopy_experimental_DeploymentStatus,
|
||||
deepCopy_experimental_DeploymentStrategy,
|
||||
deepCopy_experimental_HTTPIngressPath,
|
||||
deepCopy_experimental_HTTPIngressRuleValue,
|
||||
deepCopy_experimental_HorizontalPodAutoscaler,
|
||||
deepCopy_experimental_HorizontalPodAutoscalerList,
|
||||
deepCopy_experimental_HorizontalPodAutoscalerSpec,
|
||||
@@ -1465,8 +1491,8 @@ func init() {
|
||||
deepCopy_experimental_Ingress,
|
||||
deepCopy_experimental_IngressBackend,
|
||||
deepCopy_experimental_IngressList,
|
||||
deepCopy_experimental_IngressPath,
|
||||
deepCopy_experimental_IngressRule,
|
||||
deepCopy_experimental_IngressRuleValue,
|
||||
deepCopy_experimental_IngressSpec,
|
||||
deepCopy_experimental_IngressStatus,
|
||||
deepCopy_experimental_Job,
|
||||
|
@@ -464,9 +464,10 @@ type JobCondition struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// An Ingress is a way to give services externally-reachable urls. Each Ingress is a
|
||||
// collection of rules that allow inbound connections to reach the endpoints defined by
|
||||
// a backend.
|
||||
// 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 {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
@@ -495,11 +496,13 @@ type IngressList struct {
|
||||
|
||||
// IngressSpec describes the Ingress the user wishes to exist.
|
||||
type IngressSpec struct {
|
||||
// TODO: Add the ability to specify load-balancer IP just like what Service has already done?
|
||||
// A list of rules used to configure the Ingress.
|
||||
// http://<host>:<port>/<path>?<searchpart> -> IngressBackend
|
||||
// Where parts of the url conform to RFC 1738.
|
||||
// A default backend capable of servicing requests that don't match any
|
||||
// IngressRule. It is optional to allow the loadbalancer controller or
|
||||
// defaulting logic to specify a global default.
|
||||
Backend *IngressBackend `json:"backend,omitempty"`
|
||||
// A list of host rules used to configure the Ingress.
|
||||
Rules []IngressRule `json:"rules"`
|
||||
// TODO: Add the ability to specify load-balancer IP through claims
|
||||
}
|
||||
|
||||
// IngressStatus describe the current state of the Ingress.
|
||||
@@ -508,35 +511,71 @@ type IngressStatus struct {
|
||||
LoadBalancer api.LoadBalancerStatus `json:"loadBalancer,omitempty"`
|
||||
}
|
||||
|
||||
// IngressRule represents the rules mapping the paths under a specified host to the related backend services.
|
||||
// IngressRule represents the rules mapping the paths under a specified host to
|
||||
// the related backend services.
|
||||
type IngressRule struct {
|
||||
// Host is the fully qualified domain name of a network host, or its IP
|
||||
// address as a set of four decimal digit groups separated by ".".
|
||||
// Conforms to RFC 1738.
|
||||
// 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.
|
||||
Host string `json:"host,omitempty"`
|
||||
// IngressRuleValue represents a rule to route requests for this IngressRule.
|
||||
IngressRuleValue `json:",inline"`
|
||||
}
|
||||
|
||||
// Paths describe a list of load-balancer rules under the specified host.
|
||||
Paths []IngressPath `json:"paths"`
|
||||
// IngressRuleValue represents a rule to apply against incoming requests. If the
|
||||
// rule is satisfied, the request is routed to the specified backend.
|
||||
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.
|
||||
|
||||
// Currently mixing different types of rules in a single Ingress is
|
||||
// disallowed, so exactly one of the following must be set.
|
||||
HTTP *HTTPIngressRuleValue `json:"http"`
|
||||
}
|
||||
|
||||
// HTTPIngressRuleValue is a list of http selectors pointing to IngressBackends.
|
||||
// In the example: http://<host>/<path>?<searchpart> -> IngressBackend 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 IngressBackends.
|
||||
Paths []HTTPIngressPath `json:"paths"`
|
||||
// TODO: Consider adding fields for ingress-type specific global
|
||||
// options usable by a loadbalancer, like http keep-alive.
|
||||
}
|
||||
|
||||
// IngressPath associates a path regex with an IngressBackend.
|
||||
// Incoming urls matching the Path are forwarded to the Backend.
|
||||
type IngressPath struct {
|
||||
// Path is a regex matched against the url of an incoming request.
|
||||
type HTTPIngressPath struct {
|
||||
// Path is a 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 '/'.
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// Define the referenced service endpoint which the traffic will be forwarded to.
|
||||
// Define the referenced service endpoint which the traffic will be
|
||||
// forwarded to.
|
||||
Backend IngressBackend `json:"backend"`
|
||||
}
|
||||
|
||||
// IngressBackend describes all endpoints for a given Service, port and protocol.
|
||||
// IngressBackend describes all endpoints for a given Service and port.
|
||||
type IngressBackend struct {
|
||||
// Specifies the referenced service.
|
||||
ServiceRef api.LocalObjectReference `json:"serviceRef"`
|
||||
// Specifies the name of the referenced service.
|
||||
ServiceName string `json:"serviceName"`
|
||||
|
||||
// Specifies the port of the referenced service.
|
||||
ServicePort util.IntOrString `json:"servicePort,omitempty"`
|
||||
|
||||
// Specifies the protocol of the referenced service.
|
||||
Protocol api.Protocol `json:"protocol,omitempty"`
|
||||
ServicePort util.IntOrString `json:"servicePort"`
|
||||
}
|
||||
|
@@ -2296,6 +2296,42 @@ func autoconvert_experimental_DeploymentStrategy_To_v1alpha1_DeploymentStrategy(
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoconvert_experimental_HTTPIngressPath_To_v1alpha1_HTTPIngressPath(in *experimental.HTTPIngressPath, out *HTTPIngressPath, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.HTTPIngressPath))(in)
|
||||
}
|
||||
out.Path = in.Path
|
||||
if err := convert_experimental_IngressBackend_To_v1alpha1_IngressBackend(&in.Backend, &out.Backend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_experimental_HTTPIngressPath_To_v1alpha1_HTTPIngressPath(in *experimental.HTTPIngressPath, out *HTTPIngressPath, s conversion.Scope) error {
|
||||
return autoconvert_experimental_HTTPIngressPath_To_v1alpha1_HTTPIngressPath(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_experimental_HTTPIngressRuleValue_To_v1alpha1_HTTPIngressRuleValue(in *experimental.HTTPIngressRuleValue, out *HTTPIngressRuleValue, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.HTTPIngressRuleValue))(in)
|
||||
}
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]HTTPIngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := convert_experimental_HTTPIngressPath_To_v1alpha1_HTTPIngressPath(&in.Paths[i], &out.Paths[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_experimental_HTTPIngressRuleValue_To_v1alpha1_HTTPIngressRuleValue(in *experimental.HTTPIngressRuleValue, out *HTTPIngressRuleValue, s conversion.Scope) error {
|
||||
return autoconvert_experimental_HTTPIngressRuleValue_To_v1alpha1_HTTPIngressRuleValue(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_experimental_HorizontalPodAutoscaler_To_v1alpha1_HorizontalPodAutoscaler(in *experimental.HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.HorizontalPodAutoscaler))(in)
|
||||
@@ -2425,13 +2461,10 @@ func autoconvert_experimental_IngressBackend_To_v1alpha1_IngressBackend(in *expe
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.IngressBackend))(in)
|
||||
}
|
||||
if err := convert_api_LocalObjectReference_To_v1_LocalObjectReference(&in.ServiceRef, &out.ServiceRef, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.ServiceName = in.ServiceName
|
||||
if err := s.Convert(&in.ServicePort, &out.ServicePort, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Protocol = v1.Protocol(in.Protocol)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2466,36 +2499,14 @@ func convert_experimental_IngressList_To_v1alpha1_IngressList(in *experimental.I
|
||||
return autoconvert_experimental_IngressList_To_v1alpha1_IngressList(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_experimental_IngressPath_To_v1alpha1_IngressPath(in *experimental.IngressPath, out *IngressPath, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.IngressPath))(in)
|
||||
}
|
||||
out.Path = in.Path
|
||||
if err := convert_experimental_IngressBackend_To_v1alpha1_IngressBackend(&in.Backend, &out.Backend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_experimental_IngressPath_To_v1alpha1_IngressPath(in *experimental.IngressPath, out *IngressPath, s conversion.Scope) error {
|
||||
return autoconvert_experimental_IngressPath_To_v1alpha1_IngressPath(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_experimental_IngressRule_To_v1alpha1_IngressRule(in *experimental.IngressRule, out *IngressRule, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.IngressRule))(in)
|
||||
}
|
||||
out.Host = in.Host
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]IngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := convert_experimental_IngressPath_To_v1alpha1_IngressPath(&in.Paths[i], &out.Paths[i], s); err != nil {
|
||||
if err := convert_experimental_IngressRuleValue_To_v1alpha1_IngressRuleValue(&in.IngressRuleValue, &out.IngressRuleValue, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2503,10 +2514,37 @@ func convert_experimental_IngressRule_To_v1alpha1_IngressRule(in *experimental.I
|
||||
return autoconvert_experimental_IngressRule_To_v1alpha1_IngressRule(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_experimental_IngressRuleValue_To_v1alpha1_IngressRuleValue(in *experimental.IngressRuleValue, out *IngressRuleValue, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.IngressRuleValue))(in)
|
||||
}
|
||||
if in.HTTP != nil {
|
||||
out.HTTP = new(HTTPIngressRuleValue)
|
||||
if err := convert_experimental_HTTPIngressRuleValue_To_v1alpha1_HTTPIngressRuleValue(in.HTTP, out.HTTP, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.HTTP = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_experimental_IngressRuleValue_To_v1alpha1_IngressRuleValue(in *experimental.IngressRuleValue, out *IngressRuleValue, s conversion.Scope) error {
|
||||
return autoconvert_experimental_IngressRuleValue_To_v1alpha1_IngressRuleValue(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_experimental_IngressSpec_To_v1alpha1_IngressSpec(in *experimental.IngressSpec, out *IngressSpec, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*experimental.IngressSpec))(in)
|
||||
}
|
||||
if in.Backend != nil {
|
||||
out.Backend = new(IngressBackend)
|
||||
if err := convert_experimental_IngressBackend_To_v1alpha1_IngressBackend(in.Backend, out.Backend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Backend = nil
|
||||
}
|
||||
if in.Rules != nil {
|
||||
out.Rules = make([]IngressRule, len(in.Rules))
|
||||
for i := range in.Rules {
|
||||
@@ -3097,6 +3135,42 @@ func convert_v1alpha1_DeploymentStatus_To_experimental_DeploymentStatus(in *Depl
|
||||
return autoconvert_v1alpha1_DeploymentStatus_To_experimental_DeploymentStatus(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_HTTPIngressPath_To_experimental_HTTPIngressPath(in *HTTPIngressPath, out *experimental.HTTPIngressPath, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*HTTPIngressPath))(in)
|
||||
}
|
||||
out.Path = in.Path
|
||||
if err := convert_v1alpha1_IngressBackend_To_experimental_IngressBackend(&in.Backend, &out.Backend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_v1alpha1_HTTPIngressPath_To_experimental_HTTPIngressPath(in *HTTPIngressPath, out *experimental.HTTPIngressPath, s conversion.Scope) error {
|
||||
return autoconvert_v1alpha1_HTTPIngressPath_To_experimental_HTTPIngressPath(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_HTTPIngressRuleValue_To_experimental_HTTPIngressRuleValue(in *HTTPIngressRuleValue, out *experimental.HTTPIngressRuleValue, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*HTTPIngressRuleValue))(in)
|
||||
}
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]experimental.HTTPIngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := convert_v1alpha1_HTTPIngressPath_To_experimental_HTTPIngressPath(&in.Paths[i], &out.Paths[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_v1alpha1_HTTPIngressRuleValue_To_experimental_HTTPIngressRuleValue(in *HTTPIngressRuleValue, out *experimental.HTTPIngressRuleValue, s conversion.Scope) error {
|
||||
return autoconvert_v1alpha1_HTTPIngressRuleValue_To_experimental_HTTPIngressRuleValue(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_HorizontalPodAutoscaler_To_experimental_HorizontalPodAutoscaler(in *HorizontalPodAutoscaler, out *experimental.HorizontalPodAutoscaler, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*HorizontalPodAutoscaler))(in)
|
||||
@@ -3226,13 +3300,10 @@ func autoconvert_v1alpha1_IngressBackend_To_experimental_IngressBackend(in *Ingr
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*IngressBackend))(in)
|
||||
}
|
||||
if err := convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.ServiceRef, &out.ServiceRef, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.ServiceName = in.ServiceName
|
||||
if err := s.Convert(&in.ServicePort, &out.ServicePort, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Protocol = api.Protocol(in.Protocol)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3267,36 +3338,14 @@ func convert_v1alpha1_IngressList_To_experimental_IngressList(in *IngressList, o
|
||||
return autoconvert_v1alpha1_IngressList_To_experimental_IngressList(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_IngressPath_To_experimental_IngressPath(in *IngressPath, out *experimental.IngressPath, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*IngressPath))(in)
|
||||
}
|
||||
out.Path = in.Path
|
||||
if err := convert_v1alpha1_IngressBackend_To_experimental_IngressBackend(&in.Backend, &out.Backend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_v1alpha1_IngressPath_To_experimental_IngressPath(in *IngressPath, out *experimental.IngressPath, s conversion.Scope) error {
|
||||
return autoconvert_v1alpha1_IngressPath_To_experimental_IngressPath(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_IngressRule_To_experimental_IngressRule(in *IngressRule, out *experimental.IngressRule, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*IngressRule))(in)
|
||||
}
|
||||
out.Host = in.Host
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]experimental.IngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := convert_v1alpha1_IngressPath_To_experimental_IngressPath(&in.Paths[i], &out.Paths[i], s); err != nil {
|
||||
if err := convert_v1alpha1_IngressRuleValue_To_experimental_IngressRuleValue(&in.IngressRuleValue, &out.IngressRuleValue, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3304,10 +3353,37 @@ func convert_v1alpha1_IngressRule_To_experimental_IngressRule(in *IngressRule, o
|
||||
return autoconvert_v1alpha1_IngressRule_To_experimental_IngressRule(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_IngressRuleValue_To_experimental_IngressRuleValue(in *IngressRuleValue, out *experimental.IngressRuleValue, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*IngressRuleValue))(in)
|
||||
}
|
||||
if in.HTTP != nil {
|
||||
out.HTTP = new(experimental.HTTPIngressRuleValue)
|
||||
if err := convert_v1alpha1_HTTPIngressRuleValue_To_experimental_HTTPIngressRuleValue(in.HTTP, out.HTTP, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.HTTP = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_v1alpha1_IngressRuleValue_To_experimental_IngressRuleValue(in *IngressRuleValue, out *experimental.IngressRuleValue, s conversion.Scope) error {
|
||||
return autoconvert_v1alpha1_IngressRuleValue_To_experimental_IngressRuleValue(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1alpha1_IngressSpec_To_experimental_IngressSpec(in *IngressSpec, out *experimental.IngressSpec, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*IngressSpec))(in)
|
||||
}
|
||||
if in.Backend != nil {
|
||||
out.Backend = new(experimental.IngressBackend)
|
||||
if err := convert_v1alpha1_IngressBackend_To_experimental_IngressBackend(in.Backend, out.Backend, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Backend = nil
|
||||
}
|
||||
if in.Rules != nil {
|
||||
out.Rules = make([]experimental.IngressRule, len(in.Rules))
|
||||
for i := range in.Rules {
|
||||
@@ -3751,13 +3827,15 @@ func init() {
|
||||
autoconvert_experimental_DeploymentStatus_To_v1alpha1_DeploymentStatus,
|
||||
autoconvert_experimental_DeploymentStrategy_To_v1alpha1_DeploymentStrategy,
|
||||
autoconvert_experimental_Deployment_To_v1alpha1_Deployment,
|
||||
autoconvert_experimental_HTTPIngressPath_To_v1alpha1_HTTPIngressPath,
|
||||
autoconvert_experimental_HTTPIngressRuleValue_To_v1alpha1_HTTPIngressRuleValue,
|
||||
autoconvert_experimental_HorizontalPodAutoscalerList_To_v1alpha1_HorizontalPodAutoscalerList,
|
||||
autoconvert_experimental_HorizontalPodAutoscalerSpec_To_v1alpha1_HorizontalPodAutoscalerSpec,
|
||||
autoconvert_experimental_HorizontalPodAutoscalerStatus_To_v1alpha1_HorizontalPodAutoscalerStatus,
|
||||
autoconvert_experimental_HorizontalPodAutoscaler_To_v1alpha1_HorizontalPodAutoscaler,
|
||||
autoconvert_experimental_IngressBackend_To_v1alpha1_IngressBackend,
|
||||
autoconvert_experimental_IngressList_To_v1alpha1_IngressList,
|
||||
autoconvert_experimental_IngressPath_To_v1alpha1_IngressPath,
|
||||
autoconvert_experimental_IngressRuleValue_To_v1alpha1_IngressRuleValue,
|
||||
autoconvert_experimental_IngressRule_To_v1alpha1_IngressRule,
|
||||
autoconvert_experimental_IngressSpec_To_v1alpha1_IngressSpec,
|
||||
autoconvert_experimental_IngressStatus_To_v1alpha1_IngressStatus,
|
||||
@@ -3827,13 +3905,15 @@ func init() {
|
||||
autoconvert_v1alpha1_DeploymentSpec_To_experimental_DeploymentSpec,
|
||||
autoconvert_v1alpha1_DeploymentStatus_To_experimental_DeploymentStatus,
|
||||
autoconvert_v1alpha1_Deployment_To_experimental_Deployment,
|
||||
autoconvert_v1alpha1_HTTPIngressPath_To_experimental_HTTPIngressPath,
|
||||
autoconvert_v1alpha1_HTTPIngressRuleValue_To_experimental_HTTPIngressRuleValue,
|
||||
autoconvert_v1alpha1_HorizontalPodAutoscalerList_To_experimental_HorizontalPodAutoscalerList,
|
||||
autoconvert_v1alpha1_HorizontalPodAutoscalerSpec_To_experimental_HorizontalPodAutoscalerSpec,
|
||||
autoconvert_v1alpha1_HorizontalPodAutoscalerStatus_To_experimental_HorizontalPodAutoscalerStatus,
|
||||
autoconvert_v1alpha1_HorizontalPodAutoscaler_To_experimental_HorizontalPodAutoscaler,
|
||||
autoconvert_v1alpha1_IngressBackend_To_experimental_IngressBackend,
|
||||
autoconvert_v1alpha1_IngressList_To_experimental_IngressList,
|
||||
autoconvert_v1alpha1_IngressPath_To_experimental_IngressPath,
|
||||
autoconvert_v1alpha1_IngressRuleValue_To_experimental_IngressRuleValue,
|
||||
autoconvert_v1alpha1_IngressRule_To_experimental_IngressRule,
|
||||
autoconvert_v1alpha1_IngressSpec_To_experimental_IngressSpec,
|
||||
autoconvert_v1alpha1_IngressStatus_To_experimental_IngressStatus,
|
||||
|
@@ -978,6 +978,28 @@ func deepCopy_v1alpha1_DeploymentStrategy(in DeploymentStrategy, out *Deployment
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error {
|
||||
out.Path = in.Path
|
||||
if err := deepCopy_v1alpha1_IngressBackend(in.Backend, &out.Backend, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_HTTPIngressRuleValue(in HTTPIngressRuleValue, out *HTTPIngressRuleValue, c *conversion.Cloner) error {
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]HTTPIngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := deepCopy_v1alpha1_HTTPIngressPath(in.Paths[i], &out.Paths[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error {
|
||||
if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
|
||||
return err
|
||||
@@ -1070,13 +1092,10 @@ func deepCopy_v1alpha1_Ingress(in Ingress, out *Ingress, c *conversion.Cloner) e
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_IngressBackend(in IngressBackend, out *IngressBackend, c *conversion.Cloner) error {
|
||||
if err := deepCopy_v1_LocalObjectReference(in.ServiceRef, &out.ServiceRef, c); err != nil {
|
||||
return err
|
||||
}
|
||||
out.ServiceName = in.ServiceName
|
||||
if err := deepCopy_util_IntOrString(in.ServicePort, &out.ServicePort, c); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Protocol = in.Protocol
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1100,30 +1119,35 @@ func deepCopy_v1alpha1_IngressList(in IngressList, out *IngressList, c *conversi
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_IngressPath(in IngressPath, out *IngressPath, c *conversion.Cloner) error {
|
||||
out.Path = in.Path
|
||||
if err := deepCopy_v1alpha1_IngressBackend(in.Backend, &out.Backend, c); err != nil {
|
||||
func deepCopy_v1alpha1_IngressRule(in IngressRule, out *IngressRule, c *conversion.Cloner) error {
|
||||
out.Host = in.Host
|
||||
if err := deepCopy_v1alpha1_IngressRuleValue(in.IngressRuleValue, &out.IngressRuleValue, c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_IngressRule(in IngressRule, out *IngressRule, c *conversion.Cloner) error {
|
||||
out.Host = in.Host
|
||||
if in.Paths != nil {
|
||||
out.Paths = make([]IngressPath, len(in.Paths))
|
||||
for i := range in.Paths {
|
||||
if err := deepCopy_v1alpha1_IngressPath(in.Paths[i], &out.Paths[i], c); err != nil {
|
||||
func deepCopy_v1alpha1_IngressRuleValue(in IngressRuleValue, out *IngressRuleValue, c *conversion.Cloner) error {
|
||||
if in.HTTP != nil {
|
||||
out.HTTP = new(HTTPIngressRuleValue)
|
||||
if err := deepCopy_v1alpha1_HTTPIngressRuleValue(*in.HTTP, out.HTTP, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Paths = nil
|
||||
out.HTTP = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1alpha1_IngressSpec(in IngressSpec, out *IngressSpec, c *conversion.Cloner) error {
|
||||
if in.Backend != nil {
|
||||
out.Backend = new(IngressBackend)
|
||||
if err := deepCopy_v1alpha1_IngressBackend(*in.Backend, out.Backend, c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Backend = nil
|
||||
}
|
||||
if in.Rules != nil {
|
||||
out.Rules = make([]IngressRule, len(in.Rules))
|
||||
for i := range in.Rules {
|
||||
@@ -1480,6 +1504,8 @@ func init() {
|
||||
deepCopy_v1alpha1_DeploymentSpec,
|
||||
deepCopy_v1alpha1_DeploymentStatus,
|
||||
deepCopy_v1alpha1_DeploymentStrategy,
|
||||
deepCopy_v1alpha1_HTTPIngressPath,
|
||||
deepCopy_v1alpha1_HTTPIngressRuleValue,
|
||||
deepCopy_v1alpha1_HorizontalPodAutoscaler,
|
||||
deepCopy_v1alpha1_HorizontalPodAutoscalerList,
|
||||
deepCopy_v1alpha1_HorizontalPodAutoscalerSpec,
|
||||
@@ -1487,8 +1513,8 @@ func init() {
|
||||
deepCopy_v1alpha1_Ingress,
|
||||
deepCopy_v1alpha1_IngressBackend,
|
||||
deepCopy_v1alpha1_IngressList,
|
||||
deepCopy_v1alpha1_IngressPath,
|
||||
deepCopy_v1alpha1_IngressRule,
|
||||
deepCopy_v1alpha1_IngressRuleValue,
|
||||
deepCopy_v1alpha1_IngressSpec,
|
||||
deepCopy_v1alpha1_IngressStatus,
|
||||
deepCopy_v1alpha1_Job,
|
||||
|
@@ -473,9 +473,10 @@ type JobCondition struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// An Ingress is a way to give services externally-reachable urls. Each Ingress is a
|
||||
// collection of rules that allow inbound connections to reach the endpoints defined by
|
||||
// a backend.
|
||||
// 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 {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
@@ -504,11 +505,13 @@ type IngressList struct {
|
||||
|
||||
// IngressSpec describes the Ingress the user wishes to exist.
|
||||
type IngressSpec struct {
|
||||
// TODO: Add the ability to specify load-balancer IP just like what Service has already done?
|
||||
// A list of rules used to configure the Ingress.
|
||||
// http://<host>:<port>/<path>?<searchpart> -> IngressBackend
|
||||
// Where parts of the url conform to RFC 1738.
|
||||
// A default backend capable of servicing requests that don't match any
|
||||
// IngressRule. It is optional to allow the loadbalancer controller or
|
||||
// defaulting logic to specify a global default.
|
||||
Backend *IngressBackend `json:"backend,omitempty"`
|
||||
// A list of host rules used to configure the Ingress.
|
||||
Rules []IngressRule `json:"rules"`
|
||||
// TODO: Add the ability to specify load-balancer IP through claims
|
||||
}
|
||||
|
||||
// IngressStatus describe the current state of the Ingress.
|
||||
@@ -517,35 +520,64 @@ type IngressStatus struct {
|
||||
LoadBalancer v1.LoadBalancerStatus `json:"loadBalancer,omitempty"`
|
||||
}
|
||||
|
||||
// IngressRule represents the rules mapping the paths under a specified host to the related backend services.
|
||||
// IngressRule represents the rules mapping the paths under a specified host to
|
||||
// the related backend services.
|
||||
type IngressRule struct {
|
||||
// Host is the fully qualified domain name of a network host, or its IP
|
||||
// address as a set of four decimal digit groups separated by ".".
|
||||
// Conforms to RFC 1738.
|
||||
// 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.
|
||||
Host string `json:"host,omitempty"`
|
||||
// IngressRuleValue represents a rule to route requests for this IngressRule.
|
||||
IngressRuleValue `json:",inline"`
|
||||
}
|
||||
|
||||
// Paths describe a list of load-balancer rules under the specified host.
|
||||
Paths []IngressPath `json:"paths"`
|
||||
// IngressRuleValue represents a rule to apply against incoming requests. If the
|
||||
// rule is satisfied, the request is routed to the specified backend.
|
||||
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.
|
||||
|
||||
// Currently mixing different types of rules in a single Ingress is
|
||||
// disallowed, so exactly one of the following must be set.
|
||||
HTTP *HTTPIngressRuleValue `json:"http"`
|
||||
}
|
||||
|
||||
// HTTPIngressRuleValue is a list of http selectors pointing to IngressBackends.
|
||||
// In the example: http://<host>/<path>?<searchpart> -> IngressBackend where
|
||||
// parts of the url correspond to RFC 3986, this resource will be used to
|
||||
// to match against everything after the last '/' and before the first '?'
|
||||
// or '#'.
|
||||
type HTTPIngressRuleValue struct {
|
||||
// A collection of paths that map requests to IngressBackends.
|
||||
Paths []HTTPIngressPath `json:"paths"`
|
||||
}
|
||||
|
||||
// IngressPath associates a path regex with an IngressBackend.
|
||||
// Incoming urls matching the Path are forwarded to the Backend.
|
||||
type IngressPath struct {
|
||||
type HTTPIngressPath struct {
|
||||
// Path is a regex matched against the url of an incoming request.
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// Define the referenced service endpoint which the traffic will be forwarded to.
|
||||
// Define the referenced service endpoint which the traffic will be
|
||||
// forwarded to.
|
||||
Backend IngressBackend `json:"backend"`
|
||||
}
|
||||
|
||||
// IngressBackend describes all endpoints for a given Service, port and protocol.
|
||||
// IngressBackend describes all endpoints for a given Service and port.
|
||||
type IngressBackend struct {
|
||||
// Specifies the referenced service.
|
||||
ServiceRef v1.LocalObjectReference `json:"serviceRef"`
|
||||
// Specifies the name of the referenced service.
|
||||
ServiceName string `json:"serviceName"`
|
||||
|
||||
// Specifies the port of the referenced service.
|
||||
ServicePort util.IntOrString `json:"servicePort,omitempty"`
|
||||
|
||||
// Specifies the protocol of the referenced service.
|
||||
Protocol v1.Protocol `json:"protocol,omitempty"`
|
||||
ServicePort util.IntOrString `json:"servicePort"`
|
||||
}
|
||||
|
@@ -133,6 +133,25 @@ func (DeploymentStrategy) SwaggerDoc() map[string]string {
|
||||
return map_DeploymentStrategy
|
||||
}
|
||||
|
||||
var map_HTTPIngressPath = map[string]string{
|
||||
"": "IngressPath associates a path regex with an IngressBackend. Incoming urls matching the Path are forwarded to the Backend.",
|
||||
"path": "Path is a regex matched against the url of an incoming request.",
|
||||
"backend": "Define the referenced service endpoint which the traffic will be forwarded to.",
|
||||
}
|
||||
|
||||
func (HTTPIngressPath) SwaggerDoc() map[string]string {
|
||||
return map_HTTPIngressPath
|
||||
}
|
||||
|
||||
var map_HTTPIngressRuleValue = map[string]string{
|
||||
"": "HTTPIngressRuleValue is a list of http selectors pointing to IngressBackends. In the example: http://<host>/<path>?<searchpart> -> IngressBackend where parts of the url correspond to RFC 3986, this resource will be used to to match against everything after the last '/' and before the first '?' or '#'.",
|
||||
"paths": "A collection of paths that map requests to IngressBackends.",
|
||||
}
|
||||
|
||||
func (HTTPIngressRuleValue) SwaggerDoc() map[string]string {
|
||||
return map_HTTPIngressRuleValue
|
||||
}
|
||||
|
||||
var map_HorizontalPodAutoscaler = map[string]string{
|
||||
"": "HorizontalPodAutoscaler represents the configuration of a horizontal pod autoscaler.",
|
||||
"metadata": "Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
|
||||
@@ -179,7 +198,7 @@ func (HorizontalPodAutoscalerStatus) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_Ingress = map[string]string{
|
||||
"": "An Ingress is a way to give services externally-reachable urls. Each Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend.",
|
||||
"": "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.",
|
||||
"metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
|
||||
"spec": "Spec is the desired state of the Ingress. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
|
||||
"status": "Status is the current state of the Ingress. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
|
||||
@@ -190,10 +209,9 @@ func (Ingress) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_IngressBackend = map[string]string{
|
||||
"": "IngressBackend describes all endpoints for a given Service, port and protocol.",
|
||||
"serviceRef": "Specifies the referenced service.",
|
||||
"": "IngressBackend describes all endpoints for a given Service and port.",
|
||||
"serviceName": "Specifies the name of the referenced service.",
|
||||
"servicePort": "Specifies the port of the referenced service.",
|
||||
"protocol": "Specifies the protocol of the referenced service.",
|
||||
}
|
||||
|
||||
func (IngressBackend) SwaggerDoc() map[string]string {
|
||||
@@ -210,29 +228,28 @@ func (IngressList) SwaggerDoc() map[string]string {
|
||||
return map_IngressList
|
||||
}
|
||||
|
||||
var map_IngressPath = map[string]string{
|
||||
"": "IngressPath associates a path regex with an IngressBackend. Incoming urls matching the Path are forwarded to the Backend.",
|
||||
"path": "Path is a regex matched against the url of an incoming request.",
|
||||
"backend": "Define the referenced service endpoint which the traffic will be forwarded to.",
|
||||
}
|
||||
|
||||
func (IngressPath) SwaggerDoc() map[string]string {
|
||||
return map_IngressPath
|
||||
}
|
||||
|
||||
var map_IngressRule = map[string]string{
|
||||
"": "IngressRule represents the rules mapping the paths under a specified host to the related backend services.",
|
||||
"host": "Host is the fully qualified domain name of a network host, or its IP address as a set of four decimal digit groups separated by \".\". Conforms to RFC 1738.",
|
||||
"paths": "Paths describe a list of load-balancer rules under the specified host.",
|
||||
"host": "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\n\t IP in the Spec of the parent Ingress.\n2. The `:` delimiter is not respected because ports are not allowed.\n\t Currently the port of an Ingress is implicitly :80 for http and\n\t :443 for https.\nBoth these may change in the future. Incoming requests are matched against the Host before the IngressRuleValue.",
|
||||
}
|
||||
|
||||
func (IngressRule) SwaggerDoc() map[string]string {
|
||||
return map_IngressRule
|
||||
}
|
||||
|
||||
var map_IngressRuleValue = map[string]string{
|
||||
"": "IngressRuleValue represents a rule to apply against incoming requests. If the rule is satisfied, the request is routed to the specified backend.",
|
||||
"http": "Currently mixing different types of rules in a single Ingress is disallowed, so exactly one of the following must be set.",
|
||||
}
|
||||
|
||||
func (IngressRuleValue) SwaggerDoc() map[string]string {
|
||||
return map_IngressRuleValue
|
||||
}
|
||||
|
||||
var map_IngressSpec = map[string]string{
|
||||
"": "IngressSpec describes the Ingress the user wishes to exist.",
|
||||
"rules": "A list of rules used to configure the Ingress. http://<host>:<port>/<path>?<searchpart> -> IngressBackend Where parts of the url conform to RFC 1738.",
|
||||
"backend": "A default backend capable of servicing requests that don't match any IngressRule. It is optional to allow the loadbalancer controller or defaulting logic to specify a global default.",
|
||||
"rules": "A list of host rules used to configure the Ingress.",
|
||||
}
|
||||
|
||||
func (IngressSpec) SwaggerDoc() map[string]string {
|
||||
|
@@ -17,7 +17,11 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
apivalidation "k8s.io/kubernetes/pkg/api/validation"
|
||||
@@ -27,10 +31,19 @@ import (
|
||||
errs "k8s.io/kubernetes/pkg/util/fielderrors"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
utilvalidation "k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
const isNegativeErrorMsg string = `must be non-negative`
|
||||
|
||||
// TODO: Expose from apivalidation instead of duplicating.
|
||||
func intervalErrorMsg(lo, hi int) string {
|
||||
return fmt.Sprintf(`must be greater than %d and less than %d`, lo, hi)
|
||||
}
|
||||
|
||||
var portRangeErrorMsg string = intervalErrorMsg(0, 65536)
|
||||
var portNameErrorMsg string = fmt.Sprintf(`must be an IANA_SVC_NAME (at most 15 characters, matching regex %s, it must contain at least one letter [a-z], and hyphens cannot be adjacent to other hyphens): e.g. "http"`, validation.IdentifierNoHyphensBeginEndFmt)
|
||||
|
||||
// ValidateHorizontalPodAutoscaler can be used to check whether the given autoscaler name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case trailing dashes are allowed.
|
||||
func ValidateHorizontalPodAutoscalerName(name string, prefix bool) (bool, string) {
|
||||
@@ -371,3 +384,120 @@ func ValidateJobStatusUpdate(oldStatus, status experimental.JobStatus) errs.Vali
|
||||
allErrs = append(allErrs, ValidateJobStatus(&status)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateIngress tests if required fields in the Ingress are set.
|
||||
func ValidateIngress(ingress *experimental.Ingress) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec).Prefix("spec")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateIngressName validates that the given name can be used as an Ingress name.
|
||||
func ValidateIngressName(name string, prefix bool) (bool, string) {
|
||||
return apivalidation.NameIsDNSSubdomain(name, prefix)
|
||||
}
|
||||
|
||||
// ValidateIngressSpec tests if required fields in the IngressSpec are set.
|
||||
func ValidateIngressSpec(spec *experimental.IngressSpec) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
// TODO: Is a default backend mandatory?
|
||||
if spec.Backend != nil {
|
||||
allErrs = append(allErrs, validateIngressBackend(spec.Backend).Prefix("backend")...)
|
||||
} else if len(spec.Rules) == 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("rules", spec.Rules, "Either a default backend or a set of host rules are required for ingress."))
|
||||
}
|
||||
if len(spec.Rules) > 0 {
|
||||
allErrs = append(allErrs, validateIngressRules(spec.Rules).Prefix("rules")...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateIngressUpdate tests if required fields in the Ingress are set.
|
||||
func ValidateIngressUpdate(oldIngress, ingress *experimental.Ingress) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta).Prefix("metadata")...)
|
||||
allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec).Prefix("spec")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateIngressRules(IngressRules []experimental.IngressRule) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
if len(IngressRules) == 0 {
|
||||
return append(allErrs, errs.NewFieldRequired("IngressRules"))
|
||||
}
|
||||
for _, ih := range IngressRules {
|
||||
if len(ih.Host) > 0 {
|
||||
// TODO: Ports and ips are allowed in the host part of a url
|
||||
// according to RFC 3986, consider allowing them.
|
||||
if valid, errMsg := apivalidation.NameIsDNSSubdomain(ih.Host, false); !valid {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("host", ih.Host, errMsg))
|
||||
}
|
||||
if isIP := (net.ParseIP(ih.Host) != nil); isIP {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("host", ih.Host, "Host must be a DNS name, not ip address"))
|
||||
}
|
||||
}
|
||||
allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue).Prefix("ingressRule")...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateIngressRuleValue(ingressRule *experimental.IngressRuleValue) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
if ingressRule.HTTP != nil {
|
||||
allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP).Prefix("http")...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateHTTPIngressRuleValue(httpIngressRuleValue *experimental.HTTPIngressRuleValue) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
if len(httpIngressRuleValue.Paths) == 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("paths"))
|
||||
}
|
||||
for _, rule := range httpIngressRuleValue.Paths {
|
||||
if len(rule.Path) > 0 {
|
||||
if !strings.HasPrefix(rule.Path, "/") {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("path", rule.Path, "path must begin with /"))
|
||||
}
|
||||
// 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, errs.NewFieldInvalid("path", rule.Path, "httpIngressRuleValue.path must be a valid regex."))
|
||||
}
|
||||
}
|
||||
allErrs = append(allErrs, validateIngressBackend(&rule.Backend).Prefix("backend")...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateIngressBackend tests if a given backend is valid.
|
||||
func validateIngressBackend(backend *experimental.IngressBackend) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
// 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, errs.NewFieldRequired("serviceName"))
|
||||
} else if ok, errMsg := apivalidation.ValidateServiceName(backend.ServiceName, false); !ok {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("serviceName", backend.ServiceName, errMsg))
|
||||
}
|
||||
if backend.ServicePort.Kind == util.IntstrString {
|
||||
if !utilvalidation.IsDNS1123Label(backend.ServicePort.StrVal) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("servicePort", backend.ServicePort.StrVal, apivalidation.DNS1123LabelErrorMsg))
|
||||
}
|
||||
if !utilvalidation.IsValidPortName(backend.ServicePort.StrVal) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("servicePort", backend.ServicePort.StrVal, portNameErrorMsg))
|
||||
}
|
||||
} else if !utilvalidation.IsValidPortNum(backend.ServicePort.IntVal) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("servicePort", backend.ServicePort, portRangeErrorMsg))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -848,3 +849,104 @@ func TestValidateJob(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ingressRules map[string]string
|
||||
|
||||
func TestValidateIngress(t *testing.T) {
|
||||
defaultBackend := experimental.IngressBackend{
|
||||
ServiceName: "default-backend",
|
||||
ServicePort: util.IntOrString{Kind: util.IntstrInt, IntVal: 80},
|
||||
}
|
||||
|
||||
newValid := func() experimental.Ingress {
|
||||
return experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Backend: &experimental.IngressBackend{
|
||||
ServiceName: "default-backend",
|
||||
ServicePort: util.IntOrString{Kind: util.IntstrInt, IntVal: 80},
|
||||
},
|
||||
Rules: []experimental.IngressRule{
|
||||
{
|
||||
Host: "foo.bar.com",
|
||||
IngressRuleValue: experimental.IngressRuleValue{
|
||||
HTTP: &experimental.HTTPIngressRuleValue{
|
||||
Paths: []experimental.HTTPIngressPath{
|
||||
{
|
||||
Path: "/foo",
|
||||
Backend: defaultBackend,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: experimental.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 = &experimental.IngressBackend{ServiceName: defaultBackend.ServiceName}
|
||||
noForwardSlashPath := newValid()
|
||||
noForwardSlashPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []experimental.HTTPIngressPath{
|
||||
{
|
||||
Path: "invalid",
|
||||
Backend: defaultBackend,
|
||||
},
|
||||
}
|
||||
noPaths := newValid()
|
||||
noPaths.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []experimental.HTTPIngressPath{}
|
||||
badHost := newValid()
|
||||
badHost.Spec.Rules[0].Host = "foobar:80"
|
||||
badRegexPath := newValid()
|
||||
badPathExpr := "/invalid["
|
||||
badRegexPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []experimental.HTTPIngressPath{
|
||||
{
|
||||
Path: badPathExpr,
|
||||
Backend: defaultBackend,
|
||||
},
|
||||
}
|
||||
badPathErr := fmt.Sprintf("spec.rules.ingressRule.http.path: invalid value '%v'",
|
||||
badPathExpr)
|
||||
hostIP := "127.0.0.1"
|
||||
badHostIP := newValid()
|
||||
badHostIP.Spec.Rules[0].Host = hostIP
|
||||
badHostIPErr := fmt.Sprintf("spec.rules.host: invalid value '%v'", hostIP)
|
||||
|
||||
errorCases := map[string]experimental.Ingress{
|
||||
"spec.backend.serviceName: required value": servicelessBackend,
|
||||
"spec.backend.serviceName: invalid value": invalidNameBackend,
|
||||
"spec.backend.servicePort: invalid value": noPortBackend,
|
||||
"spec.rules.host: invalid value": badHost,
|
||||
"spec.rules.ingressRule.http.paths: required value": noPaths,
|
||||
"spec.rules.ingressRule.http.path: invalid value": noForwardSlashPath,
|
||||
}
|
||||
errorCases[badPathErr] = badRegexPath
|
||||
errorCases[badHostIPErr] = badHostIP
|
||||
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateIngress(&v)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure for %s", k)
|
||||
} else {
|
||||
s := strings.Split(k, ":")
|
||||
err := errs[0].(*errors.ValidationError)
|
||||
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
|
||||
t.Errorf("unexpected error: %v, expected: %s", errs[0], k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ type ExperimentalInterface interface {
|
||||
DaemonSetsNamespacer
|
||||
DeploymentsNamespacer
|
||||
JobsNamespacer
|
||||
IngressNamespacer
|
||||
}
|
||||
|
||||
// ExperimentalClient is used to interact with experimental Kubernetes features.
|
||||
@@ -95,6 +96,10 @@ func (c *ExperimentalClient) Jobs(namespace string) JobInterface {
|
||||
return newJobs(c, namespace)
|
||||
}
|
||||
|
||||
func (c *ExperimentalClient) Ingress(namespace string) IngressInterface {
|
||||
return newIngress(c, namespace)
|
||||
}
|
||||
|
||||
// NewExperimental creates a new ExperimentalClient for the given config. This client
|
||||
// provides access to experimental Kubernetes features.
|
||||
// Experimental features are not supported and may be changed or removed in
|
||||
|
112
pkg/client/unversioned/ingress.go
Normal file
112
pkg/client/unversioned/ingress.go
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// IngressNamespacer has methods to work with Ingress resources in a namespace
|
||||
type IngressNamespacer interface {
|
||||
Ingress(namespace string) IngressInterface
|
||||
}
|
||||
|
||||
// IngressInterface exposes methods to work on Ingress resources.
|
||||
type IngressInterface interface {
|
||||
List(label labels.Selector, field fields.Selector) (*experimental.IngressList, error)
|
||||
Get(name string) (*experimental.Ingress, error)
|
||||
Create(ingress *experimental.Ingress) (*experimental.Ingress, error)
|
||||
Update(ingress *experimental.Ingress) (*experimental.Ingress, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
|
||||
UpdateStatus(ingress *experimental.Ingress) (*experimental.Ingress, error)
|
||||
}
|
||||
|
||||
// ingress implements IngressNamespacer interface
|
||||
type ingress struct {
|
||||
r *ExperimentalClient
|
||||
ns string
|
||||
}
|
||||
|
||||
// newIngress returns a ingress
|
||||
func newIngress(c *ExperimentalClient, namespace string) *ingress {
|
||||
return &ingress{c, namespace}
|
||||
}
|
||||
|
||||
// List returns a list of ingress that match the label and field selectors.
|
||||
func (c *ingress) List(label labels.Selector, field fields.Selector) (result *experimental.IngressList, err error) {
|
||||
result = &experimental.IngressList{}
|
||||
err = c.r.Get().Namespace(c.ns).Resource("ingress").LabelsSelectorParam(label).FieldsSelectorParam(field).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns information about a particular ingress.
|
||||
func (c *ingress) Get(name string) (result *experimental.Ingress, err error) {
|
||||
result = &experimental.Ingress{}
|
||||
err = c.r.Get().Namespace(c.ns).Resource("ingress").Name(name).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Create creates a new ingress.
|
||||
func (c *ingress) Create(ingress *experimental.Ingress) (result *experimental.Ingress, err error) {
|
||||
result = &experimental.Ingress{}
|
||||
err = c.r.Post().Namespace(c.ns).Resource("ingress").Body(ingress).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update updates an existing ingress.
|
||||
func (c *ingress) Update(ingress *experimental.Ingress) (result *experimental.Ingress, err error) {
|
||||
result = &experimental.Ingress{}
|
||||
err = c.r.Put().Namespace(c.ns).Resource("ingress").Name(ingress.Name).Body(ingress).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes a ingress, returns error if one occurs.
|
||||
func (c *ingress) Delete(name string, options *api.DeleteOptions) (err error) {
|
||||
if options == nil {
|
||||
return c.r.Delete().Namespace(c.ns).Resource("ingress").Name(name).Do().Error()
|
||||
}
|
||||
|
||||
body, err := api.Scheme.EncodeToVersion(options, c.r.APIVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.r.Delete().Namespace(c.ns).Resource("ingress").Name(name).Body(body).Do().Error()
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested ingress.
|
||||
func (c *ingress) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||
return c.r.Get().
|
||||
Prefix("watch").
|
||||
Namespace(c.ns).
|
||||
Resource("ingress").
|
||||
Param("resourceVersion", resourceVersion).
|
||||
LabelsSelectorParam(label).
|
||||
FieldsSelectorParam(field).
|
||||
Watch()
|
||||
}
|
||||
|
||||
// UpdateStatus takes the name of the ingress and the new status. Returns the server's representation of the ingress, and an error, if it occurs.
|
||||
func (c *ingress) UpdateStatus(ingress *experimental.Ingress) (result *experimental.Ingress, err error) {
|
||||
result = &experimental.Ingress{}
|
||||
err = c.r.Put().Namespace(c.ns).Resource("ingress").Name(ingress.Name).SubResource("status").Body(ingress).Do().Into(result)
|
||||
return
|
||||
}
|
230
pkg/client/unversioned/ingress_test.go
Normal file
230
pkg/client/unversioned/ingress_test.go
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
func getIngressResourceName() string {
|
||||
return "ingress"
|
||||
}
|
||||
|
||||
func TestListIngress(t *testing.T) {
|
||||
ns := api.NamespaceAll
|
||||
c := &testClient{
|
||||
Request: testRequest{
|
||||
Method: "GET",
|
||||
Path: testapi.Experimental.ResourcePath(getIngressResourceName(), ns, ""),
|
||||
},
|
||||
Response: Response{StatusCode: 200,
|
||||
Body: &experimental.IngressList{
|
||||
Items: []experimental.Ingress{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Rules: []experimental.IngressRule{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedIngressList, err := c.Setup(t).Experimental().Ingress(ns).List(labels.Everything(), fields.Everything())
|
||||
c.Validate(t, receivedIngressList, err)
|
||||
}
|
||||
|
||||
func TestGetIngress(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
c := &testClient{
|
||||
Request: testRequest{
|
||||
Method: "GET",
|
||||
Path: testapi.Experimental.ResourcePath(getIngressResourceName(), ns, "foo"),
|
||||
Query: buildQueryValues(nil),
|
||||
},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Rules: []experimental.IngressRule{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedIngress, err := c.Setup(t).Experimental().Ingress(ns).Get("foo")
|
||||
c.Validate(t, receivedIngress, err)
|
||||
}
|
||||
|
||||
func TestGetIngressWithNoName(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
c := &testClient{Error: true}
|
||||
receivedIngress, err := c.Setup(t).Experimental().Ingress(ns).Get("")
|
||||
if (err != nil) && (err.Error() != nameRequiredError) {
|
||||
t.Errorf("Expected error: %v, but got %v", nameRequiredError, err)
|
||||
}
|
||||
|
||||
c.Validate(t, receivedIngress, err)
|
||||
}
|
||||
|
||||
func TestUpdateIngress(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
requestIngress := &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: ns,
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{
|
||||
Method: "PUT",
|
||||
Path: testapi.Experimental.ResourcePath(getIngressResourceName(), ns, "foo"),
|
||||
Query: buildQueryValues(nil),
|
||||
},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Rules: []experimental.IngressRule{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedIngress, err := c.Setup(t).Experimental().Ingress(ns).Update(requestIngress)
|
||||
c.Validate(t, receivedIngress, err)
|
||||
}
|
||||
|
||||
func TestUpdateIngressStatus(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
lbStatus := api.LoadBalancerStatus{
|
||||
Ingress: []api.LoadBalancerIngress{
|
||||
{IP: "127.0.0.1"},
|
||||
},
|
||||
}
|
||||
requestIngress := &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: ns,
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Status: experimental.IngressStatus{
|
||||
LoadBalancer: lbStatus,
|
||||
},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{
|
||||
Method: "PUT",
|
||||
Path: testapi.Experimental.ResourcePath(getIngressResourceName(), ns, "foo") + "/status",
|
||||
Query: buildQueryValues(nil),
|
||||
},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Rules: []experimental.IngressRule{},
|
||||
},
|
||||
Status: experimental.IngressStatus{
|
||||
LoadBalancer: lbStatus,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedIngress, err := c.Setup(t).Experimental().Ingress(ns).UpdateStatus(requestIngress)
|
||||
c.Validate(t, receivedIngress, err)
|
||||
}
|
||||
|
||||
func TestDeleteIngress(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
c := &testClient{
|
||||
Request: testRequest{
|
||||
Method: "DELETE",
|
||||
Path: testapi.Experimental.ResourcePath(getIngressResourceName(), ns, "foo"),
|
||||
Query: buildQueryValues(nil),
|
||||
},
|
||||
Response: Response{StatusCode: 200},
|
||||
}
|
||||
err := c.Setup(t).Experimental().Ingress(ns).Delete("foo", nil)
|
||||
c.Validate(t, nil, err)
|
||||
}
|
||||
|
||||
func TestCreateIngress(t *testing.T) {
|
||||
ns := api.NamespaceDefault
|
||||
requestIngress := &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: ns,
|
||||
},
|
||||
}
|
||||
c := &testClient{
|
||||
Request: testRequest{
|
||||
Method: "POST",
|
||||
Path: testapi.Experimental.ResourcePath(getIngressResourceName(), ns, ""),
|
||||
Body: requestIngress,
|
||||
Query: buildQueryValues(nil),
|
||||
},
|
||||
Response: Response{
|
||||
StatusCode: 200,
|
||||
Body: &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"name": "baz",
|
||||
},
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Rules: []experimental.IngressRule{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
receivedIngress, err := c.Setup(t).Experimental().Ingress(ns).Create(requestIngress)
|
||||
c.Validate(t, receivedIngress, err)
|
||||
}
|
86
pkg/client/unversioned/testclient/fake_ingress.go
Normal file
86
pkg/client/unversioned/testclient/fake_ingress.go
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 testclient
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeIngress implements IngressInterface. Meant to be embedded into a struct to get a default
|
||||
// implementation. This makes faking out just the method you want to test easier.
|
||||
type FakeIngress struct {
|
||||
Fake *FakeExperimental
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (c *FakeIngress) Get(name string) (*experimental.Ingress, error) {
|
||||
obj, err := c.Fake.Invokes(NewGetAction("ingress", c.Namespace, name), &experimental.Ingress{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*experimental.Ingress), err
|
||||
}
|
||||
|
||||
func (c *FakeIngress) List(label labels.Selector, fields fields.Selector) (*experimental.IngressList, error) {
|
||||
obj, err := c.Fake.Invokes(NewListAction("ingress", c.Namespace, label, nil), &experimental.IngressList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*experimental.IngressList), err
|
||||
}
|
||||
|
||||
func (c *FakeIngress) Create(ingress *experimental.Ingress) (*experimental.Ingress, error) {
|
||||
obj, err := c.Fake.Invokes(NewCreateAction("ingress", c.Namespace, ingress), ingress)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*experimental.Ingress), err
|
||||
}
|
||||
|
||||
func (c *FakeIngress) Update(ingress *experimental.Ingress) (*experimental.Ingress, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateAction("ingress", c.Namespace, ingress), ingress)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*experimental.Ingress), err
|
||||
}
|
||||
|
||||
func (c *FakeIngress) Delete(name string, options *api.DeleteOptions) error {
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("ingress", c.Namespace, name), &experimental.Ingress{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeIngress) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
|
||||
return c.Fake.InvokesWatch(NewWatchAction("ingress", c.Namespace, label, field, resourceVersion))
|
||||
}
|
||||
|
||||
func (c *FakeIngress) UpdateStatus(ingress *experimental.Ingress) (result *experimental.Ingress, err error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("ingress", "status", c.Namespace, ingress), ingress)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*experimental.Ingress), err
|
||||
}
|
@@ -327,3 +327,7 @@ func (c *FakeExperimental) Scales(namespace string) client.ScaleInterface {
|
||||
func (c *FakeExperimental) Jobs(namespace string) client.JobInterface {
|
||||
return &FakeJobs{Fake: c, Namespace: namespace}
|
||||
}
|
||||
|
||||
func (c *FakeExperimental) Ingress(namespace string) client.IngressInterface {
|
||||
return &FakeIngress{Fake: c, Namespace: namespace}
|
||||
}
|
||||
|
@@ -209,6 +209,10 @@ func deleteAllContent(kubeClient client.Interface, experimentalMode bool, namesp
|
||||
if err != nil {
|
||||
return estimate, err
|
||||
}
|
||||
err = deleteIngress(kubeClient.Experimental(), namespace)
|
||||
if err != nil {
|
||||
return estimate, err
|
||||
}
|
||||
}
|
||||
return estimate, nil
|
||||
}
|
||||
@@ -496,3 +500,17 @@ func deleteDeployments(expClient client.ExperimentalInterface, ns string) error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteIngress(expClient client.ExperimentalInterface, ns string) error {
|
||||
items, err := expClient.Ingress(ns).List(labels.Everything(), fields.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range items.Items {
|
||||
err := expClient.Ingress(ns).Delete(items.Items[i].Name, nil)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -114,6 +114,7 @@ func testSyncNamespaceThatIsTerminating(t *testing.T, experimentalMode bool) {
|
||||
strings.Join([]string{"list", "daemonsets", ""}, "-"),
|
||||
strings.Join([]string{"list", "deployments", ""}, "-"),
|
||||
strings.Join([]string{"list", "jobs", ""}, "-"),
|
||||
strings.Join([]string{"list", "ingress", ""}, "-"),
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -102,6 +102,7 @@ func expandResourceShortcut(resource string) string {
|
||||
"rc": "replicationcontrollers",
|
||||
"ds": "daemonsets",
|
||||
"svc": "services",
|
||||
"ing": "ingress",
|
||||
}
|
||||
if expanded, ok := shortForms[resource]; ok {
|
||||
return expanded
|
||||
|
@@ -45,6 +45,14 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
const (
|
||||
tabwriterMinWidth = 10
|
||||
tabwriterWidth = 4
|
||||
tabwriterPadding = 3
|
||||
tabwriterPadChar = ' '
|
||||
tabwriterFlags = 0
|
||||
)
|
||||
|
||||
// GetPrinter takes a format type, an optional format argument. It will return true
|
||||
// if the format is generic (untyped), otherwise it will return false. The printer
|
||||
// is agnostic to schema versions, so you must send arguments to PrintObj in the
|
||||
@@ -382,6 +390,7 @@ var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLA
|
||||
var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS", "AGE"}
|
||||
var jobColumns = []string{"JOB", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "SUCCESSFUL"}
|
||||
var serviceColumns = []string{"NAME", "CLUSTER_IP", "EXTERNAL_IP", "PORT(S)", "SELECTOR", "AGE"}
|
||||
var ingressColumns = []string{"NAME", "RULE", "BACKEND", "ADDRESS"}
|
||||
var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"}
|
||||
var nodeColumns = []string{"NAME", "LABELS", "STATUS", "AGE"}
|
||||
var daemonSetColumns = []string{"NAME", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "NODE-SELECTOR"}
|
||||
@@ -413,6 +422,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
|
||||
h.Handler(jobColumns, printJobList)
|
||||
h.Handler(serviceColumns, printService)
|
||||
h.Handler(serviceColumns, printServiceList)
|
||||
h.Handler(ingressColumns, printIngress)
|
||||
h.Handler(ingressColumns, printIngressList)
|
||||
h.Handler(endpointColumns, printEndpoints)
|
||||
h.Handler(endpointColumns, printEndpointsList)
|
||||
h.Handler(nodeColumns, printNode)
|
||||
@@ -739,6 +750,18 @@ func printJobList(list *experimental.JobList, w io.Writer, withNamespace bool, w
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadBalancerStatusStringer behaves just like a string interface and converts the given status to a string.
|
||||
func loadBalancerStatusStringer(s api.LoadBalancerStatus) string {
|
||||
ingress := s.Ingress
|
||||
result := []string{}
|
||||
for i := range ingress {
|
||||
if ingress[i].IP != "" {
|
||||
result = append(result, ingress[i].IP)
|
||||
}
|
||||
}
|
||||
return strings.Join(result, ",")
|
||||
}
|
||||
|
||||
func getServiceExternalIP(svc *api.Service) string {
|
||||
switch svc.Spec.Type {
|
||||
case api.ServiceTypeClusterIP:
|
||||
@@ -752,18 +775,13 @@ func getServiceExternalIP(svc *api.Service) string {
|
||||
}
|
||||
return "nodes"
|
||||
case api.ServiceTypeLoadBalancer:
|
||||
ingress := svc.Status.LoadBalancer.Ingress
|
||||
result := []string{}
|
||||
for i := range ingress {
|
||||
if ingress[i].IP != "" {
|
||||
result = append(result, ingress[i].IP)
|
||||
}
|
||||
}
|
||||
lbIps := loadBalancerStatusStringer(svc.Status.LoadBalancer)
|
||||
if len(svc.Spec.ExternalIPs) > 0 {
|
||||
result = append(result, svc.Spec.ExternalIPs...)
|
||||
}
|
||||
result := append(strings.Split(lbIps, ","), svc.Spec.ExternalIPs...)
|
||||
return strings.Join(result, ",")
|
||||
}
|
||||
return lbIps
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -813,6 +831,71 @@ func printServiceList(list *api.ServiceList, w io.Writer, withNamespace bool, wi
|
||||
return nil
|
||||
}
|
||||
|
||||
// backendStringer behaves just like a string interface and converts the given backend to a string.
|
||||
func backendStringer(backend *experimental.IngressBackend) string {
|
||||
if backend == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%v:%v", backend.ServiceName, backend.ServicePort.String())
|
||||
}
|
||||
|
||||
func printIngress(ingress *experimental.Ingress, w io.Writer, withNamespace, wide bool, showAll bool, columnLabels []string) error {
|
||||
name := ingress.Name
|
||||
namespace := ingress.Namespace
|
||||
|
||||
hostRules := ingress.Spec.Rules
|
||||
if withNamespace {
|
||||
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(w, "%s\t%v\t%v\t%v\n",
|
||||
name,
|
||||
"-",
|
||||
backendStringer(ingress.Spec.Backend),
|
||||
loadBalancerStatusStringer(ingress.Status.LoadBalancer)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lay out all the rules on separate lines.
|
||||
extraLinePrefix := ""
|
||||
if withNamespace {
|
||||
extraLinePrefix = "\t"
|
||||
}
|
||||
for _, rules := range hostRules {
|
||||
if rules.HTTP == nil {
|
||||
continue
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "%s\t%v\t", extraLinePrefix, rules.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range rules.HTTP.Paths {
|
||||
_, err := fmt.Fprintf(w, "%s\t%v\t%v", extraLinePrefix, rule.Path, backendStringer(&rule.Backend))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(w, appendLabelTabs(columnLabels)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printIngressList(ingressList *experimental.IngressList, w io.Writer, withNamespace, wide bool, showAll bool, columnLabels []string) error {
|
||||
for _, ingress := range ingressList.Items {
|
||||
if err := printIngress(&ingress, w, withNamespace, wide, true, columnLabels); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printDaemonSet(ds *experimental.DaemonSet, w io.Writer, withNamespace bool, wide bool, showAll bool, columnLabels []string) error {
|
||||
name := ds.Name
|
||||
namespace := ds.Namespace
|
||||
@@ -1369,7 +1452,7 @@ func formatWideHeaders(wide bool, t reflect.Type) []string {
|
||||
|
||||
// PrintObj prints the obj in a human-friendly format according to the type of the obj.
|
||||
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
|
||||
w := tabwriter.NewWriter(output, 10, 4, 3, ' ', 0)
|
||||
w := tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags)
|
||||
defer w.Flush()
|
||||
t := reflect.TypeOf(obj)
|
||||
if handler := h.handlerMap[t]; handler != nil {
|
||||
|
@@ -55,6 +55,7 @@ import (
|
||||
endpointsetcd "k8s.io/kubernetes/pkg/registry/endpoint/etcd"
|
||||
eventetcd "k8s.io/kubernetes/pkg/registry/event/etcd"
|
||||
expcontrolleretcd "k8s.io/kubernetes/pkg/registry/experimental/controller/etcd"
|
||||
ingressetcd "k8s.io/kubernetes/pkg/registry/ingress/etcd"
|
||||
jobetcd "k8s.io/kubernetes/pkg/registry/job/etcd"
|
||||
limitrangeetcd "k8s.io/kubernetes/pkg/registry/limitrange/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/namespace"
|
||||
@@ -967,6 +968,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
|
||||
daemonSetStorage, daemonSetStatusStorage := daemonetcd.NewREST(c.ExpDatabaseStorage)
|
||||
deploymentStorage := deploymentetcd.NewStorage(c.ExpDatabaseStorage)
|
||||
jobStorage, jobStatusStorage := jobetcd.NewREST(c.ExpDatabaseStorage)
|
||||
ingressStorage := ingressetcd.NewREST(c.ExpDatabaseStorage)
|
||||
|
||||
thirdPartyControl := ThirdPartyController{
|
||||
master: m,
|
||||
@@ -990,6 +992,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
|
||||
strings.ToLower("deployments/scale"): deploymentStorage.Scale,
|
||||
strings.ToLower("jobs"): jobStorage,
|
||||
strings.ToLower("jobs/status"): jobStatusStorage,
|
||||
strings.ToLower("ingress"): ingressStorage,
|
||||
}
|
||||
|
||||
expMeta := latest.GroupOrDie("experimental")
|
||||
|
17
pkg/registry/ingress/doc.go
Normal file
17
pkg/registry/ingress/doc.go
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 ingress
|
77
pkg/registry/ingress/etcd/etcd.go
Normal file
77
pkg/registry/ingress/etcd/etcd.go
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
|
||||
ingress "k8s.io/kubernetes/pkg/registry/ingress"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
IngressPath string = "/ingress"
|
||||
)
|
||||
|
||||
// rest implements a RESTStorage for replication controllers against etcd
|
||||
type REST struct {
|
||||
*etcdgeneric.Etcd
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against replication controllers.
|
||||
func NewREST(s storage.Interface) *REST {
|
||||
store := &etcdgeneric.Etcd{
|
||||
NewFunc: func() runtime.Object { return &experimental.Ingress{} },
|
||||
|
||||
// NewListFunc returns an object capable of storing results of an etcd list.
|
||||
NewListFunc: func() runtime.Object { return &experimental.IngressList{} },
|
||||
// Produces a ingress that etcd understands, to the root of the resource
|
||||
// by combining the namespace in the context with the given prefix
|
||||
KeyRootFunc: func(ctx api.Context) string {
|
||||
return etcdgeneric.NamespaceKeyRootFunc(ctx, IngressPath)
|
||||
},
|
||||
// Produces a ingress that etcd understands, to the resource by combining
|
||||
// the namespace in the context with the given prefix
|
||||
KeyFunc: func(ctx api.Context, name string) (string, error) {
|
||||
return etcdgeneric.NamespaceKeyFunc(ctx, IngressPath, name)
|
||||
},
|
||||
// Retrieve the name field of a replication controller
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*experimental.Ingress).Name, nil
|
||||
},
|
||||
// Used to match objects based on labels/fields for list and watch
|
||||
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return ingress.MatchIngress(label, field)
|
||||
},
|
||||
EndpointName: "ingress",
|
||||
|
||||
// Used to validate controller creation
|
||||
CreateStrategy: ingress.Strategy,
|
||||
|
||||
// Used to validate controller updates
|
||||
UpdateStrategy: ingress.Strategy,
|
||||
|
||||
Storage: s,
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
203
pkg/registry/ingress/etcd/etcd_test.go
Executable file
203
pkg/registry/ingress/etcd/etcd_test.go
Executable file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/tools"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t, "experimental")
|
||||
ingressStorage := NewREST(etcdStorage)
|
||||
return ingressStorage, fakeClient
|
||||
}
|
||||
|
||||
var (
|
||||
namespace = api.NamespaceNone
|
||||
name = "foo-ingress"
|
||||
defaultHostname = "foo.bar.com"
|
||||
defaultBackendName = "default-backend"
|
||||
defaultBackendPort = util.IntOrString{Kind: util.IntstrInt, IntVal: 80}
|
||||
defaultLoadBalancer = "127.0.0.1"
|
||||
defaultPath = "/foo"
|
||||
defaultPathMap = map[string]string{defaultPath: defaultBackendName}
|
||||
)
|
||||
|
||||
type IngressRuleValues map[string]string
|
||||
|
||||
func toHTTPIngressPaths(pathMap map[string]string) []experimental.HTTPIngressPath {
|
||||
httpPaths := []experimental.HTTPIngressPath{}
|
||||
for path, backend := range pathMap {
|
||||
httpPaths = append(httpPaths, experimental.HTTPIngressPath{
|
||||
Path: path,
|
||||
Backend: experimental.IngressBackend{
|
||||
ServiceName: backend,
|
||||
ServicePort: defaultBackendPort,
|
||||
},
|
||||
})
|
||||
}
|
||||
return httpPaths
|
||||
}
|
||||
|
||||
func toIngressRules(hostRules map[string]IngressRuleValues) []experimental.IngressRule {
|
||||
rules := []experimental.IngressRule{}
|
||||
for host, pathMap := range hostRules {
|
||||
rules = append(rules, experimental.IngressRule{
|
||||
Host: host,
|
||||
IngressRuleValue: experimental.IngressRuleValue{
|
||||
HTTP: &experimental.HTTPIngressRuleValue{
|
||||
Paths: toHTTPIngressPaths(pathMap),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func newIngress(pathMap map[string]string) *experimental.Ingress {
|
||||
return &experimental.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: experimental.IngressSpec{
|
||||
Backend: &experimental.IngressBackend{
|
||||
ServiceName: defaultBackendName,
|
||||
ServicePort: defaultBackendPort,
|
||||
},
|
||||
Rules: toIngressRules(map[string]IngressRuleValues{
|
||||
defaultHostname: pathMap,
|
||||
}),
|
||||
},
|
||||
Status: experimental.IngressStatus{
|
||||
LoadBalancer: api.LoadBalancerStatus{
|
||||
Ingress: []api.LoadBalancerIngress{
|
||||
{IP: defaultLoadBalancer},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func validIngress() *experimental.Ingress {
|
||||
return newIngress(defaultPathMap)
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
ingress := validIngress()
|
||||
noDefaultBackendAndRules := validIngress()
|
||||
noDefaultBackendAndRules.Spec.Backend = &experimental.IngressBackend{}
|
||||
noDefaultBackendAndRules.Spec.Rules = []experimental.IngressRule{}
|
||||
badPath := validIngress()
|
||||
badPath.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
|
||||
"foo.bar.com": {"/invalid[": "svc"}})
|
||||
test.TestCreate(
|
||||
// valid
|
||||
ingress,
|
||||
noDefaultBackendAndRules,
|
||||
badPath,
|
||||
)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
validIngress(),
|
||||
// updateFunc
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*experimental.Ingress)
|
||||
object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
|
||||
"bar.foo.com": {"/bar": defaultBackendName},
|
||||
})
|
||||
return object
|
||||
},
|
||||
// invalid updateFunc: ObjeceMeta is not to be tampered with.
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*experimental.Ingress)
|
||||
object.UID = "newUID"
|
||||
return object
|
||||
},
|
||||
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*experimental.Ingress)
|
||||
object.Name = ""
|
||||
return object
|
||||
},
|
||||
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*experimental.Ingress)
|
||||
object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
|
||||
"foo.bar.com": {"/invalid[": "svc"}})
|
||||
return object
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestDelete(validIngress())
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestGet(validIngress())
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestList(validIngress())
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
storage, fakeClient := newStorage(t)
|
||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||
test.TestWatch(
|
||||
validIngress(),
|
||||
// matching labels
|
||||
[]labels.Set{},
|
||||
// not matching labels
|
||||
[]labels.Set{
|
||||
{"a": "c"},
|
||||
{"foo": "bar"},
|
||||
},
|
||||
// matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": name},
|
||||
},
|
||||
// not matching fields
|
||||
[]fields.Set{
|
||||
{"metadata.name": "bar"},
|
||||
{"name": name},
|
||||
},
|
||||
)
|
||||
}
|
116
pkg/registry/ingress/strategy.go
Normal file
116
pkg/registry/ingress/strategy.go
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 ingress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental"
|
||||
"k8s.io/kubernetes/pkg/apis/experimental/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/fielderrors"
|
||||
)
|
||||
|
||||
// ingressStrategy implements verification logic for Replication Ingresss.
|
||||
type ingressStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
}
|
||||
|
||||
// Strategy is the default logic that applies when creating and updating Replication Ingress objects.
|
||||
var Strategy = ingressStrategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// NamespaceScoped returns true because all Ingress' need to be within a namespace.
|
||||
func (ingressStrategy) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears the status of an Ingress before creation.
|
||||
func (ingressStrategy) PrepareForCreate(obj runtime.Object) {
|
||||
ingress := obj.(*experimental.Ingress)
|
||||
ingress.Status = experimental.IngressStatus{}
|
||||
|
||||
ingress.Generation = 1
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (ingressStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newIngress := obj.(*experimental.Ingress)
|
||||
oldIngress := old.(*experimental.Ingress)
|
||||
//TODO: Clear Ingress status once we have a sub-resource.
|
||||
|
||||
// Any changes to the spec increment the generation number, any changes to the
|
||||
// status should reflect the generation number of the corresponding object.
|
||||
// See api.ObjectMeta description for more information on Generation.
|
||||
if !reflect.DeepEqual(oldIngress.Spec, newIngress.Spec) {
|
||||
newIngress.Generation = oldIngress.Generation + 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validate validates a new Ingress.
|
||||
func (ingressStrategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList {
|
||||
ingress := obj.(*experimental.Ingress)
|
||||
err := validation.ValidateIngress(ingress)
|
||||
return err
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is false for Ingress; this means POST is needed to create one.
|
||||
func (ingressStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (ingressStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
|
||||
validationErrorList := validation.ValidateIngress(obj.(*experimental.Ingress))
|
||||
updateErrorList := validation.ValidateIngressUpdate(old.(*experimental.Ingress), obj.(*experimental.Ingress))
|
||||
return append(validationErrorList, updateErrorList...)
|
||||
}
|
||||
|
||||
// AllowUnconditionalUpdate is the default update policy for Ingress objects.
|
||||
func (ingressStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IngressToSelectableFields returns a label set that represents the object.
|
||||
func IngressToSelectableFields(ingress *experimental.Ingress) fields.Set {
|
||||
return fields.Set{
|
||||
"metadata.name": ingress.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// MatchIngress is the filter used by the generic etcd backend to ingress
|
||||
// watch events from etcd to clients of the apiserver only interested in specific
|
||||
// labels/fields.
|
||||
func MatchIngress(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return &generic.SelectionPredicate{
|
||||
Label: label,
|
||||
Field: field,
|
||||
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
ingress, ok := obj.(*experimental.Ingress)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Given object is not an Ingress.")
|
||||
}
|
||||
return labels.Set(ingress.ObjectMeta.Labels), IngressToSelectableFields(ingress), nil
|
||||
},
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user