Merge branch 'upstream' into nginx/extauth_headers

This commit is contained in:
rsafronov 2017-03-24 20:25:18 -04:00
commit 6d07d32003
63 changed files with 1075 additions and 218 deletions

1
controllers/gce/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
glbc

View file

@ -18,7 +18,7 @@ __A reminder on Services__: A Kubernetes Service defines a set of pods and a mea
### L7 Load balancing on Kubernetes
To achive L7 loadbalancing through Kubernetes, we employ a resource called `Ingress`. The Ingress is consumed by this loadbalancer controller, which creates the following GCE resource graph:
To achieve L7 loadbalancing through Kubernetes, we employ a resource called `Ingress`. The Ingress is consumed by this loadbalancer controller, which creates the following GCE resource graph:
[Global Forwarding Rule](https://cloud.google.com/compute/docs/load-balancing/http/global-forwarding-rules) -> [TargetHttpProxy](https://cloud.google.com/compute/docs/load-balancing/http/target-proxies) -> [Url Map](https://cloud.google.com/compute/docs/load-balancing/http/url-map) -> [Backend Service](https://cloud.google.com/compute/docs/load-balancing/http/backend-service) -> [Instance Group](https://cloud.google.com/compute/docs/instance-groups/)
@ -649,12 +649,12 @@ If you hit that it means the controller isn't even starting. Re-check your input
A default GKE/GCE cluster needs at least 1 firewall rule for GLBC to function. The Ingress controller should create this for you automatically. You can also create it thus:
```console
$ gcloud compute firewall-rules create allow-130-211-0-0-22 \
--source-ranges 130.211.0.0/22 \
--source-ranges 130.211.0.0/22,35.191.0.0/16 \
--target-tags $TAG \
--allow tcp:$NODE_PORT
```
Where `130.211.0.0/22` is the source range of the GCE L7, `$NODE_PORT` is the node port your Service is exposed on, i.e:
Where `130.211.0.0/22` and `35.191.0.0/16` are the source ranges of the GCE L7, `$NODE_PORT` is the node port your Service is exposed on, i.e:
```console
$ kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services ${SERVICE_NAME}
```

View file

@ -119,7 +119,7 @@ func NewBackendPool(
func(i interface{}) (string, error) {
bs := i.(*compute.BackendService)
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)
if err != nil {

View file

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

View file

@ -290,7 +290,7 @@ func (lbc *LoadBalancerController) storesSynced() bool {
func (lbc *LoadBalancerController) sync(key string) (err error) {
if !lbc.hasSynced() {
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)
@ -326,7 +326,7 @@ func (lbc *LoadBalancerController) sync(key string) (err error) {
defer func() {
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)
}()
@ -343,7 +343,7 @@ func (lbc *LoadBalancerController) sync(key string) (err error) {
if ingExists {
lbc.recorder.Eventf(obj.(*extensions.Ingress), api.EventTypeWarning, eventMsg, err.Error())
} else {
err = fmt.Errorf("%v Error: %v", eventMsg, err)
err = fmt.Errorf("%v, error: %v", eventMsg, err)
}
syncError = err
}

View file

@ -65,11 +65,11 @@ func (t *apiServerTLSLoader) load(ing *extensions.Ingress) (*loadbalancers.TLSCe
}
cert, ok := secret.Data[api.TLSCertKey]
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]
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)}
if err := t.validate(certs); err != nil {
@ -95,5 +95,5 @@ func (f *fakeTLSSecretLoader) load(ing *extensions.Ingress) (*loadbalancers.TLSC
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

@ -216,24 +216,35 @@ func (s *StoreToIngressLister) List() (ing extensions.IngressList, err error) {
// GetServiceIngress gets all the Ingress' that have rules pointing to a service.
// Note that this ignores services without the right nodePorts.
func (s *StoreToIngressLister) GetServiceIngress(svc *api.Service) (ings []extensions.Ingress, err error) {
IngressLoop:
for _, m := range s.Store.List() {
ing := *m.(*extensions.Ingress)
if ing.Namespace != svc.Namespace {
continue
}
for _, rules := range ing.Spec.Rules {
if rules.IngressRuleValue.HTTP == nil {
// Check service of default backend
if ing.Spec.Backend != nil && ing.Spec.Backend.ServiceName == svc.Name {
ings = append(ings, ing)
continue
}
// Check the target service for each path rule
for _, rule := range ing.Spec.Rules {
if rule.IngressRuleValue.HTTP == nil {
continue
}
for _, p := range rules.IngressRuleValue.HTTP.Paths {
for _, p := range rule.IngressRuleValue.HTTP.Paths {
if p.Backend.ServiceName == svc.Name {
ings = append(ings, ing)
// Skip the rest of the rules to avoid duplicate ingresses in list
continue IngressLoop
}
}
}
}
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
}
@ -315,7 +326,7 @@ func (t *GCETranslator) toGCEBackend(be *extensions.IngressBackend, ns string) (
backend, err := t.CloudClusterManager.backendPool.Get(int64(port))
if err != nil {
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
}
@ -332,7 +343,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
})
if !exists {
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 {
return invalidPort, errorNodePortNotFound{be, err}
@ -356,7 +367,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
return nodePort, nil
}
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.
@ -374,7 +385,7 @@ func (t *GCETranslator) toNodePorts(ings *extensions.IngressList) []int64 {
}
for _, rule := range ing.Spec.Rules {
if rule.HTTP == nil {
glog.Errorf("Ignoring non http Ingress rule.")
glog.Errorf("ignoring non http Ingress rule")
continue
}
for _, path := range rule.HTTP.Paths {
@ -411,7 +422,7 @@ func (t *GCETranslator) GetZoneForNode(name string) (string, error) {
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.

View file

@ -35,7 +35,7 @@ func (f *fakeFirewallRules) GetFirewall(name string) (*compute.Firewall, error)
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 {
@ -66,7 +66,7 @@ func (f *fakeFirewallRules) DeleteFirewall(name string) error {
firewalls = append(firewalls, rule)
}
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
return nil
@ -95,7 +95,7 @@ func (f *fakeFirewallRules) UpdateFirewall(name, msgTag string, srcRange netset.
if exists {
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.

View file

@ -26,25 +26,25 @@ import (
"k8s.io/kubernetes/pkg/util/sets"
)
// Src range from which the GCE L7 performs health checks.
const l7SrcRange = "130.211.0.0/22"
// Src ranges from which the GCE L7 performs health checks.
var l7SrcRanges = []string{"130.211.0.0/22", "35.191.0.0/16"}
// FirewallRules manages firewall rules.
type FirewallRules struct {
cloud Firewall
namer *utils.Namer
srcRange netset.IPNet
cloud Firewall
namer *utils.Namer
srcRanges netset.IPNet
}
// NewFirewallPool creates a new firewall rule manager.
// cloud: the cloud object implementing Firewall.
// namer: cluster namer.
func NewFirewallPool(cloud Firewall, namer *utils.Namer) SingleFirewallPool {
srcNetSet, err := netset.ParseIPNets(l7SrcRange)
srcNetSet, err := netset.ParseIPNets(l7SrcRanges...)
if err != nil {
glog.Fatalf("Could not parse L7 src range %v for firewall rule: %v", l7SrcRange, err)
glog.Fatalf("Could not parse L7 src ranges %v for firewall rule: %v", l7SrcRanges, err)
}
return &FirewallRules{cloud: cloud, namer: namer, srcRange: srcNetSet}
return &FirewallRules{cloud: cloud, namer: namer, srcRanges: srcNetSet}
}
// Sync sync firewall rules with the cloud.
@ -60,7 +60,7 @@ func (fr *FirewallRules) Sync(nodePorts []int64, nodeNames []string) error {
rule, _ := fr.cloud.GetFirewall(name)
if rule == nil {
glog.Infof("Creating global l7 firewall rule %v", name)
return fr.cloud.CreateFirewall(suffix, "GCE L7 firewall rule", fr.srcRange, nodePorts, nodeNames)
return fr.cloud.CreateFirewall(suffix, "GCE L7 firewall rule", fr.srcRanges, nodePorts, nodeNames)
}
requiredPorts := sets.NewString()
@ -77,7 +77,7 @@ func (fr *FirewallRules) Sync(nodePorts []int64, nodeNames []string) error {
return nil
}
glog.V(3).Infof("Firewall rule %v already exists, updating nodeports %v", name, nodePorts)
return fr.cloud.UpdateFirewall(suffix, "GCE L7 firewall rule", fr.srcRange, nodePorts, nodeNames)
return fr.cloud.UpdateFirewall(suffix, "GCE L7 firewall rule", fr.srcRanges, nodePorts, nodeNames)
}
// Shutdown shuts down this firewall rules manager.

View file

@ -60,7 +60,7 @@ func (f *FakeHealthChecks) GetHttpHealthCheck(name string) (*compute.HttpHealthC
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.
@ -75,7 +75,7 @@ func (f *FakeHealthChecks) DeleteHttpHealthCheck(name string) error {
healthChecks = append(healthChecks, h)
}
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
return nil
@ -94,7 +94,7 @@ func (f *FakeHealthChecks) UpdateHttpHealthCheck(hc *compute.HttpHealthCheck) er
}
}
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
return nil

View file

@ -75,7 +75,7 @@ func (f *FakeInstanceGroups) GetInstanceGroup(name, zone string) (*compute.Insta
}
}
// 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.
@ -97,7 +97,7 @@ func (f *FakeInstanceGroups) DeleteInstanceGroup(name, zone string) error {
newGroups = append(newGroups, ig)
}
if !found {
return fmt.Errorf("Instance Group %v not found", name)
return fmt.Errorf("instance group %v not found", name)
}
f.instanceGroups = newGroups
return nil

View file

@ -109,7 +109,7 @@ func (f *FakeLoadBalancers) GetGlobalForwardingRule(name string) (*compute.Forwa
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.
@ -176,7 +176,7 @@ func (f *FakeLoadBalancers) GetUrlMap(name string) (*compute.UrlMap, error) {
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.
@ -226,7 +226,7 @@ func (f *FakeLoadBalancers) GetTargetHttpProxy(name string) (*compute.TargetHttp
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.
@ -275,7 +275,7 @@ func (f *FakeLoadBalancers) GetTargetHttpsProxy(name string) (*compute.TargetHtt
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.
@ -326,7 +326,7 @@ func (f *FakeLoadBalancers) SetSslCertificateForTargetHttpsProxy(proxy *compute.
}
}
if !found {
return fmt.Errorf("Failed to find proxy %v", proxy.Name)
return fmt.Errorf("failed to find proxy %v", proxy.Name)
}
return nil
}
@ -415,7 +415,7 @@ func (f *FakeLoadBalancers) GetGlobalStaticIP(name string) (*compute.Address, er
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.
@ -441,7 +441,7 @@ func (f *FakeLoadBalancers) GetSslCertificate(name string) (*compute.SslCertific
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.

View file

@ -106,7 +106,7 @@ func (l *L7s) Get(name string) (*L7, error) {
name = l.namer.LBName(name)
lb, exists := l.snapshotter.Get(name)
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
}
@ -292,7 +292,7 @@ type L7 struct {
func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
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))
urlMap, _ := l.cloud.GetUrlMap(urlMapName)
@ -313,7 +313,7 @@ func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
func (l *L7) checkProxy() (err error) {
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))
proxy, _ := l.cloud.GetTargetHttpProxy(proxyName)
@ -362,7 +362,7 @@ func (l *L7) checkSSLCert() (err error) {
return err
}
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)
@ -429,7 +429,7 @@ func (l *L7) checkHttpsProxy() (err error) {
return 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))
proxy, _ := l.cloud.GetTargetHttpsProxy(proxyName)
@ -535,7 +535,7 @@ func (l *L7) getEffectiveIP() (string, bool) {
func (l *L7) checkHttpForwardingRule() (err error) {
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))
address, _ := l.getEffectiveIP()
@ -565,7 +565,7 @@ func (l *L7) checkHttpsForwardingRule() (err error) {
// checkStaticIP reserves a static IP allocated to the Forwarding Rule.
func (l *L7) checkStaticIP() (err error) {
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.
if address, manageStaticIP := l.getEffectiveIP(); !manageStaticIP {
@ -704,7 +704,7 @@ func getNameForPathMatcher(hostRule string) string {
// pathmatcher of the host.
func (l *L7) UpdateUrlMap(ingressRules utils.GCEURLMap) error {
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)

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
// 3. Apiserver flake - found=false, err!=nil
// 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 {
// Not found but safe to proceed.
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)
}
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 {
apiObj.Data = map[string]string{key: val}
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)
@ -176,7 +176,7 @@ func (a *APIServerConfigMapStore) Delete(obj interface{}) error {
func (a *APIServerConfigMapStore) GetByKey(key string) (item interface{}, exists bool, err error) {
nsName := strings.Split(key, "/")
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]
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)
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])
if err != nil {
return "", fmt.Errorf("Unexpected regex match: %v", beName)
return "", fmt.Errorf("unexpected regex match: %v", beName)
}
return match[1], nil
}

View file

@ -1,5 +1,74 @@
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
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2`

View file

@ -3,7 +3,7 @@ all: push
BUILDTAGS=
# 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
GOOS?=linux
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}" \
-o rootfs/nginx-ingress-controller ${PKG}/pkg/cmd/controller
container:
container: build
$(DOCKER) build --pull -t $(PREFIX):$(RELEASE) rootfs
push: container
@ -50,4 +50,4 @@ vet:
@go vet $(shell go list ${PKG}/... | grep -v vendor)
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|
|---------------------------|------|
|[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/auth-realm](#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.
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

View file

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

View file

@ -47,7 +47,7 @@ const (
gzipTypes = "application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component"
logFormatUpstream = `%v - [$proxy_add_x_forwarded_for] - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status"`
logFormatUpstream = `%v - [$proxy_add_x_forwarded_for] - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status`
logFormatStream = `[$time_local] $protocol [$ssl_preread_server_name] [$stream_upstream] $status $bytes_sent $bytes_received $session_time`

View file

@ -138,7 +138,7 @@ func (bit BoolToFloat64) UnmarshalJSON(data []byte) error {
} else if asString == "0" || asString == "false" {
bit = 0
} 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
}

View file

@ -203,10 +203,16 @@ func buildLocation(input interface{}) string {
path := location.Path
if len(location.Redirect.Target) > 0 && location.Redirect.Target != path {
if path == "/" {
if path == slash {
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
@ -294,13 +300,10 @@ func buildProxyPass(b interface{}, loc interface{}) string {
if len(location.Redirect.Target) > 0 {
abu := ""
if location.Redirect.AddBaseURL {
bPath := location.Redirect.Target
if !strings.HasSuffix(bPath, slash) {
bPath = fmt.Sprintf("%s/", bPath)
}
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;
// path has a slash suffix, so that it can be connected with baseuri directly
bPath := fmt.Sprintf("%s%s", path, "$baseuri")
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;
`, bPath, bPath)
}

View file

@ -47,33 +47,43 @@ var (
rewrite /(.*) /jenkins/$1 break;
proxy_pass http://upstream-name;
`, false},
"redirect /something to /": {"/something", "/", "~* ^/something", `
"redirect /something to /": {"/something", "/", `~* ^/something\/?(?<baseuri>.*)`, `
rewrite /something/(.*) /$1 break;
rewrite /something / break;
proxy_pass http://upstream-name;
`, 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;
proxy_pass http://upstream-name;
`, false},
"redirect / to /jenkins and rewrite": {"/", "/jenkins", "~* /", `
rewrite /(.*) /jenkins/$1 break;
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://$server_name/jenkins/">' r;
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/$baseuri">' r;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/$baseuri">' r;
`, true},
"redirect /something to / and rewrite": {"/something", "/", "~* ^/something", `
"redirect /something to / and rewrite": {"/something", "/", `~* ^/something\/?(?<baseuri>.*)`, `
rewrite /something/(.*) /$1 break;
rewrite /something / break;
proxy_pass http://upstream-name;
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/">' 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;
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/something/$baseuri">' r;
`, 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;
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://$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://$http_host/something-complex/$baseuri">' r;
`, 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 \
diffutils \
ssl-cert \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& make-ssl-cert generate-default-snakeoil --force-overwrite
&& rm -rf /var/lib/apt/lists/*
COPY . /

View file

@ -236,7 +236,19 @@ http {
ssl_verify_client on;
ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }};
{{ 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) }}
location = {{ $authPath }} {
internal;
@ -282,12 +294,7 @@ http {
error_page 401 = {{ $location.ExternalAuth.SigninURL }};
{{ 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 */}}
{{ $limits := buildRateLimit $location }}
{{ range $limit := $limits }}
@ -493,7 +500,7 @@ stream {
# TCP services
{{ 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 }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ end }}
@ -501,22 +508,22 @@ stream {
server {
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 }}
# UDP services
{{ 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 }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ end }}
}
server {
listen {{ $udpServer.Port }};
listen {{ $udpServer.Port }} udp;
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 }}
}