Merge pull request #681 from aledbf/add-lb-status
Update Ingress status information in nginx controller
This commit is contained in:
commit
f22e6d2690
15 changed files with 269 additions and 21 deletions
|
@ -230,4 +230,4 @@ The previous behavior can be restored using `retry-non-idempotent=true` in the c
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
TODO
|
- Ingress rules for TLS require the definition of the field `host`
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/client/cache"
|
"k8s.io/kubernetes/pkg/client/cache"
|
||||||
|
"k8s.io/kubernetes/pkg/client/record"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/controller/framework"
|
"k8s.io/kubernetes/pkg/controller/framework"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
@ -64,8 +65,14 @@ type loadBalancerController struct {
|
||||||
tcpConfigMap string
|
tcpConfigMap string
|
||||||
udpConfigMap string
|
udpConfigMap string
|
||||||
|
|
||||||
|
recorder record.EventRecorder
|
||||||
|
|
||||||
syncQueue *taskQueue
|
syncQueue *taskQueue
|
||||||
|
|
||||||
|
// taskQueue used to update the status of the Ingress rules.
|
||||||
|
// this avoids a sync execution in the ResourceEventHandlerFuncs
|
||||||
|
ingQueue *taskQueue
|
||||||
|
|
||||||
// stopLock is used to enforce only a single call to Stop is active.
|
// stopLock is used to enforce only a single call to Stop is active.
|
||||||
// Needed because we allow stopping through an http endpoint and
|
// Needed because we allow stopping through an http endpoint and
|
||||||
// allowing concurrent stoppers leads to stack traces.
|
// allowing concurrent stoppers leads to stack traces.
|
||||||
|
@ -77,6 +84,11 @@ type loadBalancerController struct {
|
||||||
// newLoadBalancerController creates a controller for nginx loadbalancer
|
// newLoadBalancerController creates a controller for nginx loadbalancer
|
||||||
func newLoadBalancerController(kubeClient *client.Client, resyncPeriod time.Duration, defaultSvc,
|
func newLoadBalancerController(kubeClient *client.Client, resyncPeriod time.Duration, defaultSvc,
|
||||||
namespace, nxgConfigMapName, tcpConfigMapName, udpConfigMapName string, lbRuntimeInfo *lbInfo) (*loadBalancerController, error) {
|
namespace, nxgConfigMapName, tcpConfigMapName, udpConfigMapName string, lbRuntimeInfo *lbInfo) (*loadBalancerController, error) {
|
||||||
|
|
||||||
|
eventBroadcaster := record.NewBroadcaster()
|
||||||
|
eventBroadcaster.StartLogging(glog.Infof)
|
||||||
|
eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))
|
||||||
|
|
||||||
lbc := loadBalancerController{
|
lbc := loadBalancerController{
|
||||||
client: kubeClient,
|
client: kubeClient,
|
||||||
stopCh: make(chan struct{}),
|
stopCh: make(chan struct{}),
|
||||||
|
@ -86,9 +98,33 @@ func newLoadBalancerController(kubeClient *client.Client, resyncPeriod time.Dura
|
||||||
tcpConfigMap: tcpConfigMapName,
|
tcpConfigMap: tcpConfigMapName,
|
||||||
udpConfigMap: udpConfigMapName,
|
udpConfigMap: udpConfigMapName,
|
||||||
defaultSvc: defaultSvc,
|
defaultSvc: defaultSvc,
|
||||||
|
recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "loadbalancer-controller"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
lbc.syncQueue = NewTaskQueue(lbc.sync)
|
lbc.syncQueue = NewTaskQueue(lbc.sync)
|
||||||
|
lbc.ingQueue = NewTaskQueue(lbc.updateIngressStatus)
|
||||||
|
|
||||||
|
ingEventHandler := framework.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: func(obj interface{}) {
|
||||||
|
addIng := obj.(*extensions.Ingress)
|
||||||
|
lbc.recorder.Eventf(addIng, api.EventTypeNormal, "CREATE", fmt.Sprintf("%s/%s", addIng.Namespace, addIng.Name))
|
||||||
|
lbc.ingQueue.enqueue(obj)
|
||||||
|
lbc.syncQueue.enqueue(obj)
|
||||||
|
},
|
||||||
|
DeleteFunc: func(obj interface{}) {
|
||||||
|
upIng := obj.(*extensions.Ingress)
|
||||||
|
lbc.recorder.Eventf(upIng, api.EventTypeNormal, "DELETE", fmt.Sprintf("%s/%s", upIng.Namespace, upIng.Name))
|
||||||
|
lbc.syncQueue.enqueue(obj)
|
||||||
|
},
|
||||||
|
UpdateFunc: func(old, cur interface{}) {
|
||||||
|
if !reflect.DeepEqual(old, cur) {
|
||||||
|
upIng := cur.(*extensions.Ingress)
|
||||||
|
lbc.recorder.Eventf(upIng, api.EventTypeNormal, "UPDATE", fmt.Sprintf("%s/%s", upIng.Namespace, upIng.Name))
|
||||||
|
lbc.ingQueue.enqueue(cur)
|
||||||
|
lbc.syncQueue.enqueue(cur)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
eventHandler := framework.ResourceEventHandlerFuncs{
|
eventHandler := framework.ResourceEventHandlerFuncs{
|
||||||
AddFunc: func(obj interface{}) {
|
AddFunc: func(obj interface{}) {
|
||||||
|
@ -109,7 +145,7 @@ func newLoadBalancerController(kubeClient *client.Client, resyncPeriod time.Dura
|
||||||
ListFunc: ingressListFunc(lbc.client, namespace),
|
ListFunc: ingressListFunc(lbc.client, namespace),
|
||||||
WatchFunc: ingressWatchFunc(lbc.client, namespace),
|
WatchFunc: ingressWatchFunc(lbc.client, namespace),
|
||||||
},
|
},
|
||||||
&extensions.Ingress{}, resyncPeriod, eventHandler)
|
&extensions.Ingress{}, resyncPeriod, ingEventHandler)
|
||||||
|
|
||||||
lbc.endpLister.Store, lbc.endpController = framework.NewInformer(
|
lbc.endpLister.Store, lbc.endpController = framework.NewInformer(
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
|
@ -206,6 +242,57 @@ func (lbc *loadBalancerController) sync(key string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lbc *loadBalancerController) updateIngressStatus(key string) {
|
||||||
|
if !lbc.controllersInSync() {
|
||||||
|
lbc.ingQueue.requeue(key, fmt.Errorf("deferring sync till endpoints controller has synced"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, ingExists, err := lbc.ingLister.Store.GetByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
lbc.ingQueue.requeue(key, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ingExists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ing := obj.(*extensions.Ingress)
|
||||||
|
|
||||||
|
ingClient := lbc.client.Extensions().Ingress(ing.Namespace)
|
||||||
|
|
||||||
|
currIng, err := ingClient.Get(ing.Name)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error searching Ingress %v/%v: %v", ing.Namespace, ing.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lbIPs := ing.Status.LoadBalancer.Ingress
|
||||||
|
if !lbc.isStatusIPDefined(lbIPs) {
|
||||||
|
glog.Infof("Updating loadbalancer %v/%v with IP %v", ing.Namespace, ing.Name, lbc.lbInfo.Address)
|
||||||
|
currIng.Status.LoadBalancer.Ingress = append(currIng.Status.LoadBalancer.Ingress, api.LoadBalancerIngress{
|
||||||
|
IP: lbc.lbInfo.Address,
|
||||||
|
})
|
||||||
|
if _, err := ingClient.UpdateStatus(currIng); err != nil {
|
||||||
|
lbc.recorder.Eventf(currIng, api.EventTypeWarning, "UPDATE", "error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lbc.recorder.Eventf(currIng, api.EventTypeNormal, "CREATE", "ip: %v", lbc.lbInfo.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lbc *loadBalancerController) isStatusIPDefined(lbings []api.LoadBalancerIngress) bool {
|
||||||
|
for _, lbing := range lbings {
|
||||||
|
if lbing.IP == lbc.lbInfo.Address {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (lbc *loadBalancerController) getTCPServices() []*nginx.Location {
|
func (lbc *loadBalancerController) getTCPServices() []*nginx.Location {
|
||||||
if lbc.tcpConfigMap == "" {
|
if lbc.tcpConfigMap == "" {
|
||||||
// no configmap for TCP services
|
// no configmap for TCP services
|
||||||
|
@ -505,10 +592,14 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pemFileName := lbc.nginx.AddOrUpdateCertAndKey(secretName, string(cert), string(key))
|
pemFileName, err := lbc.nginx.AddOrUpdateCertAndKey(fmt.Sprintf("%v-%v", ing.Namespace, secretName), string(cert), string(key))
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
cn, err := lbc.nginx.CheckSSLCertificate(pemFileName)
|
cn, err := lbc.nginx.CheckSSLCertificate(pemFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warningf("No valid SSL certificate found in secret %v", secretName)
|
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,17 +662,59 @@ func (lbc *loadBalancerController) getEndpoints(s *api.Service, servicePort ints
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the loadbalancer controller.
|
// Stop stops the loadbalancer controller.
|
||||||
func (lbc *loadBalancerController) Stop() {
|
func (lbc *loadBalancerController) Stop() error {
|
||||||
// Stop is invoked from the http endpoint.
|
// Stop is invoked from the http endpoint.
|
||||||
lbc.stopLock.Lock()
|
lbc.stopLock.Lock()
|
||||||
defer lbc.stopLock.Unlock()
|
defer lbc.stopLock.Unlock()
|
||||||
|
|
||||||
// Only try draining the workqueue if we haven't already.
|
// Only try draining the workqueue if we haven't already.
|
||||||
if !lbc.shutdown {
|
if !lbc.shutdown {
|
||||||
|
|
||||||
|
lbc.removeFromIngress()
|
||||||
|
|
||||||
close(lbc.stopCh)
|
close(lbc.stopCh)
|
||||||
glog.Infof("shutting down controller queues")
|
glog.Infof("shutting down controller queues")
|
||||||
lbc.shutdown = true
|
lbc.shutdown = true
|
||||||
lbc.syncQueue.shutdown()
|
lbc.syncQueue.shutdown()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("shutdown already in progress")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lbc *loadBalancerController) removeFromIngress() {
|
||||||
|
ings := lbc.ingLister.Store.List()
|
||||||
|
glog.Infof("updating %v Ingress rule/s", len(ings))
|
||||||
|
for _, cur := range ings {
|
||||||
|
ing := cur.(*extensions.Ingress)
|
||||||
|
|
||||||
|
ingClient := lbc.client.Extensions().Ingress(ing.Namespace)
|
||||||
|
currIng, err := ingClient.Get(ing.Name)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error searching Ingress %v/%v: %v", ing.Namespace, ing.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lbIPs := ing.Status.LoadBalancer.Ingress
|
||||||
|
if len(lbIPs) > 0 && lbc.isStatusIPDefined(lbIPs) {
|
||||||
|
glog.Infof("Updating loadbalancer %v/%v. Removing IP %v", ing.Namespace, ing.Name, lbc.lbInfo.Address)
|
||||||
|
|
||||||
|
for idx, lbStatus := range currIng.Status.LoadBalancer.Ingress {
|
||||||
|
if lbStatus.IP == lbc.lbInfo.Address {
|
||||||
|
currIng.Status.LoadBalancer.Ingress = append(currIng.Status.LoadBalancer.Ingress[:idx],
|
||||||
|
currIng.Status.LoadBalancer.Ingress[idx+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ingClient.UpdateStatus(currIng); err != nil {
|
||||||
|
lbc.recorder.Eventf(currIng, api.EventTypeWarning, "UPDATE", "error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lbc.recorder.Eventf(currIng, api.EventTypeNormal, "DELETE", "ip: %v", lbc.lbInfo.Address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,6 +728,7 @@ func (lbc *loadBalancerController) Run() {
|
||||||
go lbc.svcController.Run(lbc.stopCh)
|
go lbc.svcController.Run(lbc.stopCh)
|
||||||
|
|
||||||
go lbc.syncQueue.run(time.Second, lbc.stopCh)
|
go lbc.syncQueue.run(time.Second, lbc.stopCh)
|
||||||
|
go lbc.ingQueue.run(time.Second, lbc.stopCh)
|
||||||
|
|
||||||
<-lbc.stopCh
|
<-lbc.stopCh
|
||||||
glog.Infof("shutting down NGINX loadbalancer controller")
|
glog.Infof("shutting down NGINX loadbalancer controller")
|
||||||
|
|
|
@ -14,8 +14,9 @@ spec:
|
||||||
k8s-app: nginx-ingress-lb
|
k8s-app: nginx-ingress-lb
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.4
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -8,8 +8,9 @@ spec:
|
||||||
labels:
|
labels:
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.4
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -11,7 +11,7 @@ spec:
|
||||||
labels:
|
labels:
|
||||||
app: default-http-backend
|
app: default-http-backend
|
||||||
spec:
|
spec:
|
||||||
terminationGracePeriodSeconds: 600
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- name: default-http-backend
|
- name: default-http-backend
|
||||||
# Any image is permissable as long as:
|
# Any image is permissable as long as:
|
||||||
|
|
|
@ -14,8 +14,9 @@ spec:
|
||||||
k8s-app: nginx-ingress-lb
|
k8s-app: nginx-ingress-lb
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.4
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -15,12 +15,13 @@ spec:
|
||||||
k8s-app: nginx-ingress-lb
|
k8s-app: nginx-ingress-lb
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
volumes:
|
volumes:
|
||||||
- name: dhparam-example
|
- name: dhparam-example
|
||||||
secret:
|
secret:
|
||||||
secretName: dhparam-example
|
secretName: dhparam-example
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.4
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -14,8 +14,9 @@ spec:
|
||||||
k8s-app: nginx-ingress-lb
|
k8s-app: nginx-ingress-lb
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.4
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -14,8 +14,9 @@ spec:
|
||||||
k8s-app: nginx-ingress-lb
|
k8s-app: nginx-ingress-lb
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.4
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -14,8 +14,9 @@ spec:
|
||||||
k8s-app: nginx-ingress-lb
|
k8s-app: nginx-ingress-lb
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
spec:
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- image: aledbf/nginx-third-party:0.9
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.5
|
||||||
name: nginx-ingress-lb
|
name: nginx-ingress-lb
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -22,6 +22,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
@ -32,7 +34,6 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned"
|
"k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/healthz"
|
"k8s.io/kubernetes/pkg/healthz"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -111,6 +112,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
go registerHandlers(lbc)
|
go registerHandlers(lbc)
|
||||||
|
go handleSigterm(lbc)
|
||||||
|
|
||||||
lbc.Run()
|
lbc.Run()
|
||||||
|
|
||||||
|
@ -122,11 +124,10 @@ func main() {
|
||||||
|
|
||||||
// lbInfo contains runtime information about the pod
|
// lbInfo contains runtime information about the pod
|
||||||
type lbInfo struct {
|
type lbInfo struct {
|
||||||
ObjectName string
|
|
||||||
DeployType runtime.Object
|
|
||||||
Podname string
|
Podname string
|
||||||
PodIP string
|
PodIP string
|
||||||
PodNamespace string
|
PodNamespace string
|
||||||
|
Address string
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerHandlers(lbc *loadBalancerController) {
|
func registerHandlers(lbc *loadBalancerController) {
|
||||||
|
@ -149,3 +150,18 @@ func registerHandlers(lbc *loadBalancerController) {
|
||||||
}
|
}
|
||||||
glog.Fatal(server.ListenAndServe())
|
glog.Fatal(server.ListenAndServe())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleSigterm(lbc *loadBalancerController) {
|
||||||
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalChan, syscall.SIGTERM)
|
||||||
|
<-signalChan
|
||||||
|
glog.Infof("Received SIGTERM, shutting down")
|
||||||
|
|
||||||
|
exitCode := 0
|
||||||
|
if err := lbc.Stop(); err != nil {
|
||||||
|
glog.Infof("Error during shutdown %v", err)
|
||||||
|
exitCode = 1
|
||||||
|
}
|
||||||
|
glog.Infof("Exiting with %v", exitCode)
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
|
@ -77,7 +77,9 @@ const (
|
||||||
// Size of the SSL shared cache between all worker processes.
|
// Size of the SSL shared cache between all worker processes.
|
||||||
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache
|
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache
|
||||||
sslSessionCacheSize = "10m"
|
sslSessionCacheSize = "10m"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
// Base directory that contains the mounted secrets with SSL certificates, keys and
|
// Base directory that contains the mounted secrets with SSL certificates, keys and
|
||||||
sslDirectory = "/etc/nginx-ssl"
|
sslDirectory = "/etc/nginx-ssl"
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,21 +27,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the specified name
|
// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the specified name
|
||||||
func (nginx *Manager) AddOrUpdateCertAndKey(name string, cert string, key string) string {
|
func (nginx *Manager) AddOrUpdateCertAndKey(name string, cert string, key string) (string, error) {
|
||||||
pemFileName := sslDirectory + "/" + name + ".pem"
|
pemFileName := sslDirectory + "/" + name + ".pem"
|
||||||
|
|
||||||
pem, err := os.Create(pemFileName)
|
pem, err := os.Create(pemFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("Couldn't create pem file %v: %v", pemFileName, err)
|
return "", fmt.Errorf("Couldn't create pem file %v: %v", pemFileName, err)
|
||||||
}
|
}
|
||||||
defer pem.Close()
|
defer pem.Close()
|
||||||
|
|
||||||
_, err = pem.WriteString(fmt.Sprintf("%v\n%v", key, cert))
|
_, err = pem.WriteString(fmt.Sprintf("%v\n%v", cert, key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("Couldn't write to pem file %v: %v", pemFileName, err)
|
return "", fmt.Errorf("Couldn't write to pem file %v: %v", pemFileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pemFileName
|
return pemFileName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckSSLCertificate checks if the certificate and key file are valid
|
// CheckSSLCertificate checks if the certificate and key file are valid
|
||||||
|
|
68
controllers/nginx/nginx/ssl_test.go
Normal file
68
controllers/nginx/nginx/ssl_test.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 nginx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddOrUpdateCertAndKey(t *testing.T) {
|
||||||
|
sslDirectory = os.TempDir()
|
||||||
|
// openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=echoheaders/O=echoheaders"
|
||||||
|
tlsCrt := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhakNDQWxLZ0F3SUJBZ0lKQUxHUXR5VVBKTFhYTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ3d4RkRBU0JnTlYKQkFNVEMyVmphRzlvWldGa1pYSnpNUlF3RWdZRFZRUUtFd3RsWTJodmFHVmhaR1Z5Y3pBZUZ3MHhOakF6TXpFeQpNekU1TkRoYUZ3MHhOekF6TXpFeU16RTVORGhhTUN3eEZEQVNCZ05WQkFNVEMyVmphRzlvWldGa1pYSnpNUlF3CkVnWURWUVFLRXd0bFkyaHZhR1ZoWkdWeWN6Q0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0MKZ2dFQkFONzVmS0N5RWwxanFpMjUxTlNabDYzeGQweG5HMHZTVjdYL0xxTHJveVNraW5nbnI0NDZZWlE4UEJWOAo5TUZzdW5RRGt1QVoyZzA3NHM1YWhLSm9BRGJOMzhld053RXNsVDJkRzhRTUw0TktrTUNxL1hWbzRQMDFlWG1PCmkxR2txZFA1ZUExUHlPZCtHM3gzZmxPN2xOdmtJdHVHYXFyc0tvMEhtMHhqTDVtRUpwWUlOa0tGSVhsWWVLZS8KeHRDR25CU2tLVHFMTG0yeExKSGFFcnJpaDZRdkx4NXF5U2gzZTU2QVpEcTlkTERvcWdmVHV3Z2IzekhQekc2NwppZ0E0dkYrc2FRNHpZUE1NMHQyU1NiVkx1M2pScWNvL3lxZysrOVJBTTV4bjRubnorL0hUWFhHKzZ0RDBaeGI1CmVVRDNQakVhTnlXaUV2dTN6UFJmdysyNURMY0NBd0VBQWFPQmpqQ0JpekFkQmdOVkhRNEVGZ1FVcktMZFhHeUUKNUlEOGRvd2lZNkdzK3dNMHFKc3dYQVlEVlIwakJGVXdVNEFVcktMZFhHeUU1SUQ4ZG93aVk2R3Mrd00wcUp1aApNS1F1TUN3eEZEQVNCZ05WQkFNVEMyVmphRzlvWldGa1pYSnpNUlF3RWdZRFZRUUtFd3RsWTJodmFHVmhaR1Z5CmM0SUpBTEdRdHlVUEpMWFhNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFNZVMKMHFia3VZa3Z1enlSWmtBeE1PdUFaSDJCK0Evb3N4ODhFRHB1ckV0ZWN5RXVxdnRvMmpCSVdCZ2RkR3VBYU5jVQorUUZDRm9NakJOUDVWVUxIWVhTQ3VaczN2Y25WRDU4N3NHNlBaLzhzbXJuYUhTUjg1ZVpZVS80bmFyNUErdWErClIvMHJrSkZnOTlQSmNJd3JmcWlYOHdRcWdJVVlLNE9nWEJZcUJRL0VZS2YvdXl6UFN3UVZYRnVJTTZTeDBXcTYKTUNML3d2RlhLS0FaWDBqb3J4cHRjcldkUXNCcmYzWVRnYmx4TE1sN20zL2VuR1drcEhDUHdYeVRCOC9rRkw3SApLL2ZHTU1NWGswUkVSbGFPM1hTSUhrZUQ2SXJiRnRNV3R1RlJwZms2ZFA2TXlMOHRmTmZ6a3VvUHVEWUFaWllWCnR1NnZ0c0FRS0xWb0pGaGV0b1k9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
|
||||||
|
tlsKey := "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBM3ZsOG9MSVNYV09xTGJuVTFKbVhyZkYzVEdjYlM5Slh0Zjh1b3V1akpLU0tlQ2V2CmpqcGhsRHc4Rlh6MHdXeTZkQU9TNEJuYURUdml6bHFFb21nQU5zM2Z4N0EzQVN5VlBaMGJ4QXd2ZzBxUXdLcjkKZFdqZy9UVjVlWTZMVWFTcDAvbDREVS9JNTM0YmZIZCtVN3VVMitRaTI0WnFxdXdxalFlYlRHTXZtWVFtbGdnMgpRb1VoZVZoNHA3L0cwSWFjRktRcE9vc3ViYkVza2RvU3V1S0hwQzh2SG1ySktIZDdub0JrT3IxMHNPaXFCOU83CkNCdmZNYy9NYnJ1S0FEaThYNnhwRGpOZzh3elMzWkpKdFV1N2VOR3B5ai9LcUQ3NzFFQXpuR2ZpZWZQNzhkTmQKY2I3cTBQUm5Gdmw1UVBjK01SbzNKYUlTKzdmTTlGL0Q3YmtNdHdJREFRQUJBb0lCQUViNmFEL0hMNjFtMG45bgp6bVkyMWwvYW83MUFmU0h2dlZnRCtWYUhhQkY4QjFBa1lmQUdpWlZrYjBQdjJRSFJtTERoaWxtb0lROWhadHVGCldQOVIxKythTFlnbGdmenZzanBBenR2amZTUndFaEFpM2pnSHdNY1p4S2Q3UnNJZ2hxY2huS093S0NYNHNNczQKUnBCbEFBZlhZWGs4R3F4NkxUbGptSDRDZk42QzZHM1EwTTlLMUxBN2lsck1Na3hwcngxMnBlVTNkczZMVmNpOQptOFdBL21YZ2I0c3pEbVNaWVpYRmNZMEhYNTgyS3JKRHpQWEVJdGQwZk5wd3I0eFIybzdzMEwvK2RnZCtqWERjCkh2SDBKZ3NqODJJaTIxWGZGM2tST3FxR3BKNmhVcncxTUZzVWRyZ29GL3pFck0vNWZKMDdVNEhodGFlalVzWTIKMFJuNXdpRUNnWUVBKzVUTVRiV084Wkg5K2pIdVQwc0NhZFBYcW50WTZYdTZmYU04Tm5CZWNoeTFoWGdlQVN5agpSWERlZGFWM1c0SjU5eWxIQ3FoOVdseVh4cDVTWWtyQU41RnQ3elFGYi91YmorUFIyWWhMTWZpYlBSYlYvZW1MCm5YaGF6MmtlNUUxT1JLY0x6QUVwSmpuZGQwZlZMZjdmQzFHeStnS2YyK3hTY1hjMHJqRE5iNGtDZ1lFQTR1UVEKQk91TlJQS3FKcDZUZS9zUzZrZitHbEpjQSs3RmVOMVlxM0E2WEVZVm9ydXhnZXQ4a2E2ZEo1QjZDOWtITGtNcQpwdnFwMzkxeTN3YW5uWC9ONC9KQlU2M2RxZEcyd1BWRUQ0REduaE54Qm1oaWZpQ1I0R0c2ZnE4MUV6ZE1vcTZ4CklTNHA2RVJaQnZkb1RqNk9pTHl6aUJMckpxeUhIMWR6c0hGRlNqOENnWUVBOWlSSEgyQ2JVazU4SnVYak8wRXcKUTBvNG4xdS9TZkQ4TFNBZ01VTVBwS1hpRTR2S0Qyd1U4a1BUNDFiWXlIZUh6UUpkdDFmU0RTNjZjR0ZHU1ZUSgphNVNsOG5yN051ejg3bkwvUmMzTGhFQ3Y0YjBOOFRjbW1oSy9CbDdiRXBOd0dFczNoNGs3TVdNOEF4QU15c3VxCmZmQ1pJM0tkNVJYNk0zbGwyV2QyRjhFQ2dZQlQ5RU9oTG0vVmhWMUVjUVR0cVZlMGJQTXZWaTVLSGozZm5UZkUKS0FEUVIvYVZncElLR3RLN0xUdGxlbVpPbi8yeU5wUS91UnpHZ3pDUUtldzNzU1RFSmMzYVlzbFVudzdhazJhZAp2ZTdBYXowMU84YkdHTk1oamNmdVBIS05LN2Nsc3pKRHJzcys4SnRvb245c0JHWEZYdDJuaWlpTTVPWVN5TTg4CkNJMjFEUUtCZ0hEQVRZbE84UWlDVWFBQlVqOFBsb1BtMDhwa3cyc1VmQW0xMzJCY00wQk9BN1hqYjhtNm1ManQKOUlteU5kZ2ZiM080UjlKVUxTb1pZSTc1dUxIL3k2SDhQOVlpWHZOdzMrTXl6VFU2b2d1YU8xSTNya2pna29NeAo5cU5pYlJFeGswS1A5MVZkckVLSEdHZEFwT05ES1N4VzF3ektvbUxHdmtYSTVKV05KRXFkCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg=="
|
||||||
|
|
||||||
|
dCrt, err := base64.StdEncoding.DecodeString(tlsCrt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dKey, err := base64.StdEncoding.DecodeString(tlsKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx := &Manager{}
|
||||||
|
|
||||||
|
name := fmt.Sprintf("test-%v", time.Now().UnixNano())
|
||||||
|
pemPath, err := ngx.AddOrUpdateCertAndKey(name, string(dCrt), string(dKey))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error checking SSL certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pemPath == "" {
|
||||||
|
t.Fatalf("expected path to pem file but returned empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
cnames, err := ngx.CheckSSLCertificate(pemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error checking SSL certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cnames) == 0 {
|
||||||
|
t.Fatalf("expected at least one cname but none returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnames[0] != "echoheaders" {
|
||||||
|
t.Fatalf("expected cname echoheaders but %v returned", cnames[0])
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/client/cache"
|
"k8s.io/kubernetes/pkg/client/cache"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned"
|
"k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/util/wait"
|
"k8s.io/kubernetes/pkg/util/wait"
|
||||||
|
@ -108,10 +109,30 @@ func getLBDetails(kubeClient *unversioned.Client) (*lbInfo, error) {
|
||||||
return nil, fmt.Errorf("Unable to get POD information")
|
return nil, fmt.Errorf("Unable to get POD information")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node, err := kubeClient.Nodes().Get(pod.Spec.NodeName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var externalIP string
|
||||||
|
for _, address := range node.Status.Addresses {
|
||||||
|
if address.Type == api.NodeExternalIP {
|
||||||
|
if address.Address != "" {
|
||||||
|
externalIP = address.Address
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if externalIP == "" && address.Type == api.NodeLegacyHostIP {
|
||||||
|
externalIP = address.Address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &lbInfo{
|
return &lbInfo{
|
||||||
PodIP: podIP,
|
PodIP: podIP,
|
||||||
Podname: podName,
|
Podname: podName,
|
||||||
PodNamespace: podNs,
|
PodNamespace: podNs,
|
||||||
|
Address: externalIP,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue