Merge pull request #2 from kubernetes/master

k
This commit is contained in:
Mourad Maatoug 2017-03-20 17:28:32 +01:00 committed by GitHub
commit 3f67e3e991
47 changed files with 522 additions and 187 deletions

View file

@ -119,7 +119,7 @@ func NewBackendPool(
func(i interface{}) (string, error) { func(i interface{}) (string, error) {
bs := i.(*compute.BackendService) bs := i.(*compute.BackendService)
if !namer.NameBelongsToCluster(bs.Name) { if !namer.NameBelongsToCluster(bs.Name) {
return "", fmt.Errorf("Unrecognized name %v", bs.Name) return "", fmt.Errorf("unrecognized name %v", bs.Name)
} }
port, err := namer.BePort(bs.Name) port, err := namer.BePort(bs.Name)
if err != nil { if err != nil {

View file

@ -47,7 +47,7 @@ func (f *FakeBackendServices) GetBackendService(name string) (*compute.BackendSe
f.calls = append(f.calls, utils.Get) f.calls = append(f.calls, utils.Get)
obj, exists, err := f.backendServices.GetByKey(name) obj, exists, err := f.backendServices.GetByKey(name)
if !exists { if !exists {
return nil, fmt.Errorf("Backend service %v not found", name) return nil, fmt.Errorf("backend service %v not found", name)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -57,7 +57,7 @@ func (f *FakeBackendServices) GetBackendService(name string) (*compute.BackendSe
if name == svc.Name { if name == svc.Name {
return svc, nil return svc, nil
} }
return nil, fmt.Errorf("Backend service %v not found", name) return nil, fmt.Errorf("backend service %v not found", name)
} }
// CreateBackendService fakes backend service creation. // CreateBackendService fakes backend service creation.
@ -77,7 +77,7 @@ func (f *FakeBackendServices) DeleteBackendService(name string) error {
f.calls = append(f.calls, utils.Delete) f.calls = append(f.calls, utils.Delete)
svc, exists, err := f.backendServices.GetByKey(name) svc, exists, err := f.backendServices.GetByKey(name)
if !exists { if !exists {
return fmt.Errorf("Backend service %v not found", name) return fmt.Errorf("backend service %v not found", name)
} }
if err != nil { if err != nil {
return err return err

View file

@ -290,7 +290,7 @@ func (lbc *LoadBalancerController) storesSynced() bool {
func (lbc *LoadBalancerController) sync(key string) (err error) { func (lbc *LoadBalancerController) sync(key string) (err error) {
if !lbc.hasSynced() { if !lbc.hasSynced() {
time.Sleep(storeSyncPollPeriod) time.Sleep(storeSyncPollPeriod)
return fmt.Errorf("Waiting for stores to sync") return fmt.Errorf("waiting for stores to sync")
} }
glog.V(3).Infof("Syncing %v", key) glog.V(3).Infof("Syncing %v", key)
@ -326,7 +326,7 @@ func (lbc *LoadBalancerController) sync(key string) (err error) {
defer func() { defer func() {
if deferErr := lbc.CloudClusterManager.GC(lbNames, nodePorts); deferErr != nil { if deferErr := lbc.CloudClusterManager.GC(lbNames, nodePorts); deferErr != nil {
err = fmt.Errorf("Error during sync %v, error during GC %v", err, deferErr) err = fmt.Errorf("error during sync %v, error during GC %v", err, deferErr)
} }
glog.V(3).Infof("Finished syncing %v", key) glog.V(3).Infof("Finished syncing %v", key)
}() }()
@ -343,7 +343,7 @@ func (lbc *LoadBalancerController) sync(key string) (err error) {
if ingExists { if ingExists {
lbc.recorder.Eventf(obj.(*extensions.Ingress), api.EventTypeWarning, eventMsg, err.Error()) lbc.recorder.Eventf(obj.(*extensions.Ingress), api.EventTypeWarning, eventMsg, err.Error())
} else { } else {
err = fmt.Errorf("%v Error: %v", eventMsg, err) err = fmt.Errorf("%v, error: %v", eventMsg, err)
} }
syncError = err syncError = err
} }

View file

@ -65,11 +65,11 @@ func (t *apiServerTLSLoader) load(ing *extensions.Ingress) (*loadbalancers.TLSCe
} }
cert, ok := secret.Data[api.TLSCertKey] cert, ok := secret.Data[api.TLSCertKey]
if !ok { if !ok {
return nil, fmt.Errorf("Secret %v has no private key", secretName) return nil, fmt.Errorf("secret %v has no private key", secretName)
} }
key, ok := secret.Data[api.TLSPrivateKeyKey] key, ok := secret.Data[api.TLSPrivateKeyKey]
if !ok { if !ok {
return nil, fmt.Errorf("Secret %v has no cert", secretName) return nil, fmt.Errorf("secret %v has no cert", secretName)
} }
certs := &loadbalancers.TLSCerts{Key: string(key), Cert: string(cert)} certs := &loadbalancers.TLSCerts{Key: string(key), Cert: string(cert)}
if err := t.validate(certs); err != nil { if err := t.validate(certs); err != nil {
@ -95,5 +95,5 @@ func (f *fakeTLSSecretLoader) load(ing *extensions.Ingress) (*loadbalancers.TLSC
return cert, nil return cert, nil
} }
} }
return nil, fmt.Errorf("Couldn't find secret for ingress %v", ing.Name) return nil, fmt.Errorf("couldn't find secret for ingress %v", ing.Name)
} }

View file

@ -233,7 +233,7 @@ func (s *StoreToIngressLister) GetServiceIngress(svc *api.Service) (ings []exten
} }
} }
if len(ings) == 0 { if len(ings) == 0 {
err = fmt.Errorf("No ingress for service %v", svc.Name) err = fmt.Errorf("no ingress for service %v", svc.Name)
} }
return return
} }
@ -315,7 +315,7 @@ func (t *GCETranslator) toGCEBackend(be *extensions.IngressBackend, ns string) (
backend, err := t.CloudClusterManager.backendPool.Get(int64(port)) backend, err := t.CloudClusterManager.backendPool.Get(int64(port))
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"No GCE backend exists for port %v, kube backend %+v", port, be) "no GCE backend exists for port %v, kube backend %+v", port, be)
} }
return backend, nil return backend, nil
} }
@ -332,7 +332,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
}) })
if !exists { if !exists {
return invalidPort, errorNodePortNotFound{be, fmt.Errorf( return invalidPort, errorNodePortNotFound{be, fmt.Errorf(
"Service %v/%v not found in store", namespace, be.ServiceName)} "service %v/%v not found in store", namespace, be.ServiceName)}
} }
if err != nil { if err != nil {
return invalidPort, errorNodePortNotFound{be, err} return invalidPort, errorNodePortNotFound{be, err}
@ -356,7 +356,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
return nodePort, nil return nodePort, nil
} }
return invalidPort, errorNodePortNotFound{be, fmt.Errorf( return invalidPort, errorNodePortNotFound{be, fmt.Errorf(
"Could not find matching nodeport from service.")} "could not find matching nodeport from service")}
} }
// toNodePorts converts a pathlist to a flat list of nodeports. // toNodePorts converts a pathlist to a flat list of nodeports.
@ -374,7 +374,7 @@ func (t *GCETranslator) toNodePorts(ings *extensions.IngressList) []int64 {
} }
for _, rule := range ing.Spec.Rules { for _, rule := range ing.Spec.Rules {
if rule.HTTP == nil { if rule.HTTP == nil {
glog.Errorf("Ignoring non http Ingress rule.") glog.Errorf("ignoring non http Ingress rule")
continue continue
} }
for _, path := range rule.HTTP.Paths { for _, path := range rule.HTTP.Paths {
@ -411,7 +411,7 @@ func (t *GCETranslator) GetZoneForNode(name string) (string, error) {
return getZone(n), nil return getZone(n), nil
} }
} }
return "", fmt.Errorf("Node not found %v", name) return "", fmt.Errorf("node not found %v", name)
} }
// ListZones returns a list of zones this Kubernetes cluster spans. // ListZones returns a list of zones this Kubernetes cluster spans.

View file

@ -35,7 +35,7 @@ func (f *fakeFirewallRules) GetFirewall(name string) (*compute.Firewall, error)
return rule, nil return rule, nil
} }
} }
return nil, fmt.Errorf("Firewall rule %v not found.", name) return nil, fmt.Errorf("firewall rule %v not found", name)
} }
func (f *fakeFirewallRules) CreateFirewall(name, msgTag string, srcRange netset.IPNet, ports []int64, hosts []string) error { func (f *fakeFirewallRules) CreateFirewall(name, msgTag string, srcRange netset.IPNet, ports []int64, hosts []string) error {
@ -66,7 +66,7 @@ func (f *fakeFirewallRules) DeleteFirewall(name string) error {
firewalls = append(firewalls, rule) firewalls = append(firewalls, rule)
} }
if !exists { if !exists {
return fmt.Errorf("Failed to find health check %v", name) return fmt.Errorf("failed to find health check %v", name)
} }
f.fw = firewalls f.fw = firewalls
return nil return nil
@ -95,7 +95,7 @@ func (f *fakeFirewallRules) UpdateFirewall(name, msgTag string, srcRange netset.
if exists { if exists {
return nil return nil
} }
return fmt.Errorf("Update failed for rule %v, srcRange %v ports %v, rule not found", name, srcRange, ports) return fmt.Errorf("update failed for rule %v, srcRange %v ports %v, rule not found", name, srcRange, ports)
} }
// NewFakeFirewallRules creates a fake for firewall rules. // NewFakeFirewallRules creates a fake for firewall rules.

View file

@ -60,7 +60,7 @@ func (f *FakeHealthChecks) GetHttpHealthCheck(name string) (*compute.HttpHealthC
return h, nil return h, nil
} }
} }
return nil, fmt.Errorf("Health check %v not found.", name) return nil, fmt.Errorf("health check %v not found", name)
} }
// DeleteHttpHealthCheck fakes out deleting a http health check. // DeleteHttpHealthCheck fakes out deleting a http health check.
@ -75,7 +75,7 @@ func (f *FakeHealthChecks) DeleteHttpHealthCheck(name string) error {
healthChecks = append(healthChecks, h) healthChecks = append(healthChecks, h)
} }
if !exists { if !exists {
return fmt.Errorf("Failed to find health check %v", name) return fmt.Errorf("failed to find health check %v", name)
} }
f.hc = healthChecks f.hc = healthChecks
return nil return nil
@ -94,7 +94,7 @@ func (f *FakeHealthChecks) UpdateHttpHealthCheck(hc *compute.HttpHealthCheck) er
} }
} }
if !found { if !found {
return fmt.Errorf("Cannot update a non-existent health check %v", hc.Name) return fmt.Errorf("cannot update a non-existent health check %v", hc.Name)
} }
f.hc = healthChecks f.hc = healthChecks
return nil return nil

View file

@ -75,7 +75,7 @@ func (f *FakeInstanceGroups) GetInstanceGroup(name, zone string) (*compute.Insta
} }
} }
// TODO: Return googleapi 404 error // TODO: Return googleapi 404 error
return nil, fmt.Errorf("Instance group %v not found", name) return nil, fmt.Errorf("instance group %v not found", name)
} }
// CreateInstanceGroup fakes instance group creation. // CreateInstanceGroup fakes instance group creation.
@ -97,7 +97,7 @@ func (f *FakeInstanceGroups) DeleteInstanceGroup(name, zone string) error {
newGroups = append(newGroups, ig) newGroups = append(newGroups, ig)
} }
if !found { if !found {
return fmt.Errorf("Instance Group %v not found", name) return fmt.Errorf("instance group %v not found", name)
} }
f.instanceGroups = newGroups f.instanceGroups = newGroups
return nil return nil

View file

@ -109,7 +109,7 @@ func (f *FakeLoadBalancers) GetGlobalForwardingRule(name string) (*compute.Forwa
return f.Fw[i], nil return f.Fw[i], nil
} }
} }
return nil, fmt.Errorf("Forwarding rule %v not found", name) return nil, fmt.Errorf("forwarding rule %v not found", name)
} }
// CreateGlobalForwardingRule fakes forwarding rule creation. // CreateGlobalForwardingRule fakes forwarding rule creation.
@ -176,7 +176,7 @@ func (f *FakeLoadBalancers) GetUrlMap(name string) (*compute.UrlMap, error) {
return f.Um[i], nil return f.Um[i], nil
} }
} }
return nil, fmt.Errorf("Url Map %v not found", name) return nil, fmt.Errorf("url map %v not found", name)
} }
// CreateUrlMap fakes url-map creation. // CreateUrlMap fakes url-map creation.
@ -226,7 +226,7 @@ func (f *FakeLoadBalancers) GetTargetHttpProxy(name string) (*compute.TargetHttp
return f.Tp[i], nil return f.Tp[i], nil
} }
} }
return nil, fmt.Errorf("Targetproxy %v not found", name) return nil, fmt.Errorf("target http proxy %v not found", name)
} }
// CreateTargetHttpProxy fakes creating a target http proxy. // CreateTargetHttpProxy fakes creating a target http proxy.
@ -275,7 +275,7 @@ func (f *FakeLoadBalancers) GetTargetHttpsProxy(name string) (*compute.TargetHtt
return f.Tps[i], nil return f.Tps[i], nil
} }
} }
return nil, fmt.Errorf("Targetproxy %v not found", name) return nil, fmt.Errorf("target https proxy %v not found", name)
} }
// CreateTargetHttpsProxy fakes creating a target http proxy. // CreateTargetHttpsProxy fakes creating a target http proxy.
@ -326,7 +326,7 @@ func (f *FakeLoadBalancers) SetSslCertificateForTargetHttpsProxy(proxy *compute.
} }
} }
if !found { if !found {
return fmt.Errorf("Failed to find proxy %v", proxy.Name) return fmt.Errorf("failed to find proxy %v", proxy.Name)
} }
return nil return nil
} }
@ -415,7 +415,7 @@ func (f *FakeLoadBalancers) GetGlobalStaticIP(name string) (*compute.Address, er
return f.IP[i], nil return f.IP[i], nil
} }
} }
return nil, fmt.Errorf("Static IP %v not found", name) return nil, fmt.Errorf("static IP %v not found", name)
} }
// DeleteGlobalStaticIP fakes out static IP deletion. // DeleteGlobalStaticIP fakes out static IP deletion.
@ -441,7 +441,7 @@ func (f *FakeLoadBalancers) GetSslCertificate(name string) (*compute.SslCertific
return f.Certs[i], nil return f.Certs[i], nil
} }
} }
return nil, fmt.Errorf("Cert %v not found", name) return nil, fmt.Errorf("cert %v not found", name)
} }
// CreateSslCertificate fakes out certificate creation. // CreateSslCertificate fakes out certificate creation.

View file

@ -106,7 +106,7 @@ func (l *L7s) Get(name string) (*L7, error) {
name = l.namer.LBName(name) name = l.namer.LBName(name)
lb, exists := l.snapshotter.Get(name) lb, exists := l.snapshotter.Get(name)
if !exists { if !exists {
return nil, fmt.Errorf("Loadbalancer %v not in pool", name) return nil, fmt.Errorf("loadbalancer %v not in pool", name)
} }
return lb.(*L7), nil return lb.(*L7), nil
} }
@ -292,7 +292,7 @@ type L7 struct {
func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) { func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
if l.glbcDefaultBackend == nil { if l.glbcDefaultBackend == nil {
return fmt.Errorf("Cannot create urlmap without default backend.") return fmt.Errorf("cannot create urlmap without default backend")
} }
urlMapName := l.namer.Truncate(fmt.Sprintf("%v-%v", urlMapPrefix, l.Name)) urlMapName := l.namer.Truncate(fmt.Sprintf("%v-%v", urlMapPrefix, l.Name))
urlMap, _ := l.cloud.GetUrlMap(urlMapName) urlMap, _ := l.cloud.GetUrlMap(urlMapName)
@ -313,7 +313,7 @@ func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
func (l *L7) checkProxy() (err error) { func (l *L7) checkProxy() (err error) {
if l.um == nil { if l.um == nil {
return fmt.Errorf("Cannot create proxy without urlmap.") return fmt.Errorf("cannot create proxy without urlmap")
} }
proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetProxyPrefix, l.Name)) proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetProxyPrefix, l.Name))
proxy, _ := l.cloud.GetTargetHttpProxy(proxyName) proxy, _ := l.cloud.GetTargetHttpProxy(proxyName)
@ -362,7 +362,7 @@ func (l *L7) checkSSLCert() (err error) {
return err return err
} }
if cert == nil { if cert == nil {
return fmt.Errorf("Cannot find existing sslCertificate %v for %v", certName, l.Name) return fmt.Errorf("cannot find existing sslCertificate %v for %v", certName, l.Name)
} }
glog.Infof("Using existing sslCertificate %v for %v", certName, l.Name) glog.Infof("Using existing sslCertificate %v for %v", certName, l.Name)
@ -429,7 +429,7 @@ func (l *L7) checkHttpsProxy() (err error) {
return nil return nil
} }
if l.um == nil { if l.um == nil {
return fmt.Errorf("No UrlMap for %v, will not create HTTPS proxy.", l.Name) return fmt.Errorf("no UrlMap for %v, will not create HTTPS proxy", l.Name)
} }
proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetHTTPSProxyPrefix, l.Name)) proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetHTTPSProxyPrefix, l.Name))
proxy, _ := l.cloud.GetTargetHttpsProxy(proxyName) proxy, _ := l.cloud.GetTargetHttpsProxy(proxyName)
@ -535,7 +535,7 @@ func (l *L7) getEffectiveIP() (string, bool) {
func (l *L7) checkHttpForwardingRule() (err error) { func (l *L7) checkHttpForwardingRule() (err error) {
if l.tp == nil { if l.tp == nil {
return fmt.Errorf("Cannot create forwarding rule without proxy.") return fmt.Errorf("cannot create forwarding rule without proxy")
} }
name := l.namer.Truncate(fmt.Sprintf("%v-%v", forwardingRulePrefix, l.Name)) name := l.namer.Truncate(fmt.Sprintf("%v-%v", forwardingRulePrefix, l.Name))
address, _ := l.getEffectiveIP() address, _ := l.getEffectiveIP()
@ -565,7 +565,7 @@ func (l *L7) checkHttpsForwardingRule() (err error) {
// checkStaticIP reserves a static IP allocated to the Forwarding Rule. // checkStaticIP reserves a static IP allocated to the Forwarding Rule.
func (l *L7) checkStaticIP() (err error) { func (l *L7) checkStaticIP() (err error) {
if l.fw == nil || l.fw.IPAddress == "" { if l.fw == nil || l.fw.IPAddress == "" {
return fmt.Errorf("Will not create static IP without a forwarding rule.") return fmt.Errorf("will not create static IP without a forwarding rule")
} }
// Don't manage staticIPs if the user has specified an IP. // Don't manage staticIPs if the user has specified an IP.
if address, manageStaticIP := l.getEffectiveIP(); !manageStaticIP { if address, manageStaticIP := l.getEffectiveIP(); !manageStaticIP {
@ -704,7 +704,7 @@ func getNameForPathMatcher(hostRule string) string {
// pathmatcher of the host. // pathmatcher of the host.
func (l *L7) UpdateUrlMap(ingressRules utils.GCEURLMap) error { func (l *L7) UpdateUrlMap(ingressRules utils.GCEURLMap) error {
if l.um == nil { if l.um == nil {
return fmt.Errorf("Cannot add url without an urlmap.") return fmt.Errorf("cannot add url without an urlmap")
} }
glog.V(3).Infof("Updating urlmap for l7 %v", l.Name) glog.V(3).Infof("Updating urlmap for l7 %v", l.Name)

View file

@ -316,7 +316,7 @@ func useDefaultOrLookupVault(cfgVault *storage.ConfigMapVault, cm_key, default_n
// 2. No such key in config map - found=false, err=nil // 2. No such key in config map - found=false, err=nil
// 3. Apiserver flake - found=false, err!=nil // 3. Apiserver flake - found=false, err!=nil
// It is not safe to proceed in 3. // It is not safe to proceed in 3.
return "", fmt.Errorf("Failed to retrieve %v: %v, returning empty name", cm_key, err) return "", fmt.Errorf("failed to retrieve %v: %v, returning empty name", cm_key, err)
} else if !found { } else if !found {
// Not found but safe to proceed. // Not found but safe to proceed.
return "", nil return "", nil

View file

@ -95,12 +95,12 @@ func (c *ConfigMapVault) Put(key, val string) error {
glog.Infof("Configmap %v will be updated with %v = %v", cfgMapKey, key, val) glog.Infof("Configmap %v will be updated with %v = %v", cfgMapKey, key, val)
} }
if err := c.ConfigMapStore.Update(apiObj); err != nil { if err := c.ConfigMapStore.Update(apiObj); err != nil {
return fmt.Errorf("Failed to update %v: %v", cfgMapKey, err) return fmt.Errorf("failed to update %v: %v", cfgMapKey, err)
} }
} else { } else {
apiObj.Data = map[string]string{key: val} apiObj.Data = map[string]string{key: val}
if err := c.ConfigMapStore.Add(apiObj); err != nil { if err := c.ConfigMapStore.Add(apiObj); err != nil {
return fmt.Errorf("Failed to add %v: %v", cfgMapKey, err) return fmt.Errorf("failed to add %v: %v", cfgMapKey, err)
} }
} }
glog.Infof("Successfully stored key %v = %v in config map %v", key, val, cfgMapKey) glog.Infof("Successfully stored key %v = %v in config map %v", key, val, cfgMapKey)
@ -176,7 +176,7 @@ func (a *APIServerConfigMapStore) Delete(obj interface{}) error {
func (a *APIServerConfigMapStore) GetByKey(key string) (item interface{}, exists bool, err error) { func (a *APIServerConfigMapStore) GetByKey(key string) (item interface{}, exists bool, err error) {
nsName := strings.Split(key, "/") nsName := strings.Split(key, "/")
if len(nsName) != 2 { if len(nsName) != 2 {
return nil, false, fmt.Errorf("Failed to get key %v, unexpecte format, expecting ns/name", key) return nil, false, fmt.Errorf("failed to get key %v, unexpecte format, expecting ns/name", key)
} }
ns, name := nsName[0], nsName[1] ns, name := nsName[0], nsName[1]
cfg, err := a.client.Core().ConfigMaps(ns).Get(name) cfg, err := a.client.Core().ConfigMaps(ns).Get(name)

View file

@ -223,11 +223,11 @@ func (n *Namer) BePort(beName string) (string, error) {
} }
match := r.FindStringSubmatch(beName) match := r.FindStringSubmatch(beName)
if len(match) < 2 { if len(match) < 2 {
return "", fmt.Errorf("Unable to lookup port for %v", beName) return "", fmt.Errorf("unable to lookup port for %v", beName)
} }
_, err = strconv.Atoi(match[1]) _, err = strconv.Atoi(match[1])
if err != nil { if err != nil {
return "", fmt.Errorf("Unexpected regex match: %v", beName) return "", fmt.Errorf("unexpected regex match: %v", beName)
} }
return match[1], nil return match[1], nil
} }

View file

@ -1,5 +1,74 @@
Changelog Changelog
### 0.9-beta.3
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3`
*New Features:*
- Custom log formats using `log-format-upstream` directive in the configuration configmap.
- Force redirect to SSL using the annotation `ingress.kubernetes.io/force-ssl-redirect`
- Prometheus metric for VTS status module (transparent, just enable vts stats)
- Improved external authentication adding `ingress.kubernetes.io/auth-signin` annotation. Please check this [example](https://github.com/kubernetes/ingress/tree/master/examples/external-auth/nginx)
*Breaking changes:*
- `ssl-dh-param` configuration in configmap is now the name of a secret that contains the Diffie-Hellman key
*Changes:*
- [X] [#433](https://github.com/kubernetes/ingress/pull/433) close over the ingress variable or the last assignment will be used
- [X] [#424](https://github.com/kubernetes/ingress/pull/424) Manually sync secrets from certificate authentication annotations
- [X] [#423](https://github.com/kubernetes/ingress/pull/423) Scrap json metrics from nginx vts module when enabled
- [X] [#418](https://github.com/kubernetes/ingress/pull/418) Only update Ingress status for the configured class
- [X] [#415](https://github.com/kubernetes/ingress/pull/415) Improve external authentication docs
- [X] [#410](https://github.com/kubernetes/ingress/pull/410) Add support for "signin url"
- [X] [#409](https://github.com/kubernetes/ingress/pull/409) Allow custom http2 header sizes
- [X] [#408](https://github.com/kubernetes/ingress/pull/408) Review docs
- [X] [#406](https://github.com/kubernetes/ingress/pull/406) Add debug info and fix spelling
- [X] [#402](https://github.com/kubernetes/ingress/pull/402) allow specifying custom dh param
- [X] [#397](https://github.com/kubernetes/ingress/pull/397) Fix external auth
- [X] [#394](https://github.com/kubernetes/ingress/pull/394) Update README.md
- [X] [#392](https://github.com/kubernetes/ingress/pull/392) Fix http2 header size
- [X] [#391](https://github.com/kubernetes/ingress/pull/391) remove tmp nginx-diff files
- [X] [#390](https://github.com/kubernetes/ingress/pull/390) Fix RateLimit comment
- [X] [#385](https://github.com/kubernetes/ingress/pull/385) add Copyright
- [X] [#382](https://github.com/kubernetes/ingress/pull/382) Ingress Fake Certificate generation
- [X] [#380](https://github.com/kubernetes/ingress/pull/380) Fix custom log format
- [X] [#373](https://github.com/kubernetes/ingress/pull/373) Cleanup
- [X] [#371](https://github.com/kubernetes/ingress/pull/371) add configuration to disable listening on ipv6
- [X] [#370](https://github.com/kubernetes/ingress/pull/270) Add documentation for ingress.kubernetes.io/force-ssl-redirect
- [X] [#369](https://github.com/kubernetes/ingress/pull/369) Minor text fix for "ApiServer"
- [X] [#367](https://github.com/kubernetes/ingress/pull/367) BuildLogFormatUpstream was always using the default log-format
- [X] [#366](https://github.com/kubernetes/ingress/pull/366) add_judgment
- [X] [#365](https://github.com/kubernetes/ingress/pull/365) add ForceSSLRedirect ingress annotation
- [X] [#364](https://github.com/kubernetes/ingress/pull/364) Fix error caused by increasing proxy_buffer_size (#363)
- [X] [#362](https://github.com/kubernetes/ingress/pull/362) Fix ingress class
- [X] [#360](https://github.com/kubernetes/ingress/pull/360) add example of 'run multiple nginx ingress controllers as a deployment'
- [X] [#358](https://github.com/kubernetes/ingress/pull/358) Checks if the TLS secret contains a valid keypair structure
- [X] [#356](https://github.com/kubernetes/ingress/pull/356) Disable listen only on ipv6 and fix proxy_protocol
- [X] [#354](https://github.com/kubernetes/ingress/pull/354) add judgment
- [X] [#352](https://github.com/kubernetes/ingress/pull/352) Add ability to customize upstream and stream log format
- [X] [#351](https://github.com/kubernetes/ingress/pull/351) Enable custom election id for status sync.
- [X] [#347](https://github.com/kubernetes/ingress/pull/347) Fix client source IP address
- [X] [#345](https://github.com/kubernetes/ingress/pull/345) Fix lint error
- [X] [#344](https://github.com/kubernetes/ingress/pull/344) Refactoring of TCP and UDP services
- [X] [#343](https://github.com/kubernetes/ingress/pull/343) Fix node lister when --watch-namespace is used
- [X] [#341](https://github.com/kubernetes/ingress/pull/341) Do not run coverage check in the default target.
- [X] [#340](https://github.com/kubernetes/ingress/pull/340) Add support for specify proxy cookie path/domain
- [X] [#337](https://github.com/kubernetes/ingress/pull/337) Fix for formatting error introduced in #304
- [X] [#335](https://github.com/kubernetes/ingress/pull/335) Fix for vet complaints:
- [X] [#332](https://github.com/kubernetes/ingress/pull/332) Add annotation to customize nginx configuration
- [X] [#331](https://github.com/kubernetes/ingress/pull/331) Correct spelling mistake
- [X] [#328](https://github.com/kubernetes/ingress/pull/328) fix misspell "affinity" in main.go
- [X] [#326](https://github.com/kubernetes/ingress/pull/326) add nginx daemonset example
- [X] [#311](https://github.com/kubernetes/ingress/pull/311) Sort stream service ports to avoid extra reloads
- [X] [#307](https://github.com/kubernetes/ingress/pull/307) Add docs for body-size annotation
- [X] [#306](https://github.com/kubernetes/ingress/pull/306) modify nginx readme
- [X] [#304](https://github.com/kubernetes/ingress/pull/304) change 'buildSSPassthrouthUpstreams' to 'buildSSLPassthroughUpstreams'
### 0.9-beta.2 ### 0.9-beta.2
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2` **Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2`

View file

@ -3,7 +3,7 @@ all: push
BUILDTAGS= BUILDTAGS=
# Use the 0.0 tag for testing, it shouldn't clobber any release builds # Use the 0.0 tag for testing, it shouldn't clobber any release builds
RELEASE?=0.9.0-beta.2 RELEASE?=0.9.0-beta.3
PREFIX?=gcr.io/google_containers/nginx-ingress-controller PREFIX?=gcr.io/google_containers/nginx-ingress-controller
GOOS?=linux GOOS?=linux
DOCKER?=gcloud docker -- DOCKER?=gcloud docker --
@ -21,7 +21,7 @@ build: clean
-ldflags "-s -w -X ${PKG}/pkg/version.RELEASE=${RELEASE} -X ${PKG}/pkg/version.COMMIT=${COMMIT} -X ${PKG}/pkg/version.REPO=${REPO_INFO}" \ -ldflags "-s -w -X ${PKG}/pkg/version.RELEASE=${RELEASE} -X ${PKG}/pkg/version.COMMIT=${COMMIT} -X ${PKG}/pkg/version.REPO=${REPO_INFO}" \
-o rootfs/nginx-ingress-controller ${PKG}/pkg/cmd/controller -o rootfs/nginx-ingress-controller ${PKG}/pkg/cmd/controller
container: container: build
$(DOCKER) build --pull -t $(PREFIX):$(RELEASE) rootfs $(DOCKER) build --pull -t $(PREFIX):$(RELEASE) rootfs
push: container push: container
@ -50,4 +50,4 @@ vet:
@go vet $(shell go list ${PKG}/... | grep -v vendor) @go vet $(shell go list ${PKG}/... | grep -v vendor)
clean: clean:
rm -f nginx-ingress-controller rm -f rootfs/nginx-ingress-controller

View file

@ -40,6 +40,7 @@ The following annotations are supported:
|Name |type| |Name |type|
|---------------------------|------| |---------------------------|------|
|[ingress.kubernetes.io/add-base-url](#rewrite)|true or false| |[ingress.kubernetes.io/add-base-url](#rewrite)|true or false|
|[ingress.kubernetes.io/app-root](#rewrite)|string|
|[ingress.kubernetes.io/affinity](#session-affinity)|true or false| |[ingress.kubernetes.io/affinity](#session-affinity)|true or false|
|[ingress.kubernetes.io/auth-realm](#authentication)|string| |[ingress.kubernetes.io/auth-realm](#authentication)|string|
|[ingress.kubernetes.io/auth-secret](#authentication)|string| |[ingress.kubernetes.io/auth-secret](#authentication)|string|
@ -174,7 +175,9 @@ Set the annotation `ingress.kubernetes.io/rewrite-target` to the path expected b
If the application contains relative links it is possible to add an additional annotation `ingress.kubernetes.io/add-base-url` that will prepend a [`base` tag](https://developer.mozilla.org/en/docs/Web/HTML/Element/base) in the header of the returned HTML from the backend. If the application contains relative links it is possible to add an additional annotation `ingress.kubernetes.io/add-base-url` that will prepend a [`base` tag](https://developer.mozilla.org/en/docs/Web/HTML/Element/base) in the header of the returned HTML from the backend.
Please check the [rewrite](examples/rewrite/README.md) example. If the Application Root is exposed in a different path and needs to be redirected, the annotation `ingress.kubernetes.io/app-root` might be used.
Please check the [rewrite](/examples/rewrite/README.md) example.
### Rate limiting ### Rate limiting

View file

@ -441,7 +441,7 @@ func (n NGINXController) Check(_ *http.Request) error {
} }
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode != 200 { if res.StatusCode != 200 {
return fmt.Errorf("Ingress controller is not healthy") return fmt.Errorf("ingress controller is not healthy")
} }
return nil return nil
} }

View file

@ -138,7 +138,7 @@ func (bit BoolToFloat64) UnmarshalJSON(data []byte) error {
} else if asString == "0" || asString == "false" { } else if asString == "0" || asString == "false" {
bit = 0 bit = 0
} else { } else {
return fmt.Errorf(fmt.Sprintf("Boolean unmarshal error: invalid input %s", asString)) return fmt.Errorf(fmt.Sprintf("boolean unmarshal error: invalid input %s", asString))
} }
return nil return nil
} }

View file

@ -205,7 +205,13 @@ func buildLocation(input interface{}) string {
if path == slash { if path == slash {
return fmt.Sprintf("~* %s", path) return fmt.Sprintf("~* %s", path)
} }
return fmt.Sprintf("~* ^%s", path) // baseuri regex will parse basename from the given location
baseuri := `(?<baseuri>.*)`
if !strings.HasSuffix(path, slash) {
// Not treat the slash after "location path" as a part of baseuri
baseuri = fmt.Sprintf(`\/?%s`, baseuri)
}
return fmt.Sprintf(`~* ^%s%s`, path, baseuri)
} }
return path return path
@ -273,13 +279,10 @@ func buildProxyPass(b interface{}, loc interface{}) string {
if len(location.Redirect.Target) > 0 { if len(location.Redirect.Target) > 0 {
abu := "" abu := ""
if location.Redirect.AddBaseURL { if location.Redirect.AddBaseURL {
bPath := location.Redirect.Target // path has a slash suffix, so that it can be connected with baseuri directly
if !strings.HasSuffix(bPath, slash) { bPath := fmt.Sprintf("%s%s", path, "$baseuri")
bPath = fmt.Sprintf("%s/", bPath) abu = fmt.Sprintf(`subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host%v">' r;
} subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host%v">' r;
abu = fmt.Sprintf(`subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name%v">' r;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name%v">' r;
`, bPath, bPath) `, bPath, bPath)
} }

View file

@ -45,33 +45,43 @@ var (
rewrite /(.*) /jenkins/$1 break; rewrite /(.*) /jenkins/$1 break;
proxy_pass http://upstream-name; proxy_pass http://upstream-name;
`, false}, `, false},
"redirect /something to /": {"/something", "/", "~* ^/something", ` "redirect /something to /": {"/something", "/", `~* ^/something\/?(?<baseuri>.*)`, `
rewrite /something/(.*) /$1 break; rewrite /something/(.*) /$1 break;
rewrite /something / break; rewrite /something / break;
proxy_pass http://upstream-name; proxy_pass http://upstream-name;
`, false}, `, false},
"redirect /something-complex to /not-root": {"/something-complex", "/not-root", "~* ^/something-complex", ` "redirect /end-with-slash/ to /not-root": {"/end-with-slash/", "/not-root", "~* ^/end-with-slash/(?<baseuri>.*)", `
rewrite /end-with-slash/(.*) /not-root/$1 break;
proxy_pass http://upstream-name;
`, false},
"redirect /something-complex to /not-root": {"/something-complex", "/not-root", `~* ^/something-complex\/?(?<baseuri>.*)`, `
rewrite /something-complex/(.*) /not-root/$1 break; rewrite /something-complex/(.*) /not-root/$1 break;
proxy_pass http://upstream-name; proxy_pass http://upstream-name;
`, false}, `, false},
"redirect / to /jenkins and rewrite": {"/", "/jenkins", "~* /", ` "redirect / to /jenkins and rewrite": {"/", "/jenkins", "~* /", `
rewrite /(.*) /jenkins/$1 break; rewrite /(.*) /jenkins/$1 break;
proxy_pass http://upstream-name; proxy_pass http://upstream-name;
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/jenkins/">' r; subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/$baseuri">' r;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name/jenkins/">' r; subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/$baseuri">' r;
`, true}, `, true},
"redirect /something to / and rewrite": {"/something", "/", "~* ^/something", ` "redirect /something to / and rewrite": {"/something", "/", `~* ^/something\/?(?<baseuri>.*)`, `
rewrite /something/(.*) /$1 break; rewrite /something/(.*) /$1 break;
rewrite /something / break; rewrite /something / break;
proxy_pass http://upstream-name; proxy_pass http://upstream-name;
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/">' r; subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/something/$baseuri">' r;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name/">' r; subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/something/$baseuri">' r;
`, true}, `, true},
"redirect /something-complex to /not-root and rewrite": {"/something-complex", "/not-root", "~* ^/something-complex", ` "redirect /end-with-slash/ to /not-root and rewrite": {"/end-with-slash/", "/not-root", `~* ^/end-with-slash/(?<baseuri>.*)`, `
rewrite /end-with-slash/(.*) /not-root/$1 break;
proxy_pass http://upstream-name;
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/end-with-slash/$baseuri">' r;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/end-with-slash/$baseuri">' r;
`, true},
"redirect /something-complex to /not-root and rewrite": {"/something-complex", "/not-root", `~* ^/something-complex\/?(?<baseuri>.*)`, `
rewrite /something-complex/(.*) /not-root/$1 break; rewrite /something-complex/(.*) /not-root/$1 break;
proxy_pass http://upstream-name; proxy_pass http://upstream-name;
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/not-root/">' r; subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/something-complex/$baseuri">' r;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name/not-root/">' r; subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/something-complex/$baseuri">' r;
`, true}, `, true},
} }
) )

View file

@ -16,10 +16,8 @@ FROM gcr.io/google_containers/nginx-slim:0.14
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \ RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
diffutils \ diffutils \
ssl-cert \
--no-install-recommends \ --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/*
&& make-ssl-cert generate-default-snakeoil --force-overwrite
COPY . / COPY . /

View file

@ -237,6 +237,18 @@ http {
ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }}; ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }};
{{ end }} {{ end }}
{{ if (or $location.Redirect.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect)) }}
# enforce ssl on server side
if ($pass_access_scheme = http) {
return 301 https://$host$request_uri;
}
{{ end }}
{{ if not (empty $location.Redirect.AppRoot)}}
if ($uri = /) {
return 302 {{ $location.Redirect.AppRoot }};
}
{{ end }}
{{ if not (empty $authPath) }} {{ if not (empty $authPath) }}
location = {{ $authPath }} { location = {{ $authPath }} {
internal; internal;
@ -279,12 +291,7 @@ http {
error_page 401 = {{ $location.ExternalAuth.SigninURL }}; error_page 401 = {{ $location.ExternalAuth.SigninURL }};
{{ end }} {{ end }}
{{ if (or $location.Redirect.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect)) }}
# enforce ssl on server side
if ($pass_access_scheme = http) {
return 301 https://$host$request_uri;
}
{{ end }}
{{/* if the location contains a rate limit annotation, create one */}} {{/* if the location contains a rate limit annotation, create one */}}
{{ $limits := buildRateLimit $location }} {{ $limits := buildRateLimit $location }}
{{ range $limit := $limits }} {{ range $limit := $limits }}
@ -490,7 +497,7 @@ stream {
# TCP services # TCP services
{{ range $i, $tcpServer := .TCPBackends }} {{ range $i, $tcpServer := .TCPBackends }}
upstream {{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} { upstream tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} {
{{ range $j, $endpoint := $tcpServer.Endpoints }} {{ range $j, $endpoint := $tcpServer.Endpoints }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }}; server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ end }} {{ end }}
@ -498,22 +505,22 @@ stream {
server { server {
listen {{ $tcpServer.Port }}; listen {{ $tcpServer.Port }};
proxy_pass {{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}; proxy_pass tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }};
} }
{{ end }} {{ end }}
# UDP services # UDP services
{{ range $i, $udpServer := .UDPBackends }} {{ range $i, $udpServer := .UDPBackends }}
upstream {{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} { upstream udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} {
{{ range $j, $endpoint := $udpServer.Endpoints }} {{ range $j, $endpoint := $udpServer.Endpoints }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }}; server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ end }} {{ end }}
} }
server { server {
listen {{ $udpServer.Port }}; listen {{ $udpServer.Port }} udp;
proxy_responses 1; proxy_responses 1;
proxy_pass {{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}; proxy_pass udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }};
} }
{{ end }} {{ end }}
} }

View file

@ -28,6 +28,7 @@ const (
addBaseURL = "ingress.kubernetes.io/add-base-url" addBaseURL = "ingress.kubernetes.io/add-base-url"
sslRedirect = "ingress.kubernetes.io/ssl-redirect" sslRedirect = "ingress.kubernetes.io/ssl-redirect"
forceSSLRedirect = "ingress.kubernetes.io/force-ssl-redirect" forceSSLRedirect = "ingress.kubernetes.io/force-ssl-redirect"
appRoot = "ingress.kubernetes.io/app-root"
) )
// Redirect describes the per location redirect config // Redirect describes the per location redirect config
@ -41,6 +42,8 @@ type Redirect struct {
SSLRedirect bool `json:"sslRedirect"` SSLRedirect bool `json:"sslRedirect"`
// ForceSSLRedirect indicates if the location section is accessible SSL only // ForceSSLRedirect indicates if the location section is accessible SSL only
ForceSSLRedirect bool `json:"forceSSLRedirect"` ForceSSLRedirect bool `json:"forceSSLRedirect"`
// AppRoot defines the Application Root that the Controller must redirect if it's not in '/' context
AppRoot string `json:"appRoot"`
} }
type rewrite struct { type rewrite struct {
@ -65,10 +68,12 @@ func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
fSslRe = a.backendResolver.GetDefaultBackend().ForceSSLRedirect fSslRe = a.backendResolver.GetDefaultBackend().ForceSSLRedirect
} }
abu, _ := parser.GetBoolAnnotation(addBaseURL, ing) abu, _ := parser.GetBoolAnnotation(addBaseURL, ing)
ar, _ := parser.GetStringAnnotation(appRoot, ing)
return &Redirect{ return &Redirect{
Target: rt, Target: rt,
AddBaseURL: abu, AddBaseURL: abu,
SSLRedirect: sslRe, SSLRedirect: sslRe,
ForceSSLRedirect: fSslRe, ForceSSLRedirect: fSslRe,
AppRoot: ar,
}, nil }, nil
} }

View file

@ -158,3 +158,20 @@ func TestForceSSLRedirect(t *testing.T) {
t.Errorf("Expected true but returned false") t.Errorf("Expected true but returned false")
} }
} }
func TestAppRoot(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[appRoot] = "/app1"
ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{true}).Parse(ing)
redirect, ok := i.(*Redirect)
if !ok {
t.Errorf("expected a App Context")
}
if redirect.AppRoot != "/app1" {
t.Errorf("Unexpected value got in AppRoot")
}
}

View file

@ -76,11 +76,11 @@ func (ic *GenericController) syncSecret(k interface{}) error {
// no need to update // no need to update
return nil return nil
} }
glog.V(3).Infof("updating secret %v/%v in the store", sec.Namespace, sec.Name) glog.Infof("updating secret %v/%v in the local store", sec.Namespace, sec.Name)
ic.sslCertTracker.Update(key, cert) ic.sslCertTracker.Update(key, cert)
return nil return nil
} }
glog.V(3).Infof("adding secret %v/%v to the store", sec.Namespace, sec.Name) glog.Infof("adding secret %v/%v to the local store", sec.Namespace, sec.Name)
ic.sslCertTracker.Add(key, cert) ic.sslCertTracker.Add(key, cert)
return nil return nil
} }
@ -106,10 +106,10 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC
var s *ingress.SSLCert var s *ingress.SSLCert
if okcert && okkey { if okcert && okkey {
glog.V(3).Infof("found certificate and private key, configuring %v as a TLS Secret", secretName) glog.Infof("found certificate and private key, configuring %v as a TLS Secret", secretName)
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca) s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
} else if ca != nil { } else if ca != nil {
glog.V(3).Infof("found only ca.crt, configuring %v as an Certificate Authentication secret", secretName) glog.Infof("found only ca.crt, configuring %v as an Certificate Authentication secret", secretName)
s, err = ssl.AddCertAuth(nsSecName, ca) s, err = ssl.AddCertAuth(nsSecName, ca)
} else { } else {
return nil, fmt.Errorf("ko keypair or CA cert could be found in %v", secretName) return nil, fmt.Errorf("ko keypair or CA cert could be found in %v", secretName)

View file

@ -960,6 +960,12 @@ func (ic *GenericController) createServers(data []interface{},
} }
} }
if tlsSecretName == "" {
glog.Warningf("ingress rule %v/%v for host %v does not contains a matching tls host", ing.Namespace, ing.Name, host)
glog.V(2).Infof("%v", ing.Spec.TLS)
continue
}
key := fmt.Sprintf("%v/%v", ing.Namespace, tlsSecretName) key := fmt.Sprintf("%v/%v", ing.Namespace, tlsSecretName)
bc, exists := ic.sslCertTracker.Get(key) bc, exists := ic.sslCertTracker.Get(key)
if exists { if exists {
@ -967,7 +973,11 @@ func (ic *GenericController) createServers(data []interface{},
if isHostValid(host, cert) { if isHostValid(host, cert) {
servers[host].SSLCertificate = cert.PemFileName servers[host].SSLCertificate = cert.PemFileName
servers[host].SSLPemChecksum = cert.PemSHA servers[host].SSLPemChecksum = cert.PemSHA
} else {
glog.Warningf("ssl certificate %v does not contains a common name for host %v", key, host)
} }
} else {
glog.Warningf("ssl certificate \"%v\" does not exist in local store", key)
} }
} }
} }
@ -991,6 +1001,11 @@ func (ic *GenericController) getEndpoints(
upsServers := []ingress.Endpoint{} upsServers := []ingress.Endpoint{}
// avoid duplicated upstream servers when the service
// contains multiple port definitions sharing the same
// targetport.
adus := make(map[string]bool, 0)
for _, ss := range ep.Subsets { for _, ss := range ep.Subsets {
for _, epPort := range ss.Ports { for _, epPort := range ss.Ports {
@ -1031,6 +1046,10 @@ func (ic *GenericController) getEndpoints(
} }
for _, epAddress := range ss.Addresses { for _, epAddress := range ss.Addresses {
ep := fmt.Sprintf("%v:%v", epAddress.IP, targetPort)
if _, exists := adus[ep]; exists {
continue
}
ups := ingress.Endpoint{ ups := ingress.Endpoint{
Address: epAddress.IP, Address: epAddress.IP,
Port: fmt.Sprintf("%v", targetPort), Port: fmt.Sprintf("%v", targetPort),
@ -1038,6 +1057,7 @@ func (ic *GenericController) getEndpoints(
FailTimeout: hz.FailTimeout, FailTimeout: hz.FailTimeout,
} }
upsServers = append(upsServers, ups) upsServers = append(upsServers, ups)
adus[ep] = true
} }
} }
} }

View file

@ -128,19 +128,6 @@ func NewIngressController(backend ingress.Controller) *GenericController {
glog.Infof("service %v validated as source of Ingress status", *publishSvc) glog.Infof("service %v validated as source of Ingress status", *publishSvc)
} }
for _, configMap := range []string{*configMap, *tcpConfigMapName, *udpConfigMapName} {
if configMap == "" {
continue
}
_, err = k8s.IsValidConfigMap(kubeClient, configMap)
if err != nil {
glog.Fatalf("%v", err)
}
}
if *watchNamespace != "" { if *watchNamespace != "" {
_, err = k8s.IsValidNamespace(kubeClient, *watchNamespace) _, err = k8s.IsValidNamespace(kubeClient, *watchNamespace)

View file

@ -46,7 +46,7 @@ func isHostValid(host string, cert *ingress.SSLCert) bool {
return false return false
} }
for _, cn := range cert.CN { for _, cn := range cert.CN {
if matchHostnames(cn, host) { if matchHostnames(cn, strings.ToLower(host)) {
return true return true
} }
} }

View file

@ -6,6 +6,8 @@ import "net"
// The reason of this requirements is the annotations are generic. If some implementation do not supports // The reason of this requirements is the annotations are generic. If some implementation do not supports
// one or more annotations it just can provides defaults // one or more annotations it just can provides defaults
type Backend struct { type Backend struct {
// AppRoot contains the AppRoot for apps that doesn't exposes its content in the 'root' context
AppRoot string `json:"app-root"`
// enables which HTTP codes should be passed for processing with the error_page directive // enables which HTTP codes should be passed for processing with the error_page directive
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors // http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors

View file

@ -75,13 +75,13 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
pemBlock, _ := pem.Decode(pemCerts) pemBlock, _ := pem.Decode(pemCerts)
if pemBlock == nil { if pemBlock == nil {
_ = os.Remove(tempPemFile.Name()) _ = os.Remove(tempPemFile.Name())
return nil, fmt.Errorf("No valid PEM formatted block found") return nil, fmt.Errorf("no valid PEM formatted block found")
} }
// If the file does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used. // If the file does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
if pemBlock.Type != "CERTIFICATE" { if pemBlock.Type != "CERTIFICATE" {
_ = os.Remove(tempPemFile.Name()) _ = os.Remove(tempPemFile.Name())
return nil, fmt.Errorf("Certificate %v contains invalid data, and must be created with 'kubectl create secret tls'", name) return nil, fmt.Errorf("certificate %v contains invalid data, and must be created with 'kubectl create secret tls'", name)
} }
pemCert, err := x509.ParseCertificate(pemBlock.Bytes) pemCert, err := x509.ParseCertificate(pemBlock.Bytes)
@ -115,7 +115,7 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
caFile, err := os.OpenFile(pemFileName, os.O_RDWR|os.O_APPEND, 0600) caFile, err := os.OpenFile(pemFileName, os.O_RDWR|os.O_APPEND, 0600)
if err != nil { if err != nil {
return nil, fmt.Errorf("Could not open file %v for writing additional CA chains: %v", pemFileName, err) return nil, fmt.Errorf("could not open file %v for writing additional CA chains: %v", pemFileName, err)
} }
defer caFile.Close() defer caFile.Close()
@ -150,11 +150,11 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) {
pemCABlock, _ := pem.Decode(ca) pemCABlock, _ := pem.Decode(ca)
if pemCABlock == nil { if pemCABlock == nil {
return nil, fmt.Errorf("No valid PEM formatted block found") return nil, fmt.Errorf("no valid PEM formatted block found")
} }
// If the first certificate does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used. // If the first certificate does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
if pemCABlock.Type != "CERTIFICATE" { if pemCABlock.Type != "CERTIFICATE" {
return nil, fmt.Errorf("CA File %v contains invalid data, and must be created only with PEM formated certificates", name) return nil, fmt.Errorf("CA file %v contains invalid data, and must be created only with PEM formated certificates", name)
} }
_, err := x509.ParseCertificate(pemCABlock.Bytes) _, err := x509.ParseCertificate(pemCABlock.Bytes)
@ -206,13 +206,13 @@ func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
pemBlock, _ := pem.Decode(pemCerts) pemBlock, _ := pem.Decode(pemCerts)
if pemBlock == nil { if pemBlock == nil {
_ = os.Remove(tempPemFile.Name()) _ = os.Remove(tempPemFile.Name())
return "", fmt.Errorf("No valid PEM formatted block found") return "", fmt.Errorf("no valid PEM formatted block found")
} }
// If the file does not start with 'BEGIN DH PARAMETERS' it's invalid and must not be used. // If the file does not start with 'BEGIN DH PARAMETERS' it's invalid and must not be used.
if pemBlock.Type != "DH PARAMETERS" { if pemBlock.Type != "DH PARAMETERS" {
_ = os.Remove(tempPemFile.Name()) _ = os.Remove(tempPemFile.Name())
return "", fmt.Errorf("Certificate %v contains invalid data", name) return "", fmt.Errorf("certificate %v contains invalid data", name)
} }
err = os.Rename(tempPemFile.Name(), pemFileName) err = os.Rename(tempPemFile.Name(), pemFileName)

View file

@ -255,7 +255,7 @@ spec:
spec: spec:
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: ingress-nginx name: ingress-nginx
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:

View file

@ -28,8 +28,8 @@ Re-encrypty | terminate, apply routing rules, re-encrypt | nginx | Advanced
Name | Description | Platform | Complexity Level Name | Description | Platform | Complexity Level
-----| ----------- | ---------- | ---------------- -----| ----------- | ---------- | ----------------
Daemonset | run multiple controllers in a daemonset | nginx | Intermediate Daemonset | run multiple controllers in a daemonset | nginx/haproxy | Intermediate
Deployment | run multiple controllers as a deployment | nginx | Intermediate Deployment | run multiple controllers as a deployment | nginx/haproxy | Intermediate
Multi-zone | bridge different zones in a single cluster | gce | Intermediate Multi-zone | bridge different zones in a single cluster | gce | Intermediate
Static-ip | a single ingress gets a single static ip | * | Intermediate Static-ip | a single ingress gets a single static ip | * | Intermediate
Geo-routing | route to geographically closest endpoint | nginx | Advanced Geo-routing | route to geographically closest endpoint | nginx | Advanced

View file

@ -19,7 +19,7 @@ spec:
# hostNetwork: true # hostNetwork: true
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller name: nginx-ingress-controller
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -16,7 +16,7 @@ spec:
spec: spec:
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-lb name: nginx-ingress-lb
imagePullPolicy: Always imagePullPolicy: Always
readinessProbe: readinessProbe:

View file

@ -19,7 +19,7 @@ spec:
# hostNetwork: true # hostNetwork: true
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller name: nginx-ingress-controller
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -16,7 +16,7 @@ spec:
spec: spec:
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-lb name: nginx-ingress-lb
imagePullPolicy: Always imagePullPolicy: Always
readinessProbe: readinessProbe:

View file

@ -13,7 +13,7 @@ spec:
spec: spec:
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-lb name: nginx-ingress-lb
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -71,7 +71,7 @@ spec:
hostNetwork: true hostNetwork: true
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller name: nginx-ingress-controller
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -19,7 +19,7 @@ spec:
# hostNetwork: true # hostNetwork: true
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller name: nginx-ingress-controller
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -1,66 +0,0 @@
Create an Ingress rule with a rewrite annotation:
```
$ echo "
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/rewrite-target: /
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: echoheaders
servicePort: 80
path: /something
" | kubectl create -f -
```
Check the rewrite is working
```
$ curl -v http://172.17.4.99/something -H 'Host: rewrite.bar.com'
* Trying 172.17.4.99...
* Connected to 172.17.4.99 (172.17.4.99) port 80 (#0)
> GET /something HTTP/1.1
> Host: rewrite.bar.com
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.11.0
< Date: Tue, 31 May 2016 16:07:31 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
<
CLIENT VALUES:
client_address=10.2.56.9
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://rewrite.bar.com:8080/
SERVER VALUES:
server_version=nginx: 1.9.11 - lua: 10001
HEADERS RECEIVED:
accept=*/*
connection=close
host=rewrite.bar.com
user-agent=curl/7.43.0
x-forwarded-for=10.2.56.1
x-forwarded-host=rewrite.bar.com
x-forwarded-port=80
x-forwarded-proto=http
x-real-ip=10.2.56.1
BODY:
* Connection #0 to host 172.17.4.99 left intact
-no body in request-
```

View file

@ -0,0 +1,127 @@
# Rewrite
This example demonstrates how to use the Rewrite annotations
## Prerequisites
You will need to make sure you Ingress targets exactly one Ingress
controller by specifying the [ingress.class annotation](/examples/PREREQUISITES.md#ingress-class),
and that you have an ingress controller [running](/examples/deployment) in your cluster.
## Deployment
Rewriting can be controlled using the following annotations:
|Name|Description|Values|
| --- | --- | --- |
|ingress.kubernetes.io/rewrite-target|Target URI where the traffic must be redirected|string|
|ingress.kubernetes.io/add-base-url|indicates if is required to add a base tag in the head of the responses from the upstream servers|bool|
|ingress.kubernetes.io/ssl-redirect|Indicates if the location section is accessible SSL only (defaults to True when Ingress contains a Certificate)|bool|
|ingress.kubernetes.io/force-ssl-redirect|Forces the redirection to HTTPS even if the Ingress is not TLS Enabled|bool|
|ingress.kubernetes.io/app-root|Defines the Application Root that the Controller must redirect if it's not in '/' context|string|
## Validation
### Rewrite Target
Create an Ingress rule with a rewrite annotation:
```
$ echo "
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/rewrite-target: /
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: echoheaders
servicePort: 80
path: /something
" | kubectl create -f -
```
Check the rewrite is working
```
$ curl -v http://172.17.4.99/something -H 'Host: rewrite.bar.com'
* Trying 172.17.4.99...
* Connected to 172.17.4.99 (172.17.4.99) port 80 (#0)
> GET /something HTTP/1.1
> Host: rewrite.bar.com
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.11.0
< Date: Tue, 31 May 2016 16:07:31 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
<
CLIENT VALUES:
client_address=10.2.56.9
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://rewrite.bar.com:8080/
SERVER VALUES:
server_version=nginx: 1.9.11 - lua: 10001
HEADERS RECEIVED:
accept=*/*
connection=close
host=rewrite.bar.com
user-agent=curl/7.43.0
x-forwarded-for=10.2.56.1
x-forwarded-host=rewrite.bar.com
x-forwarded-port=80
x-forwarded-proto=http
x-real-ip=10.2.56.1
BODY:
* Connection #0 to host 172.17.4.99 left intact
-no body in request-
```
### App Root
Create an Ingress rule with a app-root annotation:
```
$ echo "
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/app-root: /app1
name: approot
namespace: default
spec:
rules:
- host: approot.bar.com
http:
paths:
- backend:
serviceName: echoheaders
servicePort: 80
path: /
" | kubectl create -f -
```
Check the rewrite is working
```
$ curl -I -k http://approot.bar.com/
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.11.10
Date: Mon, 13 Mar 2017 14:57:15 GMT
Content-Type: text/html
Content-Length: 162
Location: http://stickyingress.example.com/app1
Connection: keep-alive
```

View file

@ -0,0 +1,65 @@
# Deploying multi Haproxy Ingress Controllers
This example aims to demonstrate the Deployment of multi haproxy ingress controllers.
## Prerequisites
This ingress controller doesn't yet have support for
[ingress classes](/examples/PREREQUISITES.md#ingress-class). You MUST turn
down any existing ingress controllers before running HAProxy Ingress controller or
they will fight for Ingresses. This includes any cloudprovider controller.
This document has also the following prerequisites:
* Create a [TLS secret](/examples/PREREQUISITES.md#tls-certificates) named `tls-secret` to be used as default TLS certificate
Creating the TLS secret:
```console
$ openssl req \
-x509 -newkey rsa:2048 -nodes -days 365 \
-keyout tls.key -out tls.crt -subj '/CN=localhost'
$ kubectl create secret tls tls-secret --cert=tls.crt --key=tls.key
$ rm -v tls.crt tls.key
```
## Default Backend
The default backend is a service of handling all url paths and hosts the haproxy controller doesn't understand. Deploy the default-http-backend as follow:
```console
$ kubectl create -f default-backend.yaml
deployment "default-http-backend" created
service "default-http-backend" created
$ kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default-http-backend 192.168.3.4 <none> 80/TCP 30m
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
default-http-backend-q5sb6 1/1 Running 0 30m
```
## Ingress Deployment
Deploy the Deployment of multi controllers as follows:
```console
$ kubectl apply -f haproxy-ingress-deployment.yaml
deployment "haproxy-ingress" created
```
Check if the controller was successfully deployed:
```console
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
default-http-backend 1 1 1 1 30m
haproxy-ingress 2 2 2 2 45s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
default-http-backend-q5sb6 1/1 Running 0 35m
haproxy-ingress-1779899633-k045t 1/1 Running 0 1m
haproxy-ingress-1779899633-mhthv 1/1 Running 0 1m
```

View file

@ -0,0 +1,49 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: default-http-backend
labels:
k8s-app: default-http-backend
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: default-http-backend
spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
# Any image is permissable as long as:
# 1. It serves a 404 page at /
# 2. It serves 200 on a /healthz endpoint
image: gcr.io/google_containers/defaultbackend:1.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
labels:
k8s-app: default-http-backend
spec:
ports:
- port: 80
targetPort: 8080
selector:
k8s-app: default-http-backend

View file

@ -0,0 +1,39 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
run: haproxy-ingress
name: haproxy-ingress
spec:
replicas: 2
selector:
matchLabels:
run: haproxy-ingress
template:
metadata:
labels:
run: haproxy-ingress
spec:
containers:
- name: haproxy-ingress
image: quay.io/jcmoraisjr/haproxy-ingress
imagePullPolicy: IfNotPresent
args:
- --default-backend-service=default/default-http-backend
- --default-ssl-certificate=default/tls-secret
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
- name: stat
containerPort: 1936
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace

View file

@ -14,7 +14,7 @@ spec:
spec: spec:
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller name: nginx-ingress-controller
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -18,7 +18,7 @@ spec:
# hostNetwork: true # hostNetwork: true
terminationGracePeriodSeconds: 60 terminationGracePeriodSeconds: 60
containers: containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2 - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller name: nginx-ingress-controller
readinessProbe: readinessProbe:
httpGet: httpGet:

View file

@ -234,7 +234,7 @@ func getDeployer() (deployer, error) {
case "bash": case "bash":
return bash{}, nil return bash{}, nil
default: default:
return nil, fmt.Errorf("Unknown deployment strategy %q", *deployment) return nil, fmt.Errorf("unknown deployment strategy %q", *deployment)
} }
} }