Merge pull request #20142 from bprashanth/ing_tls

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-02-05 15:07:09 -08:00
commit 6b20879a7f
17 changed files with 8683 additions and 7494 deletions

View File

@ -3616,6 +3616,13 @@
"$ref": "v1beta1.IngressBackend", "$ref": "v1beta1.IngressBackend",
"description": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default." "description": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default."
}, },
"tls": {
"type": "array",
"items": {
"$ref": "v1beta1.IngressTLS"
},
"description": "TLS configuration. Currently the Ingress only supports a single TLS port, 443, and assumes TLS termination. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension."
},
"rules": { "rules": {
"type": "array", "type": "array",
"items": { "items": {
@ -3643,6 +3650,23 @@
} }
} }
}, },
"v1beta1.IngressTLS": {
"id": "v1beta1.IngressTLS",
"description": "IngressTLS describes the transport layer security associated with an Ingress.",
"properties": {
"hosts": {
"type": "array",
"items": {
"type": "string"
},
"description": "Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified."
},
"secretName": {
"type": "string",
"description": "SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing."
}
}
},
"v1beta1.IngressRule": { "v1beta1.IngressRule": {
"id": "v1beta1.IngressRule", "id": "v1beta1.IngressRule",
"description": "IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.", "description": "IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.",

View File

@ -726,6 +726,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">tls</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">TLS configuration. Currently the Ingress only supports a single TLS port, 443, and assumes TLS termination. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1beta1_ingresstls">v1beta1.IngressTLS</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">rules</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">rules</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
@ -2631,6 +2638,40 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody> </tbody>
</table> </table>
</div>
<div class="sect2">
<h3 id="_v1_flockervolumesource">v1.FlockerVolumeSource</h3>
<div class="paragraph">
<p>Represents a Flocker volume mounted by the Flocker agent. Flocker volumes do not support ownership management or SELinux relabeling.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">datasetName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required: the volume name. This is going to be store on metadata &#8594; name on the payload for Flocker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_v1_awselasticblockstorevolumesource">v1.AWSElasticBlockStoreVolumeSource</h3> <h3 id="_v1_awselasticblockstorevolumesource">v1.AWSElasticBlockStoreVolumeSource</h3>
@ -2730,40 +2771,6 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody> </tbody>
</table> </table>
</div>
<div class="sect2">
<h3 id="_v1_flockervolumesource">v1.FlockerVolumeSource</h3>
<div class="paragraph">
<p>Represents a Flocker volume mounted by the Flocker agent. Flocker volumes do not support ownership management or SELinux relabeling.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">datasetName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Required: the volume name. This is going to be store on metadata &#8594; name on the payload for Flocker</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_unversioned_listmeta">unversioned.ListMeta</h3> <h3 id="_unversioned_listmeta">unversioned.ListMeta</h3>
@ -4612,6 +4619,47 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tbody> </tbody>
</table> </table>
</div>
<div class="sect2">
<h3 id="_v1beta1_ingresstls">v1beta1.IngressTLS</h3>
<div class="paragraph">
<p>IngressTLS describes the transport layer security associated with an Ingress.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">hosts</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the "Host" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_v1beta1_subresourcereference">v1beta1.SubresourceReference</h3> <h3 id="_v1beta1_subresourcereference">v1beta1.SubresourceReference</h3>
@ -4886,7 +4934,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</div> </div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2016-02-05 16:19:07 UTC Last updated 2016-02-05 18:58:44 UTC
</div> </div>
</div> </div>
</body> </body>

View File

@ -2241,6 +2241,21 @@ const (
// SSHAuthPrivateKey is the key of the required SSH private key for SecretTypeSSHAuth secrets // SSHAuthPrivateKey is the key of the required SSH private key for SecretTypeSSHAuth secrets
SSHAuthPrivateKey = "ssh-privatekey" SSHAuthPrivateKey = "ssh-privatekey"
// SecretTypeTLS contains information about a TLS client or server secret. It
// is primarily used with TLS termination of the Ingress resource, but may be
// used in other types.
//
// Required fields:
// - Secret.Data["tls.key"] - TLS private key.
// Secret.Data["tls.crt"] - TLS certificate.
// TODO: Consider supporting different formats, specifying CA/destinationCA.
SecretTypeTLS SecretType = "kubernetes.io/tls"
// TLSCertKey is the key for tls certificates in a TLS secert.
TLSCertKey = "tls.crt"
// TLSPrivateKeyKey is the key for the private key field in a TLS secret.
TLSPrivateKeyKey = "tls.key"
) )
type SecretList struct { type SecretList struct {

View File

@ -2653,6 +2653,21 @@ const (
// DockerConfigKey is the key of the required data for SecretTypeDockercfg secrets // DockerConfigKey is the key of the required data for SecretTypeDockercfg secrets
DockerConfigKey = ".dockercfg" DockerConfigKey = ".dockercfg"
// SecretTypeTLS contains information about a TLS client or server secret. It
// is primarily used with TLS termination of the Ingress resource, but may be
// used in other types.
//
// Required fields:
// - Secret.Data["tls.key"] - TLS private key.
// Secret.Data["tls.crt"] - TLS certificate.
// TODO: Consider supporting different formats, specifying CA/destinationCA.
SecretTypeTLS SecretType = "kubernetes.io/tls"
// TLSCertKey is the key for tls certificates in a TLS secert.
TLSCertKey = "tls.crt"
// TLSPrivateKeyKey is the key for the private key field in a TLS secret.
TLSPrivateKeyKey = "tls.key"
) )
// SecretList is a list of Secret. // SecretList is a list of Secret.

View File

@ -2083,6 +2083,14 @@ func ValidateSecret(secret *api.Secret) field.ErrorList {
break break
} }
case api.SecretTypeTLS:
if _, exists := secret.Data[api.TLSCertKey]; !exists {
allErrs = append(allErrs, field.Required(dataPath.Key(api.TLSCertKey), ""))
}
if _, exists := secret.Data[api.TLSPrivateKeyKey]; !exists {
allErrs = append(allErrs, field.Required(dataPath.Key(api.TLSPrivateKeyKey), ""))
}
// TODO: Verify that the key matches the cert.
default: default:
// no-op // no-op
} }

View File

@ -4583,6 +4583,52 @@ func TestValidateEndpoints(t *testing.T) {
} }
} }
func TestValidateTLSSecret(t *testing.T) {
successCases := map[string]api.Secret{
"emtpy certificate chain": {
ObjectMeta: api.ObjectMeta{Name: "tls-cert", Namespace: "namespace"},
Data: map[string][]byte{
api.TLSCertKey: []byte("public key"),
api.TLSPrivateKeyKey: []byte("private key"),
},
},
}
for k, v := range successCases {
if errs := ValidateSecret(&v); len(errs) != 0 {
t.Errorf("Expected success for %s, got %v", k, errs)
}
}
errorCases := map[string]struct {
secrets api.Secret
errorType field.ErrorType
errorDetail string
}{
"missing public key": {
secrets: api.Secret{
ObjectMeta: api.ObjectMeta{Name: "tls-cert"},
Data: map[string][]byte{
api.TLSCertKey: []byte("public key"),
},
},
errorType: "FieldValueRequired",
},
"missing private key": {
secrets: api.Secret{
ObjectMeta: api.ObjectMeta{Name: "tls-cert"},
Data: map[string][]byte{
api.TLSCertKey: []byte("public key"),
},
},
errorType: "FieldValueRequired",
},
}
for k, v := range errorCases {
if errs := ValidateSecret(&v.secrets); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) {
t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs)
}
}
}
func TestValidateSecurityContext(t *testing.T) { func TestValidateSecurityContext(t *testing.T) {
priv := false priv := false
var runAsUser int64 = 1 var runAsUser int64 = 1

File diff suppressed because it is too large Load Diff

View File

@ -643,12 +643,35 @@ type IngressSpec struct {
// is optional to allow the loadbalancer controller or defaulting logic to // is optional to allow the loadbalancer controller or defaulting logic to
// specify a global default. // specify a global default.
Backend *IngressBackend `json:"backend,omitempty"` Backend *IngressBackend `json:"backend,omitempty"`
// TLS is the TLS configuration. Currently the Ingress only supports a single TLS
// port, 443, and assumes TLS termination. If multiple members of this
// list specify different hosts, they will be multiplexed on the same
// port according to the hostname specified through the SNI TLS extension.
TLS []IngressTLS `json:"tls,omitempty"`
// A list of host rules used to configure the Ingress. If unspecified, or // A list of host rules used to configure the Ingress. If unspecified, or
// no rule matches, all traffic is sent to the default backend. // no rule matches, all traffic is sent to the default backend.
Rules []IngressRule `json:"rules,omitempty"` Rules []IngressRule `json:"rules,omitempty"`
// TODO: Add the ability to specify load-balancer IP through claims // TODO: Add the ability to specify load-balancer IP through claims
} }
// IngressTLS describes the transport layer security associated with an Ingress.
type IngressTLS struct {
// Hosts are a list of hosts included in the TLS certificate. The values in
// this list must match the name/s used in the tlsSecret. Defaults to the
// wildcard host setting for the loadbalancer controller fulfilling this
// Ingress, if left unspecified.
Hosts []string `json:"hosts,omitempty"`
// SecretName is the name of the secret used to terminate SSL traffic on 443.
// Field is left optional to allow SSL routing based on SNI hostname alone.
// If the SNI host in a listener conflicts with the "Host" header field used
// by an IngressRule, the SNI host is used for termination and value of the
// Host header is used for routing.
SecretName string `json:"secretName,omitempty"`
// TODO: Consider specifying different modes of termination, protocols etc.
}
// IngressStatus describe the current state of the Ingress. // IngressStatus describe the current state of the Ingress.
type IngressStatus struct { type IngressStatus struct {
// LoadBalancer contains the current status of the load-balancer. // LoadBalancer contains the current status of the load-balancer.

View File

@ -3167,6 +3167,16 @@ func autoConvert_extensions_IngressSpec_To_v1beta1_IngressSpec(in *extensions.In
} else { } else {
out.Backend = nil out.Backend = nil
} }
if in.TLS != nil {
out.TLS = make([]IngressTLS, len(in.TLS))
for i := range in.TLS {
if err := Convert_extensions_IngressTLS_To_v1beta1_IngressTLS(&in.TLS[i], &out.TLS[i], s); err != nil {
return err
}
}
} else {
out.TLS = nil
}
if in.Rules != nil { if in.Rules != nil {
out.Rules = make([]IngressRule, len(in.Rules)) out.Rules = make([]IngressRule, len(in.Rules))
for i := range in.Rules { for i := range in.Rules {
@ -3198,6 +3208,26 @@ func Convert_extensions_IngressStatus_To_v1beta1_IngressStatus(in *extensions.In
return autoConvert_extensions_IngressStatus_To_v1beta1_IngressStatus(in, out, s) return autoConvert_extensions_IngressStatus_To_v1beta1_IngressStatus(in, out, s)
} }
func autoConvert_extensions_IngressTLS_To_v1beta1_IngressTLS(in *extensions.IngressTLS, out *IngressTLS, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*extensions.IngressTLS))(in)
}
if in.Hosts != nil {
out.Hosts = make([]string, len(in.Hosts))
for i := range in.Hosts {
out.Hosts[i] = in.Hosts[i]
}
} else {
out.Hosts = nil
}
out.SecretName = in.SecretName
return nil
}
func Convert_extensions_IngressTLS_To_v1beta1_IngressTLS(in *extensions.IngressTLS, out *IngressTLS, s conversion.Scope) error {
return autoConvert_extensions_IngressTLS_To_v1beta1_IngressTLS(in, out, s)
}
func autoConvert_extensions_Job_To_v1beta1_Job(in *extensions.Job, out *Job, s conversion.Scope) error { func autoConvert_extensions_Job_To_v1beta1_Job(in *extensions.Job, out *Job, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*extensions.Job))(in) defaulting.(func(*extensions.Job))(in)
@ -4438,6 +4468,16 @@ func autoConvert_v1beta1_IngressSpec_To_extensions_IngressSpec(in *IngressSpec,
} else { } else {
out.Backend = nil out.Backend = nil
} }
if in.TLS != nil {
out.TLS = make([]extensions.IngressTLS, len(in.TLS))
for i := range in.TLS {
if err := Convert_v1beta1_IngressTLS_To_extensions_IngressTLS(&in.TLS[i], &out.TLS[i], s); err != nil {
return err
}
}
} else {
out.TLS = nil
}
if in.Rules != nil { if in.Rules != nil {
out.Rules = make([]extensions.IngressRule, len(in.Rules)) out.Rules = make([]extensions.IngressRule, len(in.Rules))
for i := range in.Rules { for i := range in.Rules {
@ -4469,6 +4509,26 @@ func Convert_v1beta1_IngressStatus_To_extensions_IngressStatus(in *IngressStatus
return autoConvert_v1beta1_IngressStatus_To_extensions_IngressStatus(in, out, s) return autoConvert_v1beta1_IngressStatus_To_extensions_IngressStatus(in, out, s)
} }
func autoConvert_v1beta1_IngressTLS_To_extensions_IngressTLS(in *IngressTLS, out *extensions.IngressTLS, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*IngressTLS))(in)
}
if in.Hosts != nil {
out.Hosts = make([]string, len(in.Hosts))
for i := range in.Hosts {
out.Hosts[i] = in.Hosts[i]
}
} else {
out.Hosts = nil
}
out.SecretName = in.SecretName
return nil
}
func Convert_v1beta1_IngressTLS_To_extensions_IngressTLS(in *IngressTLS, out *extensions.IngressTLS, s conversion.Scope) error {
return autoConvert_v1beta1_IngressTLS_To_extensions_IngressTLS(in, out, s)
}
func autoConvert_v1beta1_Job_To_extensions_Job(in *Job, out *extensions.Job, s conversion.Scope) error { func autoConvert_v1beta1_Job_To_extensions_Job(in *Job, out *extensions.Job, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*Job))(in) defaulting.(func(*Job))(in)
@ -5234,6 +5294,7 @@ func init() {
autoConvert_extensions_IngressRule_To_v1beta1_IngressRule, autoConvert_extensions_IngressRule_To_v1beta1_IngressRule,
autoConvert_extensions_IngressSpec_To_v1beta1_IngressSpec, autoConvert_extensions_IngressSpec_To_v1beta1_IngressSpec,
autoConvert_extensions_IngressStatus_To_v1beta1_IngressStatus, autoConvert_extensions_IngressStatus_To_v1beta1_IngressStatus,
autoConvert_extensions_IngressTLS_To_v1beta1_IngressTLS,
autoConvert_extensions_Ingress_To_v1beta1_Ingress, autoConvert_extensions_Ingress_To_v1beta1_Ingress,
autoConvert_extensions_JobCondition_To_v1beta1_JobCondition, autoConvert_extensions_JobCondition_To_v1beta1_JobCondition,
autoConvert_extensions_JobList_To_v1beta1_JobList, autoConvert_extensions_JobList_To_v1beta1_JobList,
@ -5338,6 +5399,7 @@ func init() {
autoConvert_v1beta1_IngressRule_To_extensions_IngressRule, autoConvert_v1beta1_IngressRule_To_extensions_IngressRule,
autoConvert_v1beta1_IngressSpec_To_extensions_IngressSpec, autoConvert_v1beta1_IngressSpec_To_extensions_IngressSpec,
autoConvert_v1beta1_IngressStatus_To_extensions_IngressStatus, autoConvert_v1beta1_IngressStatus_To_extensions_IngressStatus,
autoConvert_v1beta1_IngressTLS_To_extensions_IngressTLS,
autoConvert_v1beta1_Ingress_To_extensions_Ingress, autoConvert_v1beta1_Ingress_To_extensions_Ingress,
autoConvert_v1beta1_JobCondition_To_extensions_JobCondition, autoConvert_v1beta1_JobCondition_To_extensions_JobCondition,
autoConvert_v1beta1_JobList_To_extensions_JobList, autoConvert_v1beta1_JobList_To_extensions_JobList,

View File

@ -1420,6 +1420,16 @@ func deepCopy_v1beta1_IngressSpec(in IngressSpec, out *IngressSpec, c *conversio
} else { } else {
out.Backend = nil out.Backend = nil
} }
if in.TLS != nil {
out.TLS = make([]IngressTLS, len(in.TLS))
for i := range in.TLS {
if err := deepCopy_v1beta1_IngressTLS(in.TLS[i], &out.TLS[i], c); err != nil {
return err
}
}
} else {
out.TLS = nil
}
if in.Rules != nil { if in.Rules != nil {
out.Rules = make([]IngressRule, len(in.Rules)) out.Rules = make([]IngressRule, len(in.Rules))
for i := range in.Rules { for i := range in.Rules {
@ -1440,6 +1450,19 @@ func deepCopy_v1beta1_IngressStatus(in IngressStatus, out *IngressStatus, c *con
return nil return nil
} }
func deepCopy_v1beta1_IngressTLS(in IngressTLS, out *IngressTLS, c *conversion.Cloner) error {
if in.Hosts != nil {
out.Hosts = make([]string, len(in.Hosts))
for i := range in.Hosts {
out.Hosts[i] = in.Hosts[i]
}
} else {
out.Hosts = nil
}
out.SecretName = in.SecretName
return nil
}
func deepCopy_v1beta1_Job(in Job, out *Job, c *conversion.Cloner) error { func deepCopy_v1beta1_Job(in Job, out *Job, c *conversion.Cloner) error {
if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err return err
@ -2040,6 +2063,7 @@ func init() {
deepCopy_v1beta1_IngressRuleValue, deepCopy_v1beta1_IngressRuleValue,
deepCopy_v1beta1_IngressSpec, deepCopy_v1beta1_IngressSpec,
deepCopy_v1beta1_IngressStatus, deepCopy_v1beta1_IngressStatus,
deepCopy_v1beta1_IngressTLS,
deepCopy_v1beta1_Job, deepCopy_v1beta1_Job,
deepCopy_v1beta1_JobCondition, deepCopy_v1beta1_JobCondition,
deepCopy_v1beta1_JobList, deepCopy_v1beta1_JobList,

File diff suppressed because it is too large Load Diff

View File

@ -639,12 +639,35 @@ type IngressSpec struct {
// is optional to allow the loadbalancer controller or defaulting logic to // is optional to allow the loadbalancer controller or defaulting logic to
// specify a global default. // specify a global default.
Backend *IngressBackend `json:"backend,omitempty"` Backend *IngressBackend `json:"backend,omitempty"`
// TLS configuration. Currently the Ingress only supports a single TLS
// port, 443, and assumes TLS termination. If multiple members of this
// list specify different hosts, they will be multiplexed on the same
// port according to the hostname specified through the SNI TLS extension.
TLS []IngressTLS `json:"tls,omitempty"`
// A list of host rules used to configure the Ingress. If unspecified, or // A list of host rules used to configure the Ingress. If unspecified, or
// no rule matches, all traffic is sent to the default backend. // no rule matches, all traffic is sent to the default backend.
Rules []IngressRule `json:"rules,omitempty"` Rules []IngressRule `json:"rules,omitempty"`
// TODO: Add the ability to specify load-balancer IP through claims // TODO: Add the ability to specify load-balancer IP through claims
} }
// IngressTLS describes the transport layer security associated with an Ingress.
type IngressTLS struct {
// Hosts are a list of hosts included in the TLS certificate. The values in
// this list must match the name/s used in the tlsSecret. Defaults to the
// wildcard host setting for the loadbalancer controller fulfilling this
// Ingress, if left unspecified.
Hosts []string `json:"hosts,omitempty"`
// SecretName is the name of the secret used to terminate SSL traffic on 443.
// Field is left optional to allow SSL routing based on SNI hostname alone.
// If the SNI host in a listener conflicts with the "Host" header field used
// by an IngressRule, the SNI host is used for termination and value of the
// Host header is used for routing.
SecretName string `json:"secretName,omitempty"`
// TODO: Consider specifying different modes of termination, protocols etc.
}
// IngressStatus describe the current state of the Ingress. // IngressStatus describe the current state of the Ingress.
type IngressStatus struct { type IngressStatus struct {
// LoadBalancer contains the current status of the load-balancer. // LoadBalancer contains the current status of the load-balancer.

View File

@ -363,6 +363,7 @@ func (IngressRuleValue) SwaggerDoc() map[string]string {
var map_IngressSpec = map[string]string{ var map_IngressSpec = map[string]string{
"": "IngressSpec describes the Ingress the user wishes to exist.", "": "IngressSpec describes the Ingress the user wishes to exist.",
"backend": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default.", "backend": "A default backend capable of servicing requests that don't match any rule. At least one of 'backend' or 'rules' must be specified. This field is optional to allow the loadbalancer controller or defaulting logic to specify a global default.",
"tls": "TLS configuration. Currently the Ingress only supports a single TLS port, 443, and assumes TLS termination. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension.",
"rules": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.", "rules": "A list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.",
} }
@ -379,6 +380,16 @@ func (IngressStatus) SwaggerDoc() map[string]string {
return map_IngressStatus return map_IngressStatus
} }
var map_IngressTLS = map[string]string{
"": "IngressTLS describes the transport layer security associated with an Ingress.",
"hosts": "Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.",
"secretName": "SecretName is the name of the secret used to terminate SSL traffic on 443. Field is left optional to allow SSL routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.",
}
func (IngressTLS) SwaggerDoc() map[string]string {
return map_IngressTLS
}
var map_Job = map[string]string{ var map_Job = map[string]string{
"": "Job represents the configuration of a single job.", "": "Job represents the configuration of a single job.",
"metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",

View File

@ -459,6 +459,20 @@ func ValidateIngressName(name string, prefix bool) (bool, string) {
return apivalidation.NameIsDNSSubdomain(name, prefix) return apivalidation.NameIsDNSSubdomain(name, prefix)
} }
func validateIngressTLS(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// Currently the Ingress only supports HTTP(S), so a secretName is required.
// This will not be the case if we support SSL routing at L4 via SNI.
for i, t := range spec.TLS {
if t.SecretName == "" {
allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("secretName"), spec.TLS[i].SecretName))
}
}
// TODO: Perform a more thorough validation of spec.TLS.Hosts that takes
// the wildcard spec from RFC 6125 into account.
return allErrs
}
// ValidateIngressSpec tests if required fields in the IngressSpec are set. // ValidateIngressSpec tests if required fields in the IngressSpec are set.
func ValidateIngressSpec(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList { func ValidateIngressSpec(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
@ -471,6 +485,9 @@ func ValidateIngressSpec(spec *extensions.IngressSpec, fldPath *field.Path) fiel
if len(spec.Rules) > 0 { if len(spec.Rules) > 0 {
allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...) allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...)
} }
if len(spec.TLS) > 0 {
allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"))...)
}
return allErrs return allErrs
} }

View File

@ -1211,6 +1211,8 @@ func TestValidateIngress(t *testing.T) {
badHostIP := newValid() badHostIP := newValid()
badHostIP.Spec.Rules[0].Host = hostIP badHostIP.Spec.Rules[0].Host = hostIP
badHostIPErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", hostIP) badHostIPErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", hostIP)
noSecretName := newValid()
noSecretName.Spec.TLS = []extensions.IngressTLS{{SecretName: ""}}
errorCases := map[string]extensions.Ingress{ errorCases := map[string]extensions.Ingress{
"spec.backend.serviceName: Required value": servicelessBackend, "spec.backend.serviceName: Required value": servicelessBackend,
@ -1219,6 +1221,7 @@ func TestValidateIngress(t *testing.T) {
"spec.rules[0].host: Invalid value": badHost, "spec.rules[0].host: Invalid value": badHost,
"spec.rules[0].http.paths: Required value": noPaths, "spec.rules[0].http.paths: Required value": noPaths,
"spec.rules[0].http.paths[0].path: Invalid value": noForwardSlashPath, "spec.rules[0].http.paths[0].path: Invalid value": noForwardSlashPath,
"spec.tls[0].secretName: Required value": noSecretName,
} }
errorCases[badPathErr] = badRegexPath errorCases[badPathErr] = badRegexPath
errorCases[badHostIPErr] = badHostIP errorCases[badHostIPErr] = badHostIP

View File

@ -1084,6 +1084,9 @@ func (i *IngressDescriber) describeIngress(ing *extensions.Ingress) (string, err
ns = api.NamespaceSystem ns = api.NamespaceSystem
} }
fmt.Fprintf(out, "Default backend:\t%s (%s)\n", backendStringer(def), i.describeBackend(ns, def)) fmt.Fprintf(out, "Default backend:\t%s (%s)\n", backendStringer(def), i.describeBackend(ns, def))
if len(ing.Spec.TLS) != 0 {
describeIngressTLS(out, ing.Spec.TLS)
}
fmt.Fprint(out, "Rules:\n Host\tPath\tBackends\n") fmt.Fprint(out, "Rules:\n Host\tPath\tBackends\n")
fmt.Fprint(out, " ----\t----\t--------\n") fmt.Fprint(out, " ----\t----\t--------\n")
for _, rules := range ing.Spec.Rules { for _, rules := range ing.Spec.Rules {
@ -1105,6 +1108,14 @@ func (i *IngressDescriber) describeIngress(ing *extensions.Ingress) (string, err
}) })
} }
func describeIngressTLS(out io.Writer, ingTLS []extensions.IngressTLS) {
fmt.Fprintf(out, "TLS:\n")
for _, t := range ingTLS {
fmt.Fprintf(out, " %v terminates %v\n", t.SecretName, strings.Join(t.Hosts, ","))
}
return
}
// TODO: Move from annotations into Ingress status. // TODO: Move from annotations into Ingress status.
func describeIngressAnnotations(out io.Writer, annotations map[string]string) { func describeIngressAnnotations(out io.Writer, annotations map[string]string) {
fmt.Fprintf(out, "Annotations:\n") fmt.Fprintf(out, "Annotations:\n")

View File

@ -45,6 +45,9 @@ var (
defaultLoadBalancer = "127.0.0.1" defaultLoadBalancer = "127.0.0.1"
defaultPath = "/foo" defaultPath = "/foo"
defaultPathMap = map[string]string{defaultPath: defaultBackendName} defaultPathMap = map[string]string{defaultPath: defaultBackendName}
defaultTLS = []extensions.IngressTLS{
{Hosts: []string{"foo.bar.com", "*.bar.com"}, SecretName: "fooSecret"},
}
) )
type IngressRuleValues map[string]string type IngressRuleValues map[string]string
@ -92,6 +95,7 @@ func newIngress(pathMap map[string]string) *extensions.Ingress {
Rules: toIngressRules(map[string]IngressRuleValues{ Rules: toIngressRules(map[string]IngressRuleValues{
defaultHostname: pathMap, defaultHostname: pathMap,
}), }),
TLS: defaultTLS,
}, },
Status: extensions.IngressStatus{ Status: extensions.IngressStatus{
LoadBalancer: api.LoadBalancerStatus{ LoadBalancer: api.LoadBalancerStatus{
@ -139,6 +143,10 @@ func TestUpdate(t *testing.T) {
object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{ object.Spec.Rules = toIngressRules(map[string]IngressRuleValues{
"bar.foo.com": {"/bar": defaultBackendName}, "bar.foo.com": {"/bar": defaultBackendName},
}) })
object.Spec.TLS = append(object.Spec.TLS, extensions.IngressTLS{
Hosts: []string{"*.google.com"},
SecretName: "googleSecret",
})
return object return object
}, },
// invalid updateFunc: ObjeceMeta is not to be tampered with. // invalid updateFunc: ObjeceMeta is not to be tampered with.
@ -160,6 +168,15 @@ func TestUpdate(t *testing.T) {
"foo.bar.com": {"/invalid[": "svc"}}) "foo.bar.com": {"/invalid[": "svc"}})
return object return object
}, },
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Ingress)
object.Spec.TLS = append(object.Spec.TLS, extensions.IngressTLS{
Hosts: []string{"foo.bar.com"},
SecretName: "",
})
return object
},
) )
} }