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
|
### 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/)
|
[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:
|
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
|
```console
|
||||||
$ gcloud compute firewall-rules create allow-130-211-0-0-22 \
|
$ 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 \
|
--target-tags $TAG \
|
||||||
--allow tcp:$NODE_PORT
|
--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
|
```console
|
||||||
$ kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services ${SERVICE_NAME}
|
$ kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services ${SERVICE_NAME}
|
||||||
```
|
```
|
||||||
|
|
|
@ -119,7 +119,7 @@ func NewBackendPool(
|
||||||
func(i interface{}) (string, error) {
|
func(i interface{}) (string, error) {
|
||||||
bs := i.(*compute.BackendService)
|
bs := i.(*compute.BackendService)
|
||||||
if !namer.NameBelongsToCluster(bs.Name) {
|
if !namer.NameBelongsToCluster(bs.Name) {
|
||||||
return "", fmt.Errorf("Unrecognized name %v", bs.Name)
|
return "", fmt.Errorf("unrecognized name %v", bs.Name)
|
||||||
}
|
}
|
||||||
port, err := namer.BePort(bs.Name)
|
port, err := namer.BePort(bs.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (f *FakeBackendServices) GetBackendService(name string) (*compute.BackendSe
|
||||||
f.calls = append(f.calls, utils.Get)
|
f.calls = append(f.calls, utils.Get)
|
||||||
obj, exists, err := f.backendServices.GetByKey(name)
|
obj, exists, err := f.backendServices.GetByKey(name)
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("Backend service %v not found", name)
|
return nil, fmt.Errorf("backend service %v not found", name)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -57,7 +57,7 @@ func (f *FakeBackendServices) GetBackendService(name string) (*compute.BackendSe
|
||||||
if name == svc.Name {
|
if name == svc.Name {
|
||||||
return svc, nil
|
return svc, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Backend service %v not found", name)
|
return nil, fmt.Errorf("backend service %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBackendService fakes backend service creation.
|
// CreateBackendService fakes backend service creation.
|
||||||
|
@ -77,7 +77,7 @@ func (f *FakeBackendServices) DeleteBackendService(name string) error {
|
||||||
f.calls = append(f.calls, utils.Delete)
|
f.calls = append(f.calls, utils.Delete)
|
||||||
svc, exists, err := f.backendServices.GetByKey(name)
|
svc, exists, err := f.backendServices.GetByKey(name)
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf("Backend service %v not found", name)
|
return fmt.Errorf("backend service %v not found", name)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -290,7 +290,7 @@ func (lbc *LoadBalancerController) storesSynced() bool {
|
||||||
func (lbc *LoadBalancerController) sync(key string) (err error) {
|
func (lbc *LoadBalancerController) sync(key string) (err error) {
|
||||||
if !lbc.hasSynced() {
|
if !lbc.hasSynced() {
|
||||||
time.Sleep(storeSyncPollPeriod)
|
time.Sleep(storeSyncPollPeriod)
|
||||||
return fmt.Errorf("Waiting for stores to sync")
|
return fmt.Errorf("waiting for stores to sync")
|
||||||
}
|
}
|
||||||
glog.V(3).Infof("Syncing %v", key)
|
glog.V(3).Infof("Syncing %v", key)
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ func (lbc *LoadBalancerController) sync(key string) (err error) {
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if deferErr := lbc.CloudClusterManager.GC(lbNames, nodePorts); deferErr != nil {
|
if deferErr := lbc.CloudClusterManager.GC(lbNames, nodePorts); deferErr != nil {
|
||||||
err = fmt.Errorf("Error during sync %v, error during GC %v", err, deferErr)
|
err = fmt.Errorf("error during sync %v, error during GC %v", err, deferErr)
|
||||||
}
|
}
|
||||||
glog.V(3).Infof("Finished syncing %v", key)
|
glog.V(3).Infof("Finished syncing %v", key)
|
||||||
}()
|
}()
|
||||||
|
@ -343,7 +343,7 @@ func (lbc *LoadBalancerController) sync(key string) (err error) {
|
||||||
if ingExists {
|
if ingExists {
|
||||||
lbc.recorder.Eventf(obj.(*extensions.Ingress), api.EventTypeWarning, eventMsg, err.Error())
|
lbc.recorder.Eventf(obj.(*extensions.Ingress), api.EventTypeWarning, eventMsg, err.Error())
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("%v Error: %v", eventMsg, err)
|
err = fmt.Errorf("%v, error: %v", eventMsg, err)
|
||||||
}
|
}
|
||||||
syncError = err
|
syncError = err
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,11 +65,11 @@ func (t *apiServerTLSLoader) load(ing *extensions.Ingress) (*loadbalancers.TLSCe
|
||||||
}
|
}
|
||||||
cert, ok := secret.Data[api.TLSCertKey]
|
cert, ok := secret.Data[api.TLSCertKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Secret %v has no private key", secretName)
|
return nil, fmt.Errorf("secret %v has no private key", secretName)
|
||||||
}
|
}
|
||||||
key, ok := secret.Data[api.TLSPrivateKeyKey]
|
key, ok := secret.Data[api.TLSPrivateKeyKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Secret %v has no cert", secretName)
|
return nil, fmt.Errorf("secret %v has no cert", secretName)
|
||||||
}
|
}
|
||||||
certs := &loadbalancers.TLSCerts{Key: string(key), Cert: string(cert)}
|
certs := &loadbalancers.TLSCerts{Key: string(key), Cert: string(cert)}
|
||||||
if err := t.validate(certs); err != nil {
|
if err := t.validate(certs); err != nil {
|
||||||
|
@ -95,5 +95,5 @@ func (f *fakeTLSSecretLoader) load(ing *extensions.Ingress) (*loadbalancers.TLSC
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Couldn't find secret for ingress %v", ing.Name)
|
return nil, fmt.Errorf("couldn't find secret for ingress %v", ing.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
// GetServiceIngress gets all the Ingress' that have rules pointing to a service.
|
||||||
// Note that this ignores services without the right nodePorts.
|
// Note that this ignores services without the right nodePorts.
|
||||||
func (s *StoreToIngressLister) GetServiceIngress(svc *api.Service) (ings []extensions.Ingress, err error) {
|
func (s *StoreToIngressLister) GetServiceIngress(svc *api.Service) (ings []extensions.Ingress, err error) {
|
||||||
|
IngressLoop:
|
||||||
for _, m := range s.Store.List() {
|
for _, m := range s.Store.List() {
|
||||||
ing := *m.(*extensions.Ingress)
|
ing := *m.(*extensions.Ingress)
|
||||||
if ing.Namespace != svc.Namespace {
|
if ing.Namespace != svc.Namespace {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, rules := range ing.Spec.Rules {
|
|
||||||
if rules.IngressRuleValue.HTTP == nil {
|
// Check service of default backend
|
||||||
|
if ing.Spec.Backend != nil && ing.Spec.Backend.ServiceName == svc.Name {
|
||||||
|
ings = append(ings, ing)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the target service for each path rule
|
||||||
|
for _, rule := range ing.Spec.Rules {
|
||||||
|
if rule.IngressRuleValue.HTTP == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, p := range rules.IngressRuleValue.HTTP.Paths {
|
for _, p := range rule.IngressRuleValue.HTTP.Paths {
|
||||||
if p.Backend.ServiceName == svc.Name {
|
if p.Backend.ServiceName == svc.Name {
|
||||||
ings = append(ings, ing)
|
ings = append(ings, ing)
|
||||||
|
// Skip the rest of the rules to avoid duplicate ingresses in list
|
||||||
|
continue IngressLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(ings) == 0 {
|
if len(ings) == 0 {
|
||||||
err = fmt.Errorf("No ingress for service %v", svc.Name)
|
err = fmt.Errorf("no ingress for service %v", svc.Name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -315,7 +326,7 @@ func (t *GCETranslator) toGCEBackend(be *extensions.IngressBackend, ns string) (
|
||||||
backend, err := t.CloudClusterManager.backendPool.Get(int64(port))
|
backend, err := t.CloudClusterManager.backendPool.Get(int64(port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"No GCE backend exists for port %v, kube backend %+v", port, be)
|
"no GCE backend exists for port %v, kube backend %+v", port, be)
|
||||||
}
|
}
|
||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
@ -332,7 +343,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
|
||||||
})
|
})
|
||||||
if !exists {
|
if !exists {
|
||||||
return invalidPort, errorNodePortNotFound{be, fmt.Errorf(
|
return invalidPort, errorNodePortNotFound{be, fmt.Errorf(
|
||||||
"Service %v/%v not found in store", namespace, be.ServiceName)}
|
"service %v/%v not found in store", namespace, be.ServiceName)}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return invalidPort, errorNodePortNotFound{be, err}
|
return invalidPort, errorNodePortNotFound{be, err}
|
||||||
|
@ -356,7 +367,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
|
||||||
return nodePort, nil
|
return nodePort, nil
|
||||||
}
|
}
|
||||||
return invalidPort, errorNodePortNotFound{be, fmt.Errorf(
|
return invalidPort, errorNodePortNotFound{be, fmt.Errorf(
|
||||||
"Could not find matching nodeport from service.")}
|
"could not find matching nodeport from service")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// toNodePorts converts a pathlist to a flat list of nodeports.
|
// toNodePorts converts a pathlist to a flat list of nodeports.
|
||||||
|
@ -374,7 +385,7 @@ func (t *GCETranslator) toNodePorts(ings *extensions.IngressList) []int64 {
|
||||||
}
|
}
|
||||||
for _, rule := range ing.Spec.Rules {
|
for _, rule := range ing.Spec.Rules {
|
||||||
if rule.HTTP == nil {
|
if rule.HTTP == nil {
|
||||||
glog.Errorf("Ignoring non http Ingress rule.")
|
glog.Errorf("ignoring non http Ingress rule")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, path := range rule.HTTP.Paths {
|
for _, path := range rule.HTTP.Paths {
|
||||||
|
@ -411,7 +422,7 @@ func (t *GCETranslator) GetZoneForNode(name string) (string, error) {
|
||||||
return getZone(n), nil
|
return getZone(n), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("Node not found %v", name)
|
return "", fmt.Errorf("node not found %v", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListZones returns a list of zones this Kubernetes cluster spans.
|
// ListZones returns a list of zones this Kubernetes cluster spans.
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (f *fakeFirewallRules) GetFirewall(name string) (*compute.Firewall, error)
|
||||||
return rule, nil
|
return rule, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Firewall rule %v not found.", name)
|
return nil, fmt.Errorf("firewall rule %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeFirewallRules) CreateFirewall(name, msgTag string, srcRange netset.IPNet, ports []int64, hosts []string) error {
|
func (f *fakeFirewallRules) CreateFirewall(name, msgTag string, srcRange netset.IPNet, ports []int64, hosts []string) error {
|
||||||
|
@ -66,7 +66,7 @@ func (f *fakeFirewallRules) DeleteFirewall(name string) error {
|
||||||
firewalls = append(firewalls, rule)
|
firewalls = append(firewalls, rule)
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf("Failed to find health check %v", name)
|
return fmt.Errorf("failed to find health check %v", name)
|
||||||
}
|
}
|
||||||
f.fw = firewalls
|
f.fw = firewalls
|
||||||
return nil
|
return nil
|
||||||
|
@ -95,7 +95,7 @@ func (f *fakeFirewallRules) UpdateFirewall(name, msgTag string, srcRange netset.
|
||||||
if exists {
|
if exists {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Update failed for rule %v, srcRange %v ports %v, rule not found", name, srcRange, ports)
|
return fmt.Errorf("update failed for rule %v, srcRange %v ports %v, rule not found", name, srcRange, ports)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFakeFirewallRules creates a fake for firewall rules.
|
// NewFakeFirewallRules creates a fake for firewall rules.
|
||||||
|
|
|
@ -26,25 +26,25 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Src range from which the GCE L7 performs health checks.
|
// Src ranges from which the GCE L7 performs health checks.
|
||||||
const l7SrcRange = "130.211.0.0/22"
|
var l7SrcRanges = []string{"130.211.0.0/22", "35.191.0.0/16"}
|
||||||
|
|
||||||
// FirewallRules manages firewall rules.
|
// FirewallRules manages firewall rules.
|
||||||
type FirewallRules struct {
|
type FirewallRules struct {
|
||||||
cloud Firewall
|
cloud Firewall
|
||||||
namer *utils.Namer
|
namer *utils.Namer
|
||||||
srcRange netset.IPNet
|
srcRanges netset.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFirewallPool creates a new firewall rule manager.
|
// NewFirewallPool creates a new firewall rule manager.
|
||||||
// cloud: the cloud object implementing Firewall.
|
// cloud: the cloud object implementing Firewall.
|
||||||
// namer: cluster namer.
|
// namer: cluster namer.
|
||||||
func NewFirewallPool(cloud Firewall, namer *utils.Namer) SingleFirewallPool {
|
func NewFirewallPool(cloud Firewall, namer *utils.Namer) SingleFirewallPool {
|
||||||
srcNetSet, err := netset.ParseIPNets(l7SrcRange)
|
srcNetSet, err := netset.ParseIPNets(l7SrcRanges...)
|
||||||
if err != nil {
|
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.
|
// 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)
|
rule, _ := fr.cloud.GetFirewall(name)
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
glog.Infof("Creating global l7 firewall rule %v", name)
|
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()
|
requiredPorts := sets.NewString()
|
||||||
|
@ -77,7 +77,7 @@ func (fr *FirewallRules) Sync(nodePorts []int64, nodeNames []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
glog.V(3).Infof("Firewall rule %v already exists, updating nodeports %v", name, nodePorts)
|
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.
|
// Shutdown shuts down this firewall rules manager.
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (f *FakeHealthChecks) GetHttpHealthCheck(name string) (*compute.HttpHealthC
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Health check %v not found.", name)
|
return nil, fmt.Errorf("health check %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHttpHealthCheck fakes out deleting a http health check.
|
// DeleteHttpHealthCheck fakes out deleting a http health check.
|
||||||
|
@ -75,7 +75,7 @@ func (f *FakeHealthChecks) DeleteHttpHealthCheck(name string) error {
|
||||||
healthChecks = append(healthChecks, h)
|
healthChecks = append(healthChecks, h)
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf("Failed to find health check %v", name)
|
return fmt.Errorf("failed to find health check %v", name)
|
||||||
}
|
}
|
||||||
f.hc = healthChecks
|
f.hc = healthChecks
|
||||||
return nil
|
return nil
|
||||||
|
@ -94,7 +94,7 @@ func (f *FakeHealthChecks) UpdateHttpHealthCheck(hc *compute.HttpHealthCheck) er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf("Cannot update a non-existent health check %v", hc.Name)
|
return fmt.Errorf("cannot update a non-existent health check %v", hc.Name)
|
||||||
}
|
}
|
||||||
f.hc = healthChecks
|
f.hc = healthChecks
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (f *FakeInstanceGroups) GetInstanceGroup(name, zone string) (*compute.Insta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Return googleapi 404 error
|
// TODO: Return googleapi 404 error
|
||||||
return nil, fmt.Errorf("Instance group %v not found", name)
|
return nil, fmt.Errorf("instance group %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateInstanceGroup fakes instance group creation.
|
// CreateInstanceGroup fakes instance group creation.
|
||||||
|
@ -97,7 +97,7 @@ func (f *FakeInstanceGroups) DeleteInstanceGroup(name, zone string) error {
|
||||||
newGroups = append(newGroups, ig)
|
newGroups = append(newGroups, ig)
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf("Instance Group %v not found", name)
|
return fmt.Errorf("instance group %v not found", name)
|
||||||
}
|
}
|
||||||
f.instanceGroups = newGroups
|
f.instanceGroups = newGroups
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (f *FakeLoadBalancers) GetGlobalForwardingRule(name string) (*compute.Forwa
|
||||||
return f.Fw[i], nil
|
return f.Fw[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Forwarding rule %v not found", name)
|
return nil, fmt.Errorf("forwarding rule %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateGlobalForwardingRule fakes forwarding rule creation.
|
// CreateGlobalForwardingRule fakes forwarding rule creation.
|
||||||
|
@ -176,7 +176,7 @@ func (f *FakeLoadBalancers) GetUrlMap(name string) (*compute.UrlMap, error) {
|
||||||
return f.Um[i], nil
|
return f.Um[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Url Map %v not found", name)
|
return nil, fmt.Errorf("url map %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUrlMap fakes url-map creation.
|
// CreateUrlMap fakes url-map creation.
|
||||||
|
@ -226,7 +226,7 @@ func (f *FakeLoadBalancers) GetTargetHttpProxy(name string) (*compute.TargetHttp
|
||||||
return f.Tp[i], nil
|
return f.Tp[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Targetproxy %v not found", name)
|
return nil, fmt.Errorf("target http proxy %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTargetHttpProxy fakes creating a target http proxy.
|
// CreateTargetHttpProxy fakes creating a target http proxy.
|
||||||
|
@ -275,7 +275,7 @@ func (f *FakeLoadBalancers) GetTargetHttpsProxy(name string) (*compute.TargetHtt
|
||||||
return f.Tps[i], nil
|
return f.Tps[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Targetproxy %v not found", name)
|
return nil, fmt.Errorf("target https proxy %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTargetHttpsProxy fakes creating a target http proxy.
|
// CreateTargetHttpsProxy fakes creating a target http proxy.
|
||||||
|
@ -326,7 +326,7 @@ func (f *FakeLoadBalancers) SetSslCertificateForTargetHttpsProxy(proxy *compute.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf("Failed to find proxy %v", proxy.Name)
|
return fmt.Errorf("failed to find proxy %v", proxy.Name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ func (f *FakeLoadBalancers) GetGlobalStaticIP(name string) (*compute.Address, er
|
||||||
return f.IP[i], nil
|
return f.IP[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Static IP %v not found", name)
|
return nil, fmt.Errorf("static IP %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteGlobalStaticIP fakes out static IP deletion.
|
// DeleteGlobalStaticIP fakes out static IP deletion.
|
||||||
|
@ -441,7 +441,7 @@ func (f *FakeLoadBalancers) GetSslCertificate(name string) (*compute.SslCertific
|
||||||
return f.Certs[i], nil
|
return f.Certs[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Cert %v not found", name)
|
return nil, fmt.Errorf("cert %v not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateSslCertificate fakes out certificate creation.
|
// CreateSslCertificate fakes out certificate creation.
|
||||||
|
|
|
@ -106,7 +106,7 @@ func (l *L7s) Get(name string) (*L7, error) {
|
||||||
name = l.namer.LBName(name)
|
name = l.namer.LBName(name)
|
||||||
lb, exists := l.snapshotter.Get(name)
|
lb, exists := l.snapshotter.Get(name)
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("Loadbalancer %v not in pool", name)
|
return nil, fmt.Errorf("loadbalancer %v not in pool", name)
|
||||||
}
|
}
|
||||||
return lb.(*L7), nil
|
return lb.(*L7), nil
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ type L7 struct {
|
||||||
|
|
||||||
func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
|
func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
|
||||||
if l.glbcDefaultBackend == nil {
|
if l.glbcDefaultBackend == nil {
|
||||||
return fmt.Errorf("Cannot create urlmap without default backend.")
|
return fmt.Errorf("cannot create urlmap without default backend")
|
||||||
}
|
}
|
||||||
urlMapName := l.namer.Truncate(fmt.Sprintf("%v-%v", urlMapPrefix, l.Name))
|
urlMapName := l.namer.Truncate(fmt.Sprintf("%v-%v", urlMapPrefix, l.Name))
|
||||||
urlMap, _ := l.cloud.GetUrlMap(urlMapName)
|
urlMap, _ := l.cloud.GetUrlMap(urlMapName)
|
||||||
|
@ -313,7 +313,7 @@ func (l *L7) checkUrlMap(backend *compute.BackendService) (err error) {
|
||||||
|
|
||||||
func (l *L7) checkProxy() (err error) {
|
func (l *L7) checkProxy() (err error) {
|
||||||
if l.um == nil {
|
if l.um == nil {
|
||||||
return fmt.Errorf("Cannot create proxy without urlmap.")
|
return fmt.Errorf("cannot create proxy without urlmap")
|
||||||
}
|
}
|
||||||
proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetProxyPrefix, l.Name))
|
proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetProxyPrefix, l.Name))
|
||||||
proxy, _ := l.cloud.GetTargetHttpProxy(proxyName)
|
proxy, _ := l.cloud.GetTargetHttpProxy(proxyName)
|
||||||
|
@ -362,7 +362,7 @@ func (l *L7) checkSSLCert() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cert == nil {
|
if cert == nil {
|
||||||
return fmt.Errorf("Cannot find existing sslCertificate %v for %v", certName, l.Name)
|
return fmt.Errorf("cannot find existing sslCertificate %v for %v", certName, l.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Infof("Using existing sslCertificate %v for %v", certName, l.Name)
|
glog.Infof("Using existing sslCertificate %v for %v", certName, l.Name)
|
||||||
|
@ -429,7 +429,7 @@ func (l *L7) checkHttpsProxy() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if l.um == nil {
|
if l.um == nil {
|
||||||
return fmt.Errorf("No UrlMap for %v, will not create HTTPS proxy.", l.Name)
|
return fmt.Errorf("no UrlMap for %v, will not create HTTPS proxy", l.Name)
|
||||||
}
|
}
|
||||||
proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetHTTPSProxyPrefix, l.Name))
|
proxyName := l.namer.Truncate(fmt.Sprintf("%v-%v", targetHTTPSProxyPrefix, l.Name))
|
||||||
proxy, _ := l.cloud.GetTargetHttpsProxy(proxyName)
|
proxy, _ := l.cloud.GetTargetHttpsProxy(proxyName)
|
||||||
|
@ -535,7 +535,7 @@ func (l *L7) getEffectiveIP() (string, bool) {
|
||||||
|
|
||||||
func (l *L7) checkHttpForwardingRule() (err error) {
|
func (l *L7) checkHttpForwardingRule() (err error) {
|
||||||
if l.tp == nil {
|
if l.tp == nil {
|
||||||
return fmt.Errorf("Cannot create forwarding rule without proxy.")
|
return fmt.Errorf("cannot create forwarding rule without proxy")
|
||||||
}
|
}
|
||||||
name := l.namer.Truncate(fmt.Sprintf("%v-%v", forwardingRulePrefix, l.Name))
|
name := l.namer.Truncate(fmt.Sprintf("%v-%v", forwardingRulePrefix, l.Name))
|
||||||
address, _ := l.getEffectiveIP()
|
address, _ := l.getEffectiveIP()
|
||||||
|
@ -565,7 +565,7 @@ func (l *L7) checkHttpsForwardingRule() (err error) {
|
||||||
// checkStaticIP reserves a static IP allocated to the Forwarding Rule.
|
// checkStaticIP reserves a static IP allocated to the Forwarding Rule.
|
||||||
func (l *L7) checkStaticIP() (err error) {
|
func (l *L7) checkStaticIP() (err error) {
|
||||||
if l.fw == nil || l.fw.IPAddress == "" {
|
if l.fw == nil || l.fw.IPAddress == "" {
|
||||||
return fmt.Errorf("Will not create static IP without a forwarding rule.")
|
return fmt.Errorf("will not create static IP without a forwarding rule")
|
||||||
}
|
}
|
||||||
// Don't manage staticIPs if the user has specified an IP.
|
// Don't manage staticIPs if the user has specified an IP.
|
||||||
if address, manageStaticIP := l.getEffectiveIP(); !manageStaticIP {
|
if address, manageStaticIP := l.getEffectiveIP(); !manageStaticIP {
|
||||||
|
@ -704,7 +704,7 @@ func getNameForPathMatcher(hostRule string) string {
|
||||||
// pathmatcher of the host.
|
// pathmatcher of the host.
|
||||||
func (l *L7) UpdateUrlMap(ingressRules utils.GCEURLMap) error {
|
func (l *L7) UpdateUrlMap(ingressRules utils.GCEURLMap) error {
|
||||||
if l.um == nil {
|
if l.um == nil {
|
||||||
return fmt.Errorf("Cannot add url without an urlmap.")
|
return fmt.Errorf("cannot add url without an urlmap")
|
||||||
}
|
}
|
||||||
glog.V(3).Infof("Updating urlmap for l7 %v", l.Name)
|
glog.V(3).Infof("Updating urlmap for l7 %v", l.Name)
|
||||||
|
|
||||||
|
|
|
@ -316,7 +316,7 @@ func useDefaultOrLookupVault(cfgVault *storage.ConfigMapVault, cm_key, default_n
|
||||||
// 2. No such key in config map - found=false, err=nil
|
// 2. No such key in config map - found=false, err=nil
|
||||||
// 3. Apiserver flake - found=false, err!=nil
|
// 3. Apiserver flake - found=false, err!=nil
|
||||||
// It is not safe to proceed in 3.
|
// It is not safe to proceed in 3.
|
||||||
return "", fmt.Errorf("Failed to retrieve %v: %v, returning empty name", cm_key, err)
|
return "", fmt.Errorf("failed to retrieve %v: %v, returning empty name", cm_key, err)
|
||||||
} else if !found {
|
} else if !found {
|
||||||
// Not found but safe to proceed.
|
// Not found but safe to proceed.
|
||||||
return "", nil
|
return "", nil
|
||||||
|
|
|
@ -95,12 +95,12 @@ func (c *ConfigMapVault) Put(key, val string) error {
|
||||||
glog.Infof("Configmap %v will be updated with %v = %v", cfgMapKey, key, val)
|
glog.Infof("Configmap %v will be updated with %v = %v", cfgMapKey, key, val)
|
||||||
}
|
}
|
||||||
if err := c.ConfigMapStore.Update(apiObj); err != nil {
|
if err := c.ConfigMapStore.Update(apiObj); err != nil {
|
||||||
return fmt.Errorf("Failed to update %v: %v", cfgMapKey, err)
|
return fmt.Errorf("failed to update %v: %v", cfgMapKey, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
apiObj.Data = map[string]string{key: val}
|
apiObj.Data = map[string]string{key: val}
|
||||||
if err := c.ConfigMapStore.Add(apiObj); err != nil {
|
if err := c.ConfigMapStore.Add(apiObj); err != nil {
|
||||||
return fmt.Errorf("Failed to add %v: %v", cfgMapKey, err)
|
return fmt.Errorf("failed to add %v: %v", cfgMapKey, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glog.Infof("Successfully stored key %v = %v in config map %v", key, val, cfgMapKey)
|
glog.Infof("Successfully stored key %v = %v in config map %v", key, val, cfgMapKey)
|
||||||
|
@ -176,7 +176,7 @@ func (a *APIServerConfigMapStore) Delete(obj interface{}) error {
|
||||||
func (a *APIServerConfigMapStore) GetByKey(key string) (item interface{}, exists bool, err error) {
|
func (a *APIServerConfigMapStore) GetByKey(key string) (item interface{}, exists bool, err error) {
|
||||||
nsName := strings.Split(key, "/")
|
nsName := strings.Split(key, "/")
|
||||||
if len(nsName) != 2 {
|
if len(nsName) != 2 {
|
||||||
return nil, false, fmt.Errorf("Failed to get key %v, unexpecte format, expecting ns/name", key)
|
return nil, false, fmt.Errorf("failed to get key %v, unexpecte format, expecting ns/name", key)
|
||||||
}
|
}
|
||||||
ns, name := nsName[0], nsName[1]
|
ns, name := nsName[0], nsName[1]
|
||||||
cfg, err := a.client.Core().ConfigMaps(ns).Get(name)
|
cfg, err := a.client.Core().ConfigMaps(ns).Get(name)
|
||||||
|
|
|
@ -223,11 +223,11 @@ func (n *Namer) BePort(beName string) (string, error) {
|
||||||
}
|
}
|
||||||
match := r.FindStringSubmatch(beName)
|
match := r.FindStringSubmatch(beName)
|
||||||
if len(match) < 2 {
|
if len(match) < 2 {
|
||||||
return "", fmt.Errorf("Unable to lookup port for %v", beName)
|
return "", fmt.Errorf("unable to lookup port for %v", beName)
|
||||||
}
|
}
|
||||||
_, err = strconv.Atoi(match[1])
|
_, err = strconv.Atoi(match[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Unexpected regex match: %v", beName)
|
return "", fmt.Errorf("unexpected regex match: %v", beName)
|
||||||
}
|
}
|
||||||
return match[1], nil
|
return match[1], nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,74 @@
|
||||||
Changelog
|
Changelog
|
||||||
|
|
||||||
|
### 0.9-beta.3
|
||||||
|
|
||||||
|
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3`
|
||||||
|
|
||||||
|
*New Features:*
|
||||||
|
|
||||||
|
- Custom log formats using `log-format-upstream` directive in the configuration configmap.
|
||||||
|
- Force redirect to SSL using the annotation `ingress.kubernetes.io/force-ssl-redirect`
|
||||||
|
- Prometheus metric for VTS status module (transparent, just enable vts stats)
|
||||||
|
- Improved external authentication adding `ingress.kubernetes.io/auth-signin` annotation. Please check this [example](https://github.com/kubernetes/ingress/tree/master/examples/external-auth/nginx)
|
||||||
|
|
||||||
|
|
||||||
|
*Breaking changes:*
|
||||||
|
|
||||||
|
- `ssl-dh-param` configuration in configmap is now the name of a secret that contains the Diffie-Hellman key
|
||||||
|
|
||||||
|
*Changes:*
|
||||||
|
|
||||||
|
- [X] [#433](https://github.com/kubernetes/ingress/pull/433) close over the ingress variable or the last assignment will be used
|
||||||
|
- [X] [#424](https://github.com/kubernetes/ingress/pull/424) Manually sync secrets from certificate authentication annotations
|
||||||
|
- [X] [#423](https://github.com/kubernetes/ingress/pull/423) Scrap json metrics from nginx vts module when enabled
|
||||||
|
- [X] [#418](https://github.com/kubernetes/ingress/pull/418) Only update Ingress status for the configured class
|
||||||
|
- [X] [#415](https://github.com/kubernetes/ingress/pull/415) Improve external authentication docs
|
||||||
|
- [X] [#410](https://github.com/kubernetes/ingress/pull/410) Add support for "signin url"
|
||||||
|
- [X] [#409](https://github.com/kubernetes/ingress/pull/409) Allow custom http2 header sizes
|
||||||
|
- [X] [#408](https://github.com/kubernetes/ingress/pull/408) Review docs
|
||||||
|
- [X] [#406](https://github.com/kubernetes/ingress/pull/406) Add debug info and fix spelling
|
||||||
|
- [X] [#402](https://github.com/kubernetes/ingress/pull/402) allow specifying custom dh param
|
||||||
|
- [X] [#397](https://github.com/kubernetes/ingress/pull/397) Fix external auth
|
||||||
|
- [X] [#394](https://github.com/kubernetes/ingress/pull/394) Update README.md
|
||||||
|
- [X] [#392](https://github.com/kubernetes/ingress/pull/392) Fix http2 header size
|
||||||
|
- [X] [#391](https://github.com/kubernetes/ingress/pull/391) remove tmp nginx-diff files
|
||||||
|
- [X] [#390](https://github.com/kubernetes/ingress/pull/390) Fix RateLimit comment
|
||||||
|
- [X] [#385](https://github.com/kubernetes/ingress/pull/385) add Copyright
|
||||||
|
- [X] [#382](https://github.com/kubernetes/ingress/pull/382) Ingress Fake Certificate generation
|
||||||
|
- [X] [#380](https://github.com/kubernetes/ingress/pull/380) Fix custom log format
|
||||||
|
- [X] [#373](https://github.com/kubernetes/ingress/pull/373) Cleanup
|
||||||
|
- [X] [#371](https://github.com/kubernetes/ingress/pull/371) add configuration to disable listening on ipv6
|
||||||
|
- [X] [#370](https://github.com/kubernetes/ingress/pull/270) Add documentation for ingress.kubernetes.io/force-ssl-redirect
|
||||||
|
- [X] [#369](https://github.com/kubernetes/ingress/pull/369) Minor text fix for "ApiServer"
|
||||||
|
- [X] [#367](https://github.com/kubernetes/ingress/pull/367) BuildLogFormatUpstream was always using the default log-format
|
||||||
|
- [X] [#366](https://github.com/kubernetes/ingress/pull/366) add_judgment
|
||||||
|
- [X] [#365](https://github.com/kubernetes/ingress/pull/365) add ForceSSLRedirect ingress annotation
|
||||||
|
- [X] [#364](https://github.com/kubernetes/ingress/pull/364) Fix error caused by increasing proxy_buffer_size (#363)
|
||||||
|
- [X] [#362](https://github.com/kubernetes/ingress/pull/362) Fix ingress class
|
||||||
|
- [X] [#360](https://github.com/kubernetes/ingress/pull/360) add example of 'run multiple nginx ingress controllers as a deployment'
|
||||||
|
- [X] [#358](https://github.com/kubernetes/ingress/pull/358) Checks if the TLS secret contains a valid keypair structure
|
||||||
|
- [X] [#356](https://github.com/kubernetes/ingress/pull/356) Disable listen only on ipv6 and fix proxy_protocol
|
||||||
|
- [X] [#354](https://github.com/kubernetes/ingress/pull/354) add judgment
|
||||||
|
- [X] [#352](https://github.com/kubernetes/ingress/pull/352) Add ability to customize upstream and stream log format
|
||||||
|
- [X] [#351](https://github.com/kubernetes/ingress/pull/351) Enable custom election id for status sync.
|
||||||
|
- [X] [#347](https://github.com/kubernetes/ingress/pull/347) Fix client source IP address
|
||||||
|
- [X] [#345](https://github.com/kubernetes/ingress/pull/345) Fix lint error
|
||||||
|
- [X] [#344](https://github.com/kubernetes/ingress/pull/344) Refactoring of TCP and UDP services
|
||||||
|
- [X] [#343](https://github.com/kubernetes/ingress/pull/343) Fix node lister when --watch-namespace is used
|
||||||
|
- [X] [#341](https://github.com/kubernetes/ingress/pull/341) Do not run coverage check in the default target.
|
||||||
|
- [X] [#340](https://github.com/kubernetes/ingress/pull/340) Add support for specify proxy cookie path/domain
|
||||||
|
- [X] [#337](https://github.com/kubernetes/ingress/pull/337) Fix for formatting error introduced in #304
|
||||||
|
- [X] [#335](https://github.com/kubernetes/ingress/pull/335) Fix for vet complaints:
|
||||||
|
- [X] [#332](https://github.com/kubernetes/ingress/pull/332) Add annotation to customize nginx configuration
|
||||||
|
- [X] [#331](https://github.com/kubernetes/ingress/pull/331) Correct spelling mistake
|
||||||
|
- [X] [#328](https://github.com/kubernetes/ingress/pull/328) fix misspell "affinity" in main.go
|
||||||
|
- [X] [#326](https://github.com/kubernetes/ingress/pull/326) add nginx daemonset example
|
||||||
|
- [X] [#311](https://github.com/kubernetes/ingress/pull/311) Sort stream service ports to avoid extra reloads
|
||||||
|
- [X] [#307](https://github.com/kubernetes/ingress/pull/307) Add docs for body-size annotation
|
||||||
|
- [X] [#306](https://github.com/kubernetes/ingress/pull/306) modify nginx readme
|
||||||
|
- [X] [#304](https://github.com/kubernetes/ingress/pull/304) change 'buildSSPassthrouthUpstreams' to 'buildSSLPassthroughUpstreams'
|
||||||
|
|
||||||
|
|
||||||
### 0.9-beta.2
|
### 0.9-beta.2
|
||||||
|
|
||||||
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2`
|
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2`
|
||||||
|
|
|
@ -3,7 +3,7 @@ all: push
|
||||||
BUILDTAGS=
|
BUILDTAGS=
|
||||||
|
|
||||||
# Use the 0.0 tag for testing, it shouldn't clobber any release builds
|
# Use the 0.0 tag for testing, it shouldn't clobber any release builds
|
||||||
RELEASE?=0.9.0-beta.2
|
RELEASE?=0.9.0-beta.3
|
||||||
PREFIX?=gcr.io/google_containers/nginx-ingress-controller
|
PREFIX?=gcr.io/google_containers/nginx-ingress-controller
|
||||||
GOOS?=linux
|
GOOS?=linux
|
||||||
DOCKER?=gcloud docker --
|
DOCKER?=gcloud docker --
|
||||||
|
@ -21,7 +21,7 @@ build: clean
|
||||||
-ldflags "-s -w -X ${PKG}/pkg/version.RELEASE=${RELEASE} -X ${PKG}/pkg/version.COMMIT=${COMMIT} -X ${PKG}/pkg/version.REPO=${REPO_INFO}" \
|
-ldflags "-s -w -X ${PKG}/pkg/version.RELEASE=${RELEASE} -X ${PKG}/pkg/version.COMMIT=${COMMIT} -X ${PKG}/pkg/version.REPO=${REPO_INFO}" \
|
||||||
-o rootfs/nginx-ingress-controller ${PKG}/pkg/cmd/controller
|
-o rootfs/nginx-ingress-controller ${PKG}/pkg/cmd/controller
|
||||||
|
|
||||||
container:
|
container: build
|
||||||
$(DOCKER) build --pull -t $(PREFIX):$(RELEASE) rootfs
|
$(DOCKER) build --pull -t $(PREFIX):$(RELEASE) rootfs
|
||||||
|
|
||||||
push: container
|
push: container
|
||||||
|
@ -50,4 +50,4 @@ vet:
|
||||||
@go vet $(shell go list ${PKG}/... | grep -v vendor)
|
@go vet $(shell go list ${PKG}/... | grep -v vendor)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f nginx-ingress-controller
|
rm -f rootfs/nginx-ingress-controller
|
||||||
|
|
|
@ -40,6 +40,7 @@ The following annotations are supported:
|
||||||
|Name |type|
|
|Name |type|
|
||||||
|---------------------------|------|
|
|---------------------------|------|
|
||||||
|[ingress.kubernetes.io/add-base-url](#rewrite)|true or false|
|
|[ingress.kubernetes.io/add-base-url](#rewrite)|true or false|
|
||||||
|
|[ingress.kubernetes.io/app-root](#rewrite)|string|
|
||||||
|[ingress.kubernetes.io/affinity](#session-affinity)|true or false|
|
|[ingress.kubernetes.io/affinity](#session-affinity)|true or false|
|
||||||
|[ingress.kubernetes.io/auth-realm](#authentication)|string|
|
|[ingress.kubernetes.io/auth-realm](#authentication)|string|
|
||||||
|[ingress.kubernetes.io/auth-secret](#authentication)|string|
|
|[ingress.kubernetes.io/auth-secret](#authentication)|string|
|
||||||
|
@ -174,7 +175,9 @@ Set the annotation `ingress.kubernetes.io/rewrite-target` to the path expected b
|
||||||
|
|
||||||
If the application contains relative links it is possible to add an additional annotation `ingress.kubernetes.io/add-base-url` that will prepend a [`base` tag](https://developer.mozilla.org/en/docs/Web/HTML/Element/base) in the header of the returned HTML from the backend.
|
If the application contains relative links it is possible to add an additional annotation `ingress.kubernetes.io/add-base-url` that will prepend a [`base` tag](https://developer.mozilla.org/en/docs/Web/HTML/Element/base) in the header of the returned HTML from the backend.
|
||||||
|
|
||||||
Please check the [rewrite](examples/rewrite/README.md) example.
|
If the Application Root is exposed in a different path and needs to be redirected, the annotation `ingress.kubernetes.io/app-root` might be used.
|
||||||
|
|
||||||
|
Please check the [rewrite](/examples/rewrite/README.md) example.
|
||||||
|
|
||||||
|
|
||||||
### Rate limiting
|
### Rate limiting
|
||||||
|
|
|
@ -441,7 +441,7 @@ func (n NGINXController) Check(_ *http.Request) error {
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return fmt.Errorf("Ingress controller is not healthy")
|
return fmt.Errorf("ingress controller is not healthy")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
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`
|
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" {
|
} else if asString == "0" || asString == "false" {
|
||||||
bit = 0
|
bit = 0
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf(fmt.Sprintf("Boolean unmarshal error: invalid input %s", asString))
|
return fmt.Errorf(fmt.Sprintf("boolean unmarshal error: invalid input %s", asString))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,10 +203,16 @@ func buildLocation(input interface{}) string {
|
||||||
|
|
||||||
path := location.Path
|
path := location.Path
|
||||||
if len(location.Redirect.Target) > 0 && location.Redirect.Target != 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)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("~* ^%s", path)
|
// baseuri regex will parse basename from the given location
|
||||||
|
baseuri := `(?<baseuri>.*)`
|
||||||
|
if !strings.HasSuffix(path, slash) {
|
||||||
|
// Not treat the slash after "location path" as a part of baseuri
|
||||||
|
baseuri = fmt.Sprintf(`\/?%s`, baseuri)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`~* ^%s%s`, path, baseuri)
|
||||||
}
|
}
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
@ -294,13 +300,10 @@ func buildProxyPass(b interface{}, loc interface{}) string {
|
||||||
if len(location.Redirect.Target) > 0 {
|
if len(location.Redirect.Target) > 0 {
|
||||||
abu := ""
|
abu := ""
|
||||||
if location.Redirect.AddBaseURL {
|
if location.Redirect.AddBaseURL {
|
||||||
bPath := location.Redirect.Target
|
// path has a slash suffix, so that it can be connected with baseuri directly
|
||||||
if !strings.HasSuffix(bPath, slash) {
|
bPath := fmt.Sprintf("%s%s", path, "$baseuri")
|
||||||
bPath = fmt.Sprintf("%s/", bPath)
|
abu = fmt.Sprintf(`subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host%v">' r;
|
||||||
}
|
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host%v">' r;
|
||||||
|
|
||||||
abu = fmt.Sprintf(`subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name%v">' r;
|
|
||||||
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name%v">' r;
|
|
||||||
`, bPath, bPath)
|
`, bPath, bPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,33 +47,43 @@ var (
|
||||||
rewrite /(.*) /jenkins/$1 break;
|
rewrite /(.*) /jenkins/$1 break;
|
||||||
proxy_pass http://upstream-name;
|
proxy_pass http://upstream-name;
|
||||||
`, false},
|
`, false},
|
||||||
"redirect /something to /": {"/something", "/", "~* ^/something", `
|
"redirect /something to /": {"/something", "/", `~* ^/something\/?(?<baseuri>.*)`, `
|
||||||
rewrite /something/(.*) /$1 break;
|
rewrite /something/(.*) /$1 break;
|
||||||
rewrite /something / break;
|
rewrite /something / break;
|
||||||
proxy_pass http://upstream-name;
|
proxy_pass http://upstream-name;
|
||||||
`, false},
|
`, false},
|
||||||
"redirect /something-complex to /not-root": {"/something-complex", "/not-root", "~* ^/something-complex", `
|
"redirect /end-with-slash/ to /not-root": {"/end-with-slash/", "/not-root", "~* ^/end-with-slash/(?<baseuri>.*)", `
|
||||||
|
rewrite /end-with-slash/(.*) /not-root/$1 break;
|
||||||
|
proxy_pass http://upstream-name;
|
||||||
|
`, false},
|
||||||
|
"redirect /something-complex to /not-root": {"/something-complex", "/not-root", `~* ^/something-complex\/?(?<baseuri>.*)`, `
|
||||||
rewrite /something-complex/(.*) /not-root/$1 break;
|
rewrite /something-complex/(.*) /not-root/$1 break;
|
||||||
proxy_pass http://upstream-name;
|
proxy_pass http://upstream-name;
|
||||||
`, false},
|
`, false},
|
||||||
"redirect / to /jenkins and rewrite": {"/", "/jenkins", "~* /", `
|
"redirect / to /jenkins and rewrite": {"/", "/jenkins", "~* /", `
|
||||||
rewrite /(.*) /jenkins/$1 break;
|
rewrite /(.*) /jenkins/$1 break;
|
||||||
proxy_pass http://upstream-name;
|
proxy_pass http://upstream-name;
|
||||||
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/jenkins/">' r;
|
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/$baseuri">' r;
|
||||||
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name/jenkins/">' r;
|
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/$baseuri">' r;
|
||||||
`, true},
|
`, true},
|
||||||
"redirect /something to / and rewrite": {"/something", "/", "~* ^/something", `
|
"redirect /something to / and rewrite": {"/something", "/", `~* ^/something\/?(?<baseuri>.*)`, `
|
||||||
rewrite /something/(.*) /$1 break;
|
rewrite /something/(.*) /$1 break;
|
||||||
rewrite /something / break;
|
rewrite /something / break;
|
||||||
proxy_pass http://upstream-name;
|
proxy_pass http://upstream-name;
|
||||||
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/">' r;
|
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/something/$baseuri">' r;
|
||||||
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name/">' r;
|
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/something/$baseuri">' r;
|
||||||
`, true},
|
`, true},
|
||||||
"redirect /something-complex to /not-root and rewrite": {"/something-complex", "/not-root", "~* ^/something-complex", `
|
"redirect /end-with-slash/ to /not-root and rewrite": {"/end-with-slash/", "/not-root", `~* ^/end-with-slash/(?<baseuri>.*)`, `
|
||||||
|
rewrite /end-with-slash/(.*) /not-root/$1 break;
|
||||||
|
proxy_pass http://upstream-name;
|
||||||
|
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/end-with-slash/$baseuri">' r;
|
||||||
|
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/end-with-slash/$baseuri">' r;
|
||||||
|
`, true},
|
||||||
|
"redirect /something-complex to /not-root and rewrite": {"/something-complex", "/not-root", `~* ^/something-complex\/?(?<baseuri>.*)`, `
|
||||||
rewrite /something-complex/(.*) /not-root/$1 break;
|
rewrite /something-complex/(.*) /not-root/$1 break;
|
||||||
proxy_pass http://upstream-name;
|
proxy_pass http://upstream-name;
|
||||||
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$server_name/not-root/">' r;
|
subs_filter '<head(.*)>' '<head$1><base href="$scheme://$http_host/something-complex/$baseuri">' r;
|
||||||
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$server_name/not-root/">' r;
|
subs_filter '<HEAD(.*)>' '<HEAD$1><base href="$scheme://$http_host/something-complex/$baseuri">' r;
|
||||||
`, true},
|
`, true},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,10 +16,8 @@ FROM gcr.io/google_containers/nginx-slim:0.14
|
||||||
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
|
||||||
diffutils \
|
diffutils \
|
||||||
ssl-cert \
|
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
&& make-ssl-cert generate-default-snakeoil --force-overwrite
|
|
||||||
|
|
||||||
COPY . /
|
COPY . /
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,19 @@ http {
|
||||||
ssl_verify_client on;
|
ssl_verify_client on;
|
||||||
ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }};
|
ssl_verify_depth {{ $location.CertificateAuth.ValidationDepth }};
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if (or $location.Redirect.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect)) }}
|
||||||
|
# enforce ssl on server side
|
||||||
|
if ($pass_access_scheme = http) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if not (empty $location.Redirect.AppRoot)}}
|
||||||
|
if ($uri = /) {
|
||||||
|
return 302 {{ $location.Redirect.AppRoot }};
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
{{ if not (empty $authPath) }}
|
{{ if not (empty $authPath) }}
|
||||||
location = {{ $authPath }} {
|
location = {{ $authPath }} {
|
||||||
internal;
|
internal;
|
||||||
|
@ -282,12 +294,7 @@ http {
|
||||||
error_page 401 = {{ $location.ExternalAuth.SigninURL }};
|
error_page 401 = {{ $location.ExternalAuth.SigninURL }};
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ if (or $location.Redirect.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect)) }}
|
|
||||||
# enforce ssl on server side
|
|
||||||
if ($pass_access_scheme = http) {
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
{{ end }}
|
|
||||||
{{/* if the location contains a rate limit annotation, create one */}}
|
{{/* if the location contains a rate limit annotation, create one */}}
|
||||||
{{ $limits := buildRateLimit $location }}
|
{{ $limits := buildRateLimit $location }}
|
||||||
{{ range $limit := $limits }}
|
{{ range $limit := $limits }}
|
||||||
|
@ -493,7 +500,7 @@ stream {
|
||||||
|
|
||||||
# TCP services
|
# TCP services
|
||||||
{{ range $i, $tcpServer := .TCPBackends }}
|
{{ range $i, $tcpServer := .TCPBackends }}
|
||||||
upstream {{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} {
|
upstream tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} {
|
||||||
{{ range $j, $endpoint := $tcpServer.Endpoints }}
|
{{ range $j, $endpoint := $tcpServer.Endpoints }}
|
||||||
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
|
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -501,22 +508,22 @@ stream {
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen {{ $tcpServer.Port }};
|
listen {{ $tcpServer.Port }};
|
||||||
proxy_pass {{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }};
|
proxy_pass tcp-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }};
|
||||||
}
|
}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
# UDP services
|
# UDP services
|
||||||
{{ range $i, $udpServer := .UDPBackends }}
|
{{ range $i, $udpServer := .UDPBackends }}
|
||||||
upstream {{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} {
|
upstream udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} {
|
||||||
{{ range $j, $endpoint := $udpServer.Endpoints }}
|
{{ range $j, $endpoint := $udpServer.Endpoints }}
|
||||||
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
|
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
|
||||||
{{ end }}
|
{{ end }}
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen {{ $udpServer.Port }};
|
listen {{ $udpServer.Port }} udp;
|
||||||
proxy_responses 1;
|
proxy_responses 1;
|
||||||
proxy_pass {{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }};
|
proxy_pass udp-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }};
|
||||||
}
|
}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ const (
|
||||||
addBaseURL = "ingress.kubernetes.io/add-base-url"
|
addBaseURL = "ingress.kubernetes.io/add-base-url"
|
||||||
sslRedirect = "ingress.kubernetes.io/ssl-redirect"
|
sslRedirect = "ingress.kubernetes.io/ssl-redirect"
|
||||||
forceSSLRedirect = "ingress.kubernetes.io/force-ssl-redirect"
|
forceSSLRedirect = "ingress.kubernetes.io/force-ssl-redirect"
|
||||||
|
appRoot = "ingress.kubernetes.io/app-root"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Redirect describes the per location redirect config
|
// Redirect describes the per location redirect config
|
||||||
|
@ -41,6 +42,8 @@ type Redirect struct {
|
||||||
SSLRedirect bool `json:"sslRedirect"`
|
SSLRedirect bool `json:"sslRedirect"`
|
||||||
// ForceSSLRedirect indicates if the location section is accessible SSL only
|
// ForceSSLRedirect indicates if the location section is accessible SSL only
|
||||||
ForceSSLRedirect bool `json:"forceSSLRedirect"`
|
ForceSSLRedirect bool `json:"forceSSLRedirect"`
|
||||||
|
// AppRoot defines the Application Root that the Controller must redirect if it's not in '/' context
|
||||||
|
AppRoot string `json:"appRoot"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rewrite struct {
|
type rewrite struct {
|
||||||
|
@ -65,10 +68,12 @@ func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
fSslRe = a.backendResolver.GetDefaultBackend().ForceSSLRedirect
|
fSslRe = a.backendResolver.GetDefaultBackend().ForceSSLRedirect
|
||||||
}
|
}
|
||||||
abu, _ := parser.GetBoolAnnotation(addBaseURL, ing)
|
abu, _ := parser.GetBoolAnnotation(addBaseURL, ing)
|
||||||
|
ar, _ := parser.GetStringAnnotation(appRoot, ing)
|
||||||
return &Redirect{
|
return &Redirect{
|
||||||
Target: rt,
|
Target: rt,
|
||||||
AddBaseURL: abu,
|
AddBaseURL: abu,
|
||||||
SSLRedirect: sslRe,
|
SSLRedirect: sslRe,
|
||||||
ForceSSLRedirect: fSslRe,
|
ForceSSLRedirect: fSslRe,
|
||||||
|
AppRoot: ar,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,3 +158,20 @@ func TestForceSSLRedirect(t *testing.T) {
|
||||||
t.Errorf("Expected true but returned false")
|
t.Errorf("Expected true but returned false")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestAppRoot(t *testing.T) {
|
||||||
|
ing := buildIngress()
|
||||||
|
|
||||||
|
data := map[string]string{}
|
||||||
|
data[appRoot] = "/app1"
|
||||||
|
ing.SetAnnotations(data)
|
||||||
|
|
||||||
|
i, _ := NewParser(mockBackend{true}).Parse(ing)
|
||||||
|
redirect, ok := i.(*Redirect)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("expected a App Context")
|
||||||
|
}
|
||||||
|
if redirect.AppRoot != "/app1" {
|
||||||
|
t.Errorf("Unexpected value got in AppRoot")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,11 @@ limitations under the License.
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
|
||||||
"k8s.io/ingress/core/pkg/ingress/annotations/auth"
|
"k8s.io/ingress/core/pkg/ingress/annotations/auth"
|
||||||
|
@ -47,11 +50,13 @@ type extractorConfig interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type annotationExtractor struct {
|
type annotationExtractor struct {
|
||||||
annotations map[string]parser.IngressAnnotation
|
secretResolver resolver.Secret
|
||||||
|
annotations map[string]parser.IngressAnnotation
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
||||||
return annotationExtractor{
|
return annotationExtractor{
|
||||||
|
cfg,
|
||||||
map[string]parser.IngressAnnotation{
|
map[string]parser.IngressAnnotation{
|
||||||
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||||
"ExternalAuth": authreq.NewParser(),
|
"ExternalAuth": authreq.NewParser(),
|
||||||
|
@ -104,6 +109,7 @@ const (
|
||||||
healthCheck = "HealthCheck"
|
healthCheck = "HealthCheck"
|
||||||
sslPassthrough = "SSLPassthrough"
|
sslPassthrough = "SSLPassthrough"
|
||||||
sessionAffinity = "SessionAffinity"
|
sessionAffinity = "SessionAffinity"
|
||||||
|
certificateAuth = "CertificateAuth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *annotationExtractor) SecureUpstream(ing *extensions.Ingress) bool {
|
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)
|
val, _ := e.annotations[sessionAffinity].Parse(ing)
|
||||||
return val.(*sessionaffinity.AffinityConfig)
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -68,13 +69,18 @@ func (ic *GenericController) syncSecret(k interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create certificates and add or update the item in the store
|
// create certificates and add or update the item in the store
|
||||||
_, exists = ic.sslCertTracker.Get(key)
|
cur, exists := ic.sslCertTracker.Get(key)
|
||||||
if exists {
|
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)
|
ic.sslCertTracker.Update(key, cert)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
glog.V(3).Infof("adding secret %v/%v to the store ", sec.Namespace, sec.Name)
|
glog.Infof("adding secret %v/%v to the local store", sec.Namespace, sec.Name)
|
||||||
ic.sslCertTracker.Add(key, cert)
|
ic.sslCertTracker.Add(key, cert)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -84,10 +90,10 @@ func (ic *GenericController) syncSecret(k interface{}) error {
|
||||||
func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
|
func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
|
||||||
secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName)
|
secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName)
|
||||||
if err != nil {
|
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 {
|
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)
|
secret := secretInterface.(*api.Secret)
|
||||||
|
@ -100,13 +106,13 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC
|
||||||
|
|
||||||
var s *ingress.SSLCert
|
var s *ingress.SSLCert
|
||||||
if okcert && okkey {
|
if okcert && okkey {
|
||||||
glog.V(3).Infof("Found certificate and private key, configuring %v as a TLS Secret", secretName)
|
glog.Infof("found certificate and private key, configuring %v as a TLS Secret", secretName)
|
||||||
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
|
s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
|
||||||
} else if ca != nil {
|
} else if ca != nil {
|
||||||
glog.V(3).Infof("Found only ca.crt, configuring %v as an Certificate Authentication secret", secretName)
|
glog.Infof("found only ca.crt, configuring %v as an Certificate Authentication secret", secretName)
|
||||||
s, err = ssl.AddCertAuth(nsSecName, ca)
|
s, err = ssl.AddCertAuth(nsSecName, ca)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("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 {
|
if err != nil {
|
||||||
|
@ -122,10 +128,14 @@ func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLC
|
||||||
func (ic *GenericController) secrReferenced(name, namespace string) bool {
|
func (ic *GenericController) secrReferenced(name, namespace string) bool {
|
||||||
for _, ingIf := range ic.ingLister.Store.List() {
|
for _, ingIf := range ic.ingLister.Store.List() {
|
||||||
ing := ingIf.(*extensions.Ingress)
|
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) {
|
||||||
return true
|
str, _ := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing)
|
||||||
|
if str == fmt.Sprintf("%v/%v", namespace, name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ing.Namespace != namespace {
|
if ing.Namespace != namespace {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ type GenericController struct {
|
||||||
|
|
||||||
// Configuration contains all the settings required by an Ingress controller
|
// Configuration contains all the settings required by an Ingress controller
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
Client *clientset.Clientset
|
Client clientset.Interface
|
||||||
|
|
||||||
ResyncPeriod time.Duration
|
ResyncPeriod time.Duration
|
||||||
DefaultService string
|
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.recorder.Eventf(addIng, api.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", addIng.Namespace, addIng.Name))
|
||||||
ic.syncQueue.Enqueue(obj)
|
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{}) {
|
DeleteFunc: func(obj interface{}) {
|
||||||
delIng := obj.(*extensions.Ingress)
|
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)
|
ic.syncQueue.Enqueue(cur)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -280,11 +293,11 @@ func newIngressController(config *Configuration) *GenericController {
|
||||||
&api.Endpoints{}, ic.cfg.ResyncPeriod, eventHandler)
|
&api.Endpoints{}, ic.cfg.ResyncPeriod, eventHandler)
|
||||||
|
|
||||||
ic.secrLister.Store, ic.secrController = cache.NewInformer(
|
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)
|
&api.Secret{}, ic.cfg.ResyncPeriod, secrEventHandler)
|
||||||
|
|
||||||
ic.mapLister.Store, ic.mapController = cache.NewInformer(
|
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)
|
&api.ConfigMap{}, ic.cfg.ResyncPeriod, mapEventHandler)
|
||||||
|
|
||||||
ic.svcLister.Indexer, ic.svcController = cache.NewIndexerInformer(
|
ic.svcLister.Indexer, ic.svcController = cache.NewIndexerInformer(
|
||||||
|
@ -551,7 +564,7 @@ func (ic *GenericController) getDefaultUpstream() *ingress.Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !svcExists {
|
if !svcExists {
|
||||||
glog.Warningf("service %v does not exists", svcKey)
|
glog.Warningf("service %v does not exist", svcKey)
|
||||||
upstream.Endpoints = append(upstream.Endpoints, newDefaultServer())
|
upstream.Endpoints = append(upstream.Endpoints, newDefaultServer())
|
||||||
return upstream
|
return upstream
|
||||||
}
|
}
|
||||||
|
@ -693,7 +706,7 @@ func (ic GenericController) GetAuthCertificate(secretName string) (*resolver.Aut
|
||||||
|
|
||||||
bc, exists := ic.sslCertTracker.Get(secretName)
|
bc, exists := ic.sslCertTracker.Get(secretName)
|
||||||
if !exists {
|
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)
|
cert := bc.(*ingress.SSLCert)
|
||||||
return &resolver.AuthSSLCert{
|
return &resolver.AuthSSLCert{
|
||||||
|
@ -792,7 +805,7 @@ func (ic *GenericController) serviceEndpoints(svcKey, backendPort string,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !svcExists {
|
if !svcExists {
|
||||||
err = fmt.Errorf("service %v does not exists", svcKey)
|
err = fmt.Errorf("service %v does not exist", svcKey)
|
||||||
return upstreams, err
|
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)
|
key := fmt.Sprintf("%v/%v", ing.Namespace, tlsSecretName)
|
||||||
bc, exists := ic.sslCertTracker.Get(key)
|
bc, exists := ic.sslCertTracker.Get(key)
|
||||||
if exists {
|
if exists {
|
||||||
|
@ -954,7 +973,11 @@ func (ic *GenericController) createServers(data []interface{},
|
||||||
if isHostValid(host, cert) {
|
if isHostValid(host, cert) {
|
||||||
servers[host].SSLCertificate = cert.PemFileName
|
servers[host].SSLCertificate = cert.PemFileName
|
||||||
servers[host].SSLPemChecksum = cert.PemSHA
|
servers[host].SSLPemChecksum = cert.PemSHA
|
||||||
|
} else {
|
||||||
|
glog.Warningf("ssl certificate %v does not contains a common name for host %v", key, host)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
glog.Warningf("ssl certificate \"%v\" does not exist in local store", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -978,6 +1001,11 @@ func (ic *GenericController) getEndpoints(
|
||||||
|
|
||||||
upsServers := []ingress.Endpoint{}
|
upsServers := []ingress.Endpoint{}
|
||||||
|
|
||||||
|
// avoid duplicated upstream servers when the service
|
||||||
|
// contains multiple port definitions sharing the same
|
||||||
|
// targetport.
|
||||||
|
adus := make(map[string]bool, 0)
|
||||||
|
|
||||||
for _, ss := range ep.Subsets {
|
for _, ss := range ep.Subsets {
|
||||||
for _, epPort := range ss.Ports {
|
for _, epPort := range ss.Ports {
|
||||||
|
|
||||||
|
@ -1018,6 +1046,10 @@ func (ic *GenericController) getEndpoints(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, epAddress := range ss.Addresses {
|
for _, epAddress := range ss.Addresses {
|
||||||
|
ep := fmt.Sprintf("%v:%v", epAddress.IP, targetPort)
|
||||||
|
if _, exists := adus[ep]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
ups := ingress.Endpoint{
|
ups := ingress.Endpoint{
|
||||||
Address: epAddress.IP,
|
Address: epAddress.IP,
|
||||||
Port: fmt.Sprintf("%v", targetPort),
|
Port: fmt.Sprintf("%v", targetPort),
|
||||||
|
@ -1025,6 +1057,7 @@ func (ic *GenericController) getEndpoints(
|
||||||
FailTimeout: hz.FailTimeout,
|
FailTimeout: hz.FailTimeout,
|
||||||
}
|
}
|
||||||
upsServers = append(upsServers, ups)
|
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)
|
glog.Infof("service %v validated as source of Ingress status", *publishSvc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *configMap != "" {
|
if *watchNamespace != "" {
|
||||||
_, _, err = k8s.ParseNameNS(*configMap)
|
|
||||||
|
_, err = k8s.IsValidNamespace(kubeClient, *watchNamespace)
|
||||||
|
|
||||||
if err != nil {
|
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
|
// 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
|
// to the port mapping obtained from the pod the service must be updated to reflect
|
||||||
// the current state
|
// the current state
|
||||||
func (ic *GenericController) checkSvcForUpdate(svc *api.Service) error {
|
func (ic *GenericController) checkSvcForUpdate(svc *api.Service) error {
|
||||||
// get the pods associated with the service
|
// get the pods associated with the service
|
||||||
// TODO: switch this to a watch
|
// 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(),
|
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) {
|
if len(namedPorts) > 0 && !reflect.DeepEqual(curNamedPort, namedPorts) {
|
||||||
data, _ := json.Marshal(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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting service %v/%v: %v", svc.Namespace, svc.Name, err)
|
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)
|
newSvc.ObjectMeta.Annotations[service.NamedPortAnnotation] = string(data)
|
||||||
glog.Infof("updating service %v with new named port mappings", svc.Name)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error syncing service %v/%v: %v", svc.Namespace, svc.Name, err)
|
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
|
return false
|
||||||
}
|
}
|
||||||
for _, cn := range cert.CN {
|
for _, cn := range cert.CN {
|
||||||
if matchHostnames(cn, host) {
|
if matchHostnames(cn, strings.ToLower(host)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import "net"
|
||||||
// The reason of this requirements is the annotations are generic. If some implementation do not supports
|
// The reason of this requirements is the annotations are generic. If some implementation do not supports
|
||||||
// one or more annotations it just can provides defaults
|
// one or more annotations it just can provides defaults
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
|
// AppRoot contains the AppRoot for apps that doesn't exposes its content in the 'root' context
|
||||||
|
AppRoot string `json:"app-root"`
|
||||||
|
|
||||||
// enables which HTTP codes should be passed for processing with the error_page directive
|
// enables which HTTP codes should be passed for processing with the error_page directive
|
||||||
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
|
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
|
||||||
|
|
|
@ -252,7 +252,7 @@ func (s *statusSync) updateStatus(newIPs []api.LoadBalancerIngress) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(wg *sync.WaitGroup) {
|
go func(wg *sync.WaitGroup, ing *extensions.Ingress) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
ingClient := s.Client.Extensions().Ingresses(ing.Namespace)
|
ingClient := s.Client.Extensions().Ingresses(ing.Namespace)
|
||||||
currIng, err := ingClient.Get(ing.Name)
|
currIng, err := ingClient.Get(ing.Name)
|
||||||
|
@ -274,7 +274,7 @@ func (s *statusSync) updateStatus(newIPs []api.LoadBalancerIngress) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warningf("error updating ingress rule: %v", err)
|
glog.Warningf("error updating ingress rule: %v", err)
|
||||||
}
|
}
|
||||||
}(&wg)
|
}(&wg, ing)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
|
@ -34,6 +34,30 @@ func IsValidService(kubeClient clientset.Interface, name string) (*api.Service,
|
||||||
return kubeClient.Core().Services(ns).Get(name)
|
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
|
// IsValidSecret checks if exists a secret with the specified name
|
||||||
func IsValidSecret(kubeClient clientset.Interface, name string) (*api.Secret, error) {
|
func IsValidSecret(kubeClient clientset.Interface, name string) (*api.Secret, error) {
|
||||||
ns, name, err := ParseNameNS(name)
|
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) {
|
func TestIsValidSecret(t *testing.T) {
|
||||||
fk := testclient.NewSimpleClientset(&api.Secret{
|
fk := testclient.NewSimpleClientset(&api.Secret{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
|
|
@ -43,10 +43,10 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
||||||
|
|
||||||
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
||||||
|
|
||||||
glog.V(3).Infof("Creating temp file %v for Keypair: %v", tempPemFile.Name(), pemName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err)
|
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)
|
_, err = tempPemFile.Write(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -75,13 +75,13 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
||||||
pemBlock, _ := pem.Decode(pemCerts)
|
pemBlock, _ := pem.Decode(pemCerts)
|
||||||
if pemBlock == nil {
|
if pemBlock == nil {
|
||||||
_ = os.Remove(tempPemFile.Name())
|
_ = os.Remove(tempPemFile.Name())
|
||||||
return nil, fmt.Errorf("No valid PEM formatted block found")
|
return nil, fmt.Errorf("no valid PEM formatted block found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
|
// If the file does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
|
||||||
if pemBlock.Type != "CERTIFICATE" {
|
if pemBlock.Type != "CERTIFICATE" {
|
||||||
_ = os.Remove(tempPemFile.Name())
|
_ = os.Remove(tempPemFile.Name())
|
||||||
return nil, fmt.Errorf("Certificate %v contains invalid data, and must be created with 'kubectl create secret tls'", name)
|
return nil, fmt.Errorf("certificate %v contains invalid data, and must be created with 'kubectl create secret tls'", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemCert, err := x509.ParseCertificate(pemBlock.Bytes)
|
pemCert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||||
|
@ -115,7 +115,7 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
||||||
|
|
||||||
caFile, err := os.OpenFile(pemFileName, os.O_RDWR|os.O_APPEND, 0600)
|
caFile, err := os.OpenFile(pemFileName, os.O_RDWR|os.O_APPEND, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Could not open file %v for writing additional CA chains: %v", pemFileName, err)
|
return nil, fmt.Errorf("could not open file %v for writing additional CA chains: %v", pemFileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer caFile.Close()
|
defer caFile.Close()
|
||||||
|
@ -150,11 +150,11 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) {
|
||||||
|
|
||||||
pemCABlock, _ := pem.Decode(ca)
|
pemCABlock, _ := pem.Decode(ca)
|
||||||
if pemCABlock == nil {
|
if pemCABlock == nil {
|
||||||
return nil, fmt.Errorf("No valid PEM formatted block found")
|
return nil, fmt.Errorf("no valid PEM formatted block found")
|
||||||
}
|
}
|
||||||
// If the first certificate does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
|
// If the first certificate does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
|
||||||
if pemCABlock.Type != "CERTIFICATE" {
|
if pemCABlock.Type != "CERTIFICATE" {
|
||||||
return nil, fmt.Errorf("CA File %v contains invalid data, and must be created only with PEM formated certificates", name)
|
return nil, fmt.Errorf("CA file %v contains invalid data, and must be created only with PEM formated certificates", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := x509.ParseCertificate(pemCABlock.Bytes)
|
_, err := x509.ParseCertificate(pemCABlock.Bytes)
|
||||||
|
@ -206,13 +206,13 @@ func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
|
||||||
pemBlock, _ := pem.Decode(pemCerts)
|
pemBlock, _ := pem.Decode(pemCerts)
|
||||||
if pemBlock == nil {
|
if pemBlock == nil {
|
||||||
_ = os.Remove(tempPemFile.Name())
|
_ = os.Remove(tempPemFile.Name())
|
||||||
return "", fmt.Errorf("No valid PEM formatted block found")
|
return "", fmt.Errorf("no valid PEM formatted block found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file does not start with 'BEGIN DH PARAMETERS' it's invalid and must not be used.
|
// If the file does not start with 'BEGIN DH PARAMETERS' it's invalid and must not be used.
|
||||||
if pemBlock.Type != "DH PARAMETERS" {
|
if pemBlock.Type != "DH PARAMETERS" {
|
||||||
_ = os.Remove(tempPemFile.Name())
|
_ = os.Remove(tempPemFile.Name())
|
||||||
return "", fmt.Errorf("Certificate %v contains invalid data", name)
|
return "", fmt.Errorf("certificate %v contains invalid data", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Rename(tempPemFile.Name(), pemFileName)
|
err = os.Rename(tempPemFile.Name(), pemFileName)
|
||||||
|
|
|
@ -23,7 +23,7 @@ Table of Contents
|
||||||
The Kubernetes Service is an abstraction over endpoints (pod-ip:port pairings).
|
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
|
The Ingress is an abstraction over Services. This doesn't mean all Ingress
|
||||||
controller must route *through* a Service, but rather, that routing, security
|
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
|
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,
|
controller is free to route to the DNS name of a Service, the VIP, a NodePort,
|
||||||
or directly to the Service's endpoints.
|
or directly to the Service's endpoints.
|
||||||
|
|
|
@ -72,7 +72,7 @@ In addition to this pipeline:
|
||||||
Service
|
Service
|
||||||
* Each port on the Backend Service has a matching port on the Instance Group
|
* 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
|
* 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?
|
## The Ingress controller events complain about quota, how do I increase it?
|
||||||
|
|
||||||
|
|
|
@ -255,7 +255,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: ingress-nginx
|
name: ingress-nginx
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
|
|
|
@ -28,8 +28,8 @@ Re-encrypty | terminate, apply routing rules, re-encrypt | nginx | Advanced
|
||||||
|
|
||||||
Name | Description | Platform | Complexity Level
|
Name | Description | Platform | Complexity Level
|
||||||
-----| ----------- | ---------- | ----------------
|
-----| ----------- | ---------- | ----------------
|
||||||
Daemonset | run multiple controllers in a daemonset | nginx | Intermediate
|
Daemonset | run multiple controllers in a daemonset | nginx/haproxy | Intermediate
|
||||||
Deployment | run multiple controllers as a deployment | nginx | Intermediate
|
Deployment | run multiple controllers as a deployment | nginx/haproxy | Intermediate
|
||||||
Multi-zone | bridge different zones in a single cluster | gce | Intermediate
|
Multi-zone | bridge different zones in a single cluster | gce | Intermediate
|
||||||
Static-ip | a single ingress gets a single static ip | * | Intermediate
|
Static-ip | a single ingress gets a single static ip | * | Intermediate
|
||||||
Geo-routing | route to geographically closest endpoint | nginx | Advanced
|
Geo-routing | route to geographically closest endpoint | nginx | Advanced
|
||||||
|
|
|
@ -19,7 +19,7 @@ spec:
|
||||||
# hostNetwork: true
|
# hostNetwork: true
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-controller
|
name: nginx-ingress-controller
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
|
|
|
@ -19,7 +19,7 @@ spec:
|
||||||
# hostNetwork: true
|
# hostNetwork: true
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-controller
|
name: nginx-ingress-controller
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
|
|
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:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
|
annotations:
|
||||||
|
prometheus.io/port: '10254'
|
||||||
|
prometheus.io/scrape: 'true'
|
||||||
spec:
|
spec:
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -71,7 +71,7 @@ spec:
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-controller
|
name: nginx-ingress-controller
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -11,6 +11,9 @@ spec:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
k8s-app: nginx-ingress-controller
|
k8s-app: nginx-ingress-controller
|
||||||
|
annotations:
|
||||||
|
prometheus.io/port: '10254'
|
||||||
|
prometheus.io/scrape: 'true'
|
||||||
spec:
|
spec:
|
||||||
# hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
|
# 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
|
# 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
|
# hostNetwork: true
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-controller
|
name: nginx-ingress-controller
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -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:
|
spec:
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-controller
|
name: nginx-ingress-controller
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -18,7 +18,7 @@ spec:
|
||||||
# hostNetwork: true
|
# hostNetwork: true
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
|
||||||
name: nginx-ingress-controller
|
name: nginx-ingress-controller
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -234,7 +234,7 @@ func getDeployer() (deployer, error) {
|
||||||
case "bash":
|
case "bash":
|
||||||
return bash{}, nil
|
return bash{}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Unknown deployment strategy %q", *deployment)
|
return nil, fmt.Errorf("unknown deployment strategy %q", *deployment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue