Merge branch 'upstream' into nginx/extauth_headers
This commit is contained in:
commit
6d07d32003
63 changed files with 1075 additions and 218 deletions
1
controllers/gce/.gitignore
vendored
Normal file
1
controllers/gce/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
glbc
|
|
@ -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}
|
||||
```
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
for _, p := range rules.IngressRuleValue.HTTP.Paths {
|
||||
|
||||
// Check the target service for each path rule
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
if rule.IngressRuleValue.HTTP == nil {
|
||||
continue
|
||||
}
|
||||
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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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},
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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 . /
|
||||
|
||||
|
|
|
@ -237,6 +237,18 @@ http {
|
|||
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 }}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ const (
|
|||
addBaseURL = "ingress.kubernetes.io/add-base-url"
|
||||
sslRedirect = "ingress.kubernetes.io/ssl-redirect"
|
||||
forceSSLRedirect = "ingress.kubernetes.io/force-ssl-redirect"
|
||||
appRoot = "ingress.kubernetes.io/app-root"
|
||||
)
|
||||
|
||||
// Redirect describes the per location redirect config
|
||||
|
@ -41,6 +42,8 @@ type Redirect struct {
|
|||
SSLRedirect bool `json:"sslRedirect"`
|
||||
// ForceSSLRedirect indicates if the location section is accessible SSL only
|
||||
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 {
|
||||
|
@ -65,10 +68,12 @@ func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
|
|||
fSslRe = a.backendResolver.GetDefaultBackend().ForceSSLRedirect
|
||||
}
|
||||
abu, _ := parser.GetBoolAnnotation(addBaseURL, ing)
|
||||
ar, _ := parser.GetStringAnnotation(appRoot, ing)
|
||||
return &Redirect{
|
||||
Target: rt,
|
||||
AddBaseURL: abu,
|
||||
SSLRedirect: sslRe,
|
||||
ForceSSLRedirect: fSslRe,
|
||||
AppRoot: ar,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -158,3 +158,20 @@ func TestForceSSLRedirect(t *testing.T) {
|
|||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,8 +17,11 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/auth"
|
||||
|
@ -47,11 +50,13 @@ type extractorConfig interface {
|
|||
}
|
||||
|
||||
type annotationExtractor struct {
|
||||
secretResolver resolver.Secret
|
||||
annotations map[string]parser.IngressAnnotation
|
||||
}
|
||||
|
||||
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
||||
return annotationExtractor{
|
||||
cfg,
|
||||
map[string]parser.IngressAnnotation{
|
||||
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||
"ExternalAuth": authreq.NewParser(),
|
||||
|
@ -104,6 +109,7 @@ const (
|
|||
healthCheck = "HealthCheck"
|
||||
sslPassthrough = "SSLPassthrough"
|
||||
sessionAffinity = "SessionAffinity"
|
||||
certificateAuth = "CertificateAuth"
|
||||
)
|
||||
|
||||
func (e *annotationExtractor) SecureUpstream(ing *extensions.Ingress) bool {
|
||||
|
@ -125,3 +131,17 @@ func (e *annotationExtractor) SessionAffinity(ing *extensions.Ingress) *sessiona
|
|||
val, _ := e.annotations[sessionAffinity].Parse(ing)
|
||||
return val.(*sessionaffinity.AffinityConfig)
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) ContainsCertificateAuth(ing *extensions.Ingress) bool {
|
||||
val, _ := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing)
|
||||
return val != ""
|
||||
}
|
||||
|
||||
func (e *annotationExtractor) CertificateAuthSecret(ing *extensions.Ingress) (*api.Secret, error) {
|
||||
val, _ := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing)
|
||||
if val == "" {
|
||||
return nil, fmt.Errorf("ingress rule %v/%v does not contains the auth-tls-secret annotation", ing.Namespace, ing.Name)
|
||||
}
|
||||
|
||||
return e.secretResolver.GetSecret(val)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package controller
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -68,13 +69,18 @@ func (ic *GenericController) syncSecret(k interface{}) error {
|
|||
}
|
||||
|
||||
// create certificates and add or update the item in the store
|
||||
_, exists = ic.sslCertTracker.Get(key)
|
||||
cur, exists := ic.sslCertTracker.Get(key)
|
||||
if exists {
|
||||
glog.V(3).Infof("updating secret %v/%v in the store ", sec.Namespace, sec.Name)
|
||||
s := cur.(*ingress.SSLCert)
|
||||
if reflect.DeepEqual(s, cert) {
|
||||
// no need to update
|
||||
return nil
|
||||
}
|
||||
glog.Infof("updating secret %v/%v in the local store", sec.Namespace, sec.Name)
|
||||
ic.sslCertTracker.Update(key, cert)
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
@ -84,10 +90,10 @@ func (ic *GenericController) syncSecret(k interface{}) error {
|
|||
func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
|
||||
secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retriveing secret %v: %v", secretName, err)
|
||||
return nil, fmt.Errorf("error retrieving secret %v: %v", secretName, err)
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("secret named %v does not exists", secretName)
|
||||
return nil, fmt.Errorf("secret named %v does not exist", secretName)
|
||||
}
|
||||
|
||||
secret := secretInterface.(*api.Secret)
|
||||
|
@ -100,13 +106,13 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC
|
|||
|
||||
var s *ingress.SSLCert
|
||||
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)
|
||||
} 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)
|
||||
} else {
|
||||
return nil, fmt.Errorf("No 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)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -122,10 +128,14 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC
|
|||
func (ic *GenericController) secrReferenced(name, namespace string) bool {
|
||||
for _, ingIf := range ic.ingLister.Store.List() {
|
||||
ing := ingIf.(*extensions.Ingress)
|
||||
str, err := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing)
|
||||
if err == nil && str == fmt.Sprintf("%v/%v", namespace, name) {
|
||||
|
||||
if ic.annotations.ContainsCertificateAuth(ing) {
|
||||
str, _ := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing)
|
||||
if str == fmt.Sprintf("%v/%v", namespace, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if ing.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ type GenericController struct {
|
|||
|
||||
// Configuration contains all the settings required by an Ingress controller
|
||||
type Configuration struct {
|
||||
Client *clientset.Clientset
|
||||
Client clientset.Interface
|
||||
|
||||
ResyncPeriod time.Duration
|
||||
DefaultService string
|
||||
|
@ -170,6 +170,12 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
}
|
||||
ic.recorder.Eventf(addIng, api.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
|
||||
ic.syncQueue.Enqueue(obj)
|
||||
if ic.annotations.ContainsCertificateAuth(addIng) {
|
||||
s, err := ic.annotations.CertificateAuthSecret(addIng)
|
||||
if err == nil {
|
||||
ic.syncSecret(fmt.Sprintf("%v/%v", s.Namespace, s.Name))
|
||||
}
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
delIng := obj.(*extensions.Ingress)
|
||||
|
@ -209,6 +215,13 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
}()
|
||||
}
|
||||
}
|
||||
if ic.annotations.ContainsCertificateAuth(upIng) {
|
||||
s, err := ic.annotations.CertificateAuthSecret(upIng)
|
||||
if err == nil {
|
||||
ic.syncSecret(fmt.Sprintf("%v/%v", s.Namespace, s.Name))
|
||||
}
|
||||
}
|
||||
|
||||
ic.syncQueue.Enqueue(cur)
|
||||
}
|
||||
},
|
||||
|
@ -280,11 +293,11 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
&api.Endpoints{}, ic.cfg.ResyncPeriod, eventHandler)
|
||||
|
||||
ic.secrLister.Store, ic.secrController = cache.NewInformer(
|
||||
cache.NewListWatchFromClient(ic.cfg.Client.Core().RESTClient(), "secrets", ic.cfg.Namespace, fields.Everything()),
|
||||
cache.NewListWatchFromClient(ic.cfg.Client.Core().RESTClient(), "secrets", api.NamespaceAll, fields.Everything()),
|
||||
&api.Secret{}, ic.cfg.ResyncPeriod, secrEventHandler)
|
||||
|
||||
ic.mapLister.Store, ic.mapController = cache.NewInformer(
|
||||
cache.NewListWatchFromClient(ic.cfg.Client.Core().RESTClient(), "configmaps", ic.cfg.Namespace, fields.Everything()),
|
||||
cache.NewListWatchFromClient(ic.cfg.Client.Core().RESTClient(), "configmaps", api.NamespaceAll, fields.Everything()),
|
||||
&api.ConfigMap{}, ic.cfg.ResyncPeriod, mapEventHandler)
|
||||
|
||||
ic.svcLister.Indexer, ic.svcController = cache.NewIndexerInformer(
|
||||
|
@ -551,7 +564,7 @@ func (ic *GenericController) getDefaultUpstream() *ingress.Backend {
|
|||
}
|
||||
|
||||
if !svcExists {
|
||||
glog.Warningf("service %v does not exists", svcKey)
|
||||
glog.Warningf("service %v does not exist", svcKey)
|
||||
upstream.Endpoints = append(upstream.Endpoints, newDefaultServer())
|
||||
return upstream
|
||||
}
|
||||
|
@ -693,7 +706,7 @@ func (ic GenericController) GetAuthCertificate(secretName string) (*resolver.Aut
|
|||
|
||||
bc, exists := ic.sslCertTracker.Get(secretName)
|
||||
if !exists {
|
||||
return &resolver.AuthSSLCert{}, fmt.Errorf("secret %v does not exists", secretName)
|
||||
return &resolver.AuthSSLCert{}, fmt.Errorf("secret %v does not exist", secretName)
|
||||
}
|
||||
cert := bc.(*ingress.SSLCert)
|
||||
return &resolver.AuthSSLCert{
|
||||
|
@ -792,7 +805,7 @@ func (ic *GenericController) serviceEndpoints(svcKey, backendPort string,
|
|||
}
|
||||
|
||||
if !svcExists {
|
||||
err = fmt.Errorf("service %v does not exists", svcKey)
|
||||
err = fmt.Errorf("service %v does not exist", svcKey)
|
||||
return upstreams, err
|
||||
}
|
||||
|
||||
|
@ -947,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)
|
||||
bc, exists := ic.sslCertTracker.Get(key)
|
||||
if exists {
|
||||
|
@ -954,7 +973,11 @@ func (ic *GenericController) createServers(data []interface{},
|
|||
if isHostValid(host, cert) {
|
||||
servers[host].SSLCertificate = cert.PemFileName
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -978,6 +1001,11 @@ func (ic *GenericController) getEndpoints(
|
|||
|
||||
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 _, epPort := range ss.Ports {
|
||||
|
||||
|
@ -1018,6 +1046,10 @@ func (ic *GenericController) getEndpoints(
|
|||
}
|
||||
|
||||
for _, epAddress := range ss.Addresses {
|
||||
ep := fmt.Sprintf("%v:%v", epAddress.IP, targetPort)
|
||||
if _, exists := adus[ep]; exists {
|
||||
continue
|
||||
}
|
||||
ups := ingress.Endpoint{
|
||||
Address: epAddress.IP,
|
||||
Port: fmt.Sprintf("%v", targetPort),
|
||||
|
@ -1025,6 +1057,7 @@ func (ic *GenericController) getEndpoints(
|
|||
FailTimeout: hz.FailTimeout,
|
||||
}
|
||||
upsServers = append(upsServers, ups)
|
||||
adus[ep] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,10 +128,12 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
glog.Infof("service %v validated as source of Ingress status", *publishSvc)
|
||||
}
|
||||
|
||||
if *configMap != "" {
|
||||
_, _, err = k8s.ParseNameNS(*configMap)
|
||||
if *watchNamespace != "" {
|
||||
|
||||
_, err = k8s.IsValidNamespace(kubeClient, *watchNamespace)
|
||||
|
||||
if err != nil {
|
||||
glog.Fatalf("configmap error: %v", err)
|
||||
glog.Fatalf("no watchNamespace with name %v found: %v", *watchNamespace, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,13 +32,13 @@ import (
|
|||
)
|
||||
|
||||
// checkSvcForUpdate verifies if one of the running pods for a service contains
|
||||
// named port. If the annotation in the service does not exists or is not equals
|
||||
// named port. If the annotation in the service does not exist or is not equals
|
||||
// to the port mapping obtained from the pod the service must be updated to reflect
|
||||
// the current state
|
||||
func (ic *GenericController) checkSvcForUpdate(svc *api.Service) error {
|
||||
// get the pods associated with the service
|
||||
// TODO: switch this to a watch
|
||||
pods, err := ic.cfg.Client.Pods(svc.Namespace).List(api.ListOptions{
|
||||
pods, err := ic.cfg.Client.Core().Pods(svc.Namespace).List(api.ListOptions{
|
||||
LabelSelector: labels.Set(svc.Spec.Selector).AsSelector(),
|
||||
})
|
||||
|
||||
|
@ -82,7 +82,7 @@ func (ic *GenericController) checkSvcForUpdate(svc *api.Service) error {
|
|||
if len(namedPorts) > 0 && !reflect.DeepEqual(curNamedPort, namedPorts) {
|
||||
data, _ := json.Marshal(namedPorts)
|
||||
|
||||
newSvc, err := ic.cfg.Client.Services(svc.Namespace).Get(svc.Name)
|
||||
newSvc, err := ic.cfg.Client.Core().Services(svc.Namespace).Get(svc.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting service %v/%v: %v", svc.Namespace, svc.Name, err)
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ func (ic *GenericController) checkSvcForUpdate(svc *api.Service) error {
|
|||
|
||||
newSvc.ObjectMeta.Annotations[service.NamedPortAnnotation] = string(data)
|
||||
glog.Infof("updating service %v with new named port mappings", svc.Name)
|
||||
_, err = ic.cfg.Client.Services(svc.Namespace).Update(newSvc)
|
||||
_, err = ic.cfg.Client.Core().Services(svc.Namespace).Update(newSvc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error syncing service %v/%v: %v", svc.Namespace, svc.Name, err)
|
||||
}
|
||||
|
|
191
core/pkg/ingress/controller/named_port_test.go
Normal file
191
core/pkg/ingress/controller/named_port_test.go
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/service"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
testclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func buildSimpleClientSet() *testclient.Clientset {
|
||||
return testclient.NewSimpleClientset(
|
||||
&api.PodList{Items: []api.Pod{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo1",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"lable_sig": "foo_pod",
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
NodeName: "foo_node_1",
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Ports: []api.ContainerPort{
|
||||
{
|
||||
Name: "foo1_named_port_c1",
|
||||
Protocol: api.ProtocolTCP,
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo1",
|
||||
Namespace: api.NamespaceSystem,
|
||||
Labels: map[string]string{
|
||||
"lable_sig": "foo_pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
&api.ServiceList{Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Namespace: api.NamespaceDefault,
|
||||
Name: "named_port_test_service",
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
}
|
||||
|
||||
func buildGenericController() *GenericController {
|
||||
return &GenericController{
|
||||
cfg: &Configuration{
|
||||
Client: buildSimpleClientSet(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildService() *api.Service {
|
||||
return &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Namespace: api.NamespaceSystem,
|
||||
Name: "named_port_test_service",
|
||||
},
|
||||
|
||||
Spec: api.ServiceSpec{
|
||||
ClusterIP: "10.10.10.10",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckSvcForUpdate(t *testing.T) {
|
||||
foos := []struct {
|
||||
n string
|
||||
ns string
|
||||
sps []api.ServicePort
|
||||
sl map[string]string
|
||||
er string
|
||||
}{
|
||||
{
|
||||
"pods_have_not_been_found_in_this_namespace",
|
||||
api.NamespaceSystem,
|
||||
[]api.ServicePort{
|
||||
{Name: "foo_port_1", Port: 8080, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("foo1_named_port_c1")},
|
||||
{Name: "foo_port_2", Port: 8181, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(81)},
|
||||
{Name: "foo_port_3", Port: 8282, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("")},
|
||||
},
|
||||
map[string]string{
|
||||
"lable_sig": "foo_pod",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"ports_have_not_been_found_in_this_pod",
|
||||
api.NamespaceDefault,
|
||||
[]api.ServicePort{
|
||||
{Name: "foo_port_1", Port: 8080, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("foo1_named_port_cXX")},
|
||||
{Name: "foo_port_2", Port: 8181, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(81)},
|
||||
{Name: "foo_port_3", Port: 8282, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("")},
|
||||
},
|
||||
map[string]string{
|
||||
"lable_sig": "foo_pod",
|
||||
},
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"ports_fixed",
|
||||
api.NamespaceDefault,
|
||||
[]api.ServicePort{
|
||||
{Name: "foo_port_1", Port: 8080, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(80)},
|
||||
{Name: "foo_port_2", Port: 8181, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(81)},
|
||||
{Name: "foo_port_3", Port: 8282, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("")},
|
||||
},
|
||||
map[string]string{
|
||||
"lable_sig": "foo_pod",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"nil_selector",
|
||||
api.NamespaceDefault,
|
||||
[]api.ServicePort{
|
||||
{Name: "foo_port_1", Port: 8080, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("foo1_named_port_c1")},
|
||||
{Name: "foo_port_2", Port: 8181, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(81)},
|
||||
{Name: "foo_port_3", Port: 8282, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("")},
|
||||
},
|
||||
nil,
|
||||
"{\"foo1_named_port_c1\":\"80\"}",
|
||||
},
|
||||
{
|
||||
"normal_update",
|
||||
api.NamespaceDefault,
|
||||
[]api.ServicePort{
|
||||
{Name: "foo_port_1", Port: 8080, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("foo1_named_port_c1")},
|
||||
{Name: "foo_port_2", Port: 8181, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(81)},
|
||||
{Name: "foo_port_3", Port: 8282, Protocol: api.ProtocolTCP, TargetPort: intstr.FromString("")},
|
||||
},
|
||||
map[string]string{
|
||||
"lable_sig": "foo_pod",
|
||||
},
|
||||
"{\"foo1_named_port_c1\":\"80\"}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, foo := range foos {
|
||||
t.Run(foo.n, func(t *testing.T) {
|
||||
gc := buildGenericController()
|
||||
s := buildService()
|
||||
s.SetNamespace(foo.ns)
|
||||
s.Spec.Ports = foo.sps
|
||||
s.Spec.Selector = foo.sl
|
||||
|
||||
err := gc.checkSvcForUpdate(s)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
rs, _ := gc.cfg.Client.Core().Services(api.NamespaceDefault).Get("named_port_test_service")
|
||||
rr := rs.ObjectMeta.Annotations[service.NamedPortAnnotation]
|
||||
if !reflect.DeepEqual(rr, foo.er) {
|
||||
t.Errorf("Returned %s, but expected %s for %s", rr, foo.er, foo.n)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ func isHostValid(host string, cert *ingress.SSLCert) bool {
|
|||
return false
|
||||
}
|
||||
for _, cn := range cert.CN {
|
||||
if matchHostnames(cn, host) {
|
||||
if matchHostnames(cn, strings.ToLower(host)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import "net"
|
|||
// 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
|
||||
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
|
||||
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
|
||||
|
|
|
@ -252,7 +252,7 @@ func (s *statusSync) updateStatus(newIPs []api.LoadBalancerIngress) {
|
|||
continue
|
||||
}
|
||||
|
||||
go func(wg *sync.WaitGroup) {
|
||||
go func(wg *sync.WaitGroup, ing *extensions.Ingress) {
|
||||
defer wg.Done()
|
||||
ingClient := s.Client.Extensions().Ingresses(ing.Namespace)
|
||||
currIng, err := ingClient.Get(ing.Name)
|
||||
|
@ -274,7 +274,7 @@ func (s *statusSync) updateStatus(newIPs []api.LoadBalancerIngress) {
|
|||
if err != nil {
|
||||
glog.Warningf("error updating ingress rule: %v", err)
|
||||
}
|
||||
}(&wg)
|
||||
}(&wg, ing)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
|
|
@ -34,6 +34,30 @@ func IsValidService(kubeClient clientset.Interface, name string) (*api.Service,
|
|||
return kubeClient.Core().Services(ns).Get(name)
|
||||
}
|
||||
|
||||
// isValidConfigMap check if exists a configmap with the specified name
|
||||
func IsValidConfigMap(kubeClient clientset.Interface, fullName string) (*api.ConfigMap, error) {
|
||||
|
||||
ns, name, err := ParseNameNS(fullName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configMap, err := kubeClient.Core().ConfigMaps(ns).Get(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("configmap not found: %v", err)
|
||||
}
|
||||
|
||||
return configMap, nil
|
||||
|
||||
}
|
||||
|
||||
// isValidNamespace chck if exists a namespace with the specified name
|
||||
func IsValidNamespace(kubeClient clientset.Interface, name string) (*api.Namespace, error) {
|
||||
return kubeClient.Core().Namespaces().Get(name)
|
||||
}
|
||||
|
||||
// IsValidSecret checks if exists a secret with the specified name
|
||||
func IsValidSecret(kubeClient clientset.Interface, name string) (*api.Secret, error) {
|
||||
ns, name, err := ParseNameNS(name)
|
||||
|
|
|
@ -85,6 +85,64 @@ func TestIsValidService(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIsValidNamespace(t *testing.T) {
|
||||
|
||||
fk := testclient.NewSimpleClientset(&api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "default",
|
||||
},
|
||||
})
|
||||
|
||||
_, err := IsValidNamespace(fk, "empty")
|
||||
if err == nil {
|
||||
t.Errorf("expected error but return nill")
|
||||
}
|
||||
|
||||
ns, err := IsValidNamespace(fk, "default")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if ns == nil {
|
||||
t.Errorf("expected a configmap but returned nil")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIsValidConfigMap(t *testing.T) {
|
||||
|
||||
fk := testclient.NewSimpleClientset(&api.ConfigMap{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Namespace: api.NamespaceDefault,
|
||||
Name: "demo",
|
||||
},
|
||||
})
|
||||
|
||||
_, err := IsValidConfigMap(fk, "")
|
||||
if err == nil {
|
||||
t.Errorf("expected error but return nill")
|
||||
}
|
||||
|
||||
s, err := IsValidConfigMap(fk, "default/demo")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
t.Errorf("expected a configmap but returned nil")
|
||||
}
|
||||
|
||||
fk = testclient.NewSimpleClientset()
|
||||
s, err = IsValidConfigMap(fk, "default/demo")
|
||||
if err == nil {
|
||||
t.Errorf("expected an error but returned nil")
|
||||
}
|
||||
if s != nil {
|
||||
t.Errorf("unexpected Configmap returned: %v", s)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIsValidSecret(t *testing.T) {
|
||||
fk := testclient.NewSimpleClientset(&api.Secret{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
|
|
|
@ -43,10 +43,10 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
|||
|
||||
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
||||
|
||||
glog.V(3).Infof("Creating temp file %v for Keypair: %v", tempPemFile.Name(), pemName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err)
|
||||
}
|
||||
glog.V(3).Infof("Creating temp file %v for Keypair: %v", tempPemFile.Name(), pemName)
|
||||
|
||||
_, err = tempPemFile.Write(cert)
|
||||
if err != nil {
|
||||
|
@ -75,13 +75,13 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
|||
pemBlock, _ := pem.Decode(pemCerts)
|
||||
if pemBlock == nil {
|
||||
_ = 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 pemBlock.Type != "CERTIFICATE" {
|
||||
_ = 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)
|
||||
|
@ -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)
|
||||
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()
|
||||
|
@ -150,11 +150,11 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) {
|
|||
|
||||
pemCABlock, _ := pem.Decode(ca)
|
||||
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 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)
|
||||
|
@ -206,13 +206,13 @@ func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
|
|||
pemBlock, _ := pem.Decode(pemCerts)
|
||||
if pemBlock == nil {
|
||||
_ = 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 pemBlock.Type != "DH PARAMETERS" {
|
||||
_ = 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)
|
||||
|
|
|
@ -23,7 +23,7 @@ Table of Contents
|
|||
The Kubernetes Service is an abstraction over endpoints (pod-ip:port pairings).
|
||||
The Ingress is an abstraction over Services. This doesn't mean all Ingress
|
||||
controller must route *through* a Service, but rather, that routing, security
|
||||
and auth configuration is represented in the Ingerss resource per Service, and
|
||||
and auth configuration is represented in the Ingress resource per Service, and
|
||||
not per pod. As long as this configuration is respected, a given Ingress
|
||||
controller is free to route to the DNS name of a Service, the VIP, a NodePort,
|
||||
or directly to the Service's endpoints.
|
||||
|
|
|
@ -72,7 +72,7 @@ In addition to this pipeline:
|
|||
Service
|
||||
* Each port on the Backend Service has a matching port on the Instance Group
|
||||
* Each port on the Backend Service is exposed through a firewall-rule open
|
||||
to the GCE LB IP range (`130.211.0.0/22`)
|
||||
to the GCE LB IP ranges (`130.211.0.0/22` and `35.191.0.0/16`)
|
||||
|
||||
## The Ingress controller events complain about quota, how do I increase it?
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ spec:
|
|||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
|
|
|
@ -28,8 +28,8 @@ Re-encrypty | terminate, apply routing rules, re-encrypt | nginx | Advanced
|
|||
|
||||
Name | Description | Platform | Complexity Level
|
||||
-----| ----------- | ---------- | ----------------
|
||||
Daemonset | run multiple controllers in a daemonset | nginx | Intermediate
|
||||
Deployment | run multiple controllers as a deployment | nginx | Intermediate
|
||||
Daemonset | run multiple controllers in a daemonset | nginx/haproxy | Intermediate
|
||||
Deployment | run multiple controllers as a deployment | nginx/haproxy | Intermediate
|
||||
Multi-zone | bridge different zones in a single cluster | gce | Intermediate
|
||||
Static-ip | a single ingress gets a single static ip | * | Intermediate
|
||||
Geo-routing | route to geographically closest endpoint | nginx | Advanced
|
||||
|
|
|
@ -19,7 +19,7 @@ spec:
|
|||
# hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
|||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
imagePullPolicy: Always
|
||||
readinessProbe:
|
||||
|
|
|
@ -19,7 +19,7 @@ spec:
|
|||
# hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
|||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
imagePullPolicy: Always
|
||||
readinessProbe:
|
||||
|
|
59
examples/customization/custom-vts-metrics/nginx/README.md
Normal file
59
examples/customization/custom-vts-metrics/nginx/README.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Deploying the Nginx Ingress controller
|
||||
|
||||
This example aims to demonstrate the deployment of an nginx ingress controller and
|
||||
use a ConfigMap to enable nginx vts module and export metrics for prometheus.
|
||||
|
||||
## Default Backend
|
||||
|
||||
The default backend is a Service capable of handling all url paths and hosts the
|
||||
nginx controller doesn't understand. This most basic implementation just returns
|
||||
a 404 page:
|
||||
|
||||
```console
|
||||
$ kubectl apply -f default-backend.yaml
|
||||
deployment "default-http-backend" created
|
||||
service "default-http-backend" created
|
||||
|
||||
$ kubectl -n kube-system get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
default-http-backend-2657704409-qgwdd 1/1 Running 0 28s
|
||||
```
|
||||
|
||||
## Custom configuration
|
||||
|
||||
```console
|
||||
$ cat nginx-vts-metrics-conf.yaml
|
||||
apiVersion: v1
|
||||
data:
|
||||
enable-vts-status: "true"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: nginx-vts-metrics-conf
|
||||
namespace: kube-system
|
||||
```
|
||||
|
||||
```console
|
||||
$ kubectl create -f nginx-vts-metrics-conf.yaml
|
||||
```
|
||||
|
||||
## Controller
|
||||
|
||||
You can deploy the controller as follows:
|
||||
|
||||
```console
|
||||
$ kubectl apply -f nginx-ingress-controller.yaml
|
||||
deployment "nginx-ingress-controller" created
|
||||
|
||||
$ kubectl -n kube-system get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
default-http-backend-2657704409-qgwdd 1/1 Running 0 2m
|
||||
nginx-ingress-controller-873061567-4n3k2 1/1 Running 0 42s
|
||||
```
|
||||
|
||||
## Result
|
||||
Check wether to open the vts status:
|
||||
```console
|
||||
$ kubectl exec nginx-ingress-controller-873061567-4n3k2 -n kube-system cat /etc/nginx/nginx.conf|grep vhost_traffic_status_display
|
||||
vhost_traffic_status_display;
|
||||
vhost_traffic_status_display_format html;
|
||||
```
|
|
@ -0,0 +1,51 @@
|
|||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: default-http-backend
|
||||
labels:
|
||||
k8s-app: default-http-backend
|
||||
namespace: kube-system
|
||||
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
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: default-http-backend
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
selector:
|
||||
k8s-app: default-http-backend
|
|
@ -0,0 +1,56 @@
|
|||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-ingress-controller
|
||||
labels:
|
||||
k8s-app: nginx-ingress-controller
|
||||
namespace: kube-system
|
||||
annotations:
|
||||
prometheus.io/port: "10254"
|
||||
prometheus.io/scrape: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: nginx-ingress-controller
|
||||
spec:
|
||||
# hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
|
||||
# however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host
|
||||
# that said, since hostPort is broken on CNI (https://github.com/kubernetes/kubernetes/issues/31307) we have to use hostNetwork where CNI is used
|
||||
# like with kubeadm
|
||||
# hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||
name: nginx-ingress-controller
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 10254
|
||||
scheme: HTTP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 10254
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 1
|
||||
ports:
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
- containerPort: 443
|
||||
hostPort: 443
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
args:
|
||||
- /nginx-ingress-controller
|
||||
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
|
||||
- --configmap=$(POD_NAMESPACE)/nginx-vts-metrics-conf
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
enable-vts-status: "true"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: nginx-vts-metrics-conf
|
||||
namespace: kube-system
|
|
@ -10,10 +10,13 @@ spec:
|
|||
metadata:
|
||||
labels:
|
||||
name: nginx-ingress-lb
|
||||
annotations:
|
||||
prometheus.io/port: '10254'
|
||||
prometheus.io/scrape: 'true'
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -71,7 +71,7 @@ spec:
|
|||
hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -11,6 +11,9 @@ spec:
|
|||
metadata:
|
||||
labels:
|
||||
k8s-app: nginx-ingress-controller
|
||||
annotations:
|
||||
prometheus.io/port: '10254'
|
||||
prometheus.io/scrape: 'true'
|
||||
spec:
|
||||
# hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
|
||||
# however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host
|
||||
|
@ -19,7 +22,7 @@ spec:
|
|||
# hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -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-
|
||||
```
|
||||
|
127
examples/rewrite/nginx/README.md
Normal file
127
examples/rewrite/nginx/README.md
Normal 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
|
||||
```
|
65
examples/scaling-deployment/haproxy/README.md
Normal file
65
examples/scaling-deployment/haproxy/README.md
Normal 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
|
||||
```
|
49
examples/scaling-deployment/haproxy/default-backend.yaml
Normal file
49
examples/scaling-deployment/haproxy/default-backend.yaml
Normal 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
|
|
@ -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
|
|
@ -14,7 +14,7 @@ spec:
|
|||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -18,7 +18,7 @@ spec:
|
|||
# hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
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
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -234,7 +234,7 @@ func getDeployer() (deployer, error) {
|
|||
case "bash":
|
||||
return bash{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown deployment strategy %q", *deployment)
|
||||
return nil, fmt.Errorf("unknown deployment strategy %q", *deployment)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue