diff --git a/cmd/nginx/flags.go b/cmd/nginx/flags.go index e58230cc0..b2446f821 100644 --- a/cmd/nginx/flags.go +++ b/cmd/nginx/flags.go @@ -27,15 +27,12 @@ import ( apiv1 "k8s.io/api/core/v1" + "k8s.io/ingress-nginx/internal/ingress/annotations/class" "k8s.io/ingress-nginx/internal/ingress/controller" ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config" ing_net "k8s.io/ingress-nginx/internal/net" ) -const ( - defIngressClass = "nginx" -) - func parseFlags() (bool, *controller.Configuration, error) { var ( flags = pflag.NewFlagSet("", pflag.ExitOnError) @@ -152,9 +149,11 @@ func parseFlags() (bool, *controller.Configuration, error) { if *ingressClass != "" { glog.Infof("Watching for ingress class: %s", *ingressClass) - if *ingressClass != defIngressClass { + if *ingressClass != class.DefaultClass { glog.Warningf("only Ingress with class \"%v\" will be processed by this ingress controller", *ingressClass) } + + class.IngressClass = *ingressClass } // check port collisions @@ -198,7 +197,6 @@ func parseFlags() (bool, *controller.Configuration, error) { EnableSSLChainCompletion: *enableSSLChainCompletion, ResyncPeriod: *resyncPeriod, DefaultService: *defaultSvc, - IngressClass: *ingressClass, Namespace: *watchNamespace, ConfigMapName: *configMap, TCPConfigMapName: *tcpConfigMapName, diff --git a/cmd/nginx/main.go b/cmd/nginx/main.go index caab12aca..a34bfb51c 100644 --- a/cmd/nginx/main.go +++ b/cmd/nginx/main.go @@ -39,7 +39,7 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/ingress-nginx/internal/ingress" + "k8s.io/ingress-nginx/internal/file" "k8s.io/ingress-nginx/internal/ingress/controller" "k8s.io/ingress-nginx/internal/k8s" "k8s.io/ingress-nginx/internal/net/ssl" @@ -58,6 +58,11 @@ func main() { glog.Fatal(err) } + fs, err := file.NewLocalFS() + if err != nil { + glog.Fatal(err) + } + kubeClient, err := createApiserverClient(conf.APIServerHost, conf.KubeConfigFile) if err != nil { handleFatalInitError(err) @@ -111,15 +116,9 @@ func main() { glog.Fatalf("resync period (%vs) is too low", conf.ResyncPeriod.Seconds()) } - // create directory that will contains the SSL Certificates - err = os.MkdirAll(ingress.DefaultSSLDirectory, 0655) - if err != nil { - glog.Errorf("Failed to mkdir SSL directory: %v", err) - } - // create the default SSL certificate (dummy) defCert, defKey := ssl.GetFakeSSLCert() - c, err := ssl.AddOrUpdateCertAndKey(fakeCertificate, defCert, defKey, []byte{}) + c, err := ssl.AddOrUpdateCertAndKey(fakeCertificate, defCert, defKey, []byte{}, fs) if err != nil { glog.Fatalf("Error generating self signed certificate: %v", err) } @@ -128,15 +127,16 @@ func main() { conf.FakeCertificateSHA = c.PemSHA conf.Client = kubeClient - conf.DefaultIngressClass = defIngressClass - ngx := controller.NewNGINXController(conf) + ngx := controller.NewNGINXController(conf, fs) if conf.EnableSSLPassthrough { setupSSLProxy(conf.ListenPorts.HTTPS, conf.ListenPorts.SSLProxy, ngx) } - go handleSigterm(ngx) + go handleSigterm(ngx, func(code int) { + os.Exit(code) + }) mux := http.NewServeMux() go registerHandlers(conf.EnableProfiling, conf.ListenPorts.Health, ngx, mux) @@ -144,7 +144,9 @@ func main() { ngx.Start() } -func handleSigterm(ngx *controller.NGINXController) { +type exiter func(code int) + +func handleSigterm(ngx *controller.NGINXController, exit exiter) { signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGTERM) <-signalChan @@ -160,7 +162,7 @@ func handleSigterm(ngx *controller.NGINXController) { time.Sleep(10 * time.Second) glog.Infof("Exiting with %v", exitCode) - os.Exit(exitCode) + exit(exitCode) } func setupSSLProxy(sslPort, proxyPort int, n *controller.NGINXController) { diff --git a/cmd/nginx/main_test.go b/cmd/nginx/main_test.go new file mode 100644 index 000000000..752e65e68 --- /dev/null +++ b/cmd/nginx/main_test.go @@ -0,0 +1,99 @@ +/* +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 main + +import ( + "fmt" + "os" + "syscall" + "testing" + "time" + + "k8s.io/ingress-nginx/internal/file" + "k8s.io/ingress-nginx/internal/ingress/controller" +) + +func TestCreateApiserverClient(t *testing.T) { + home := os.Getenv("HOME") + kubeConfigFile := fmt.Sprintf("%v/.kube/config", home) + + cli, err := createApiserverClient("", kubeConfigFile) + if err != nil { + t.Fatalf("unexpected error creating api server client: %v", err) + } + if cli == nil { + t.Fatalf("expected a kubernetes client but none returned") + } + + _, err = createApiserverClient("", "") + if err == nil { + t.Fatalf("expected an error creating api server client without an api server URL or kubeconfig file") + } +} + +func TestHandleSigterm(t *testing.T) { + home := os.Getenv("HOME") + kubeConfigFile := fmt.Sprintf("%v/.kube/config", home) + + cli, err := createApiserverClient("", kubeConfigFile) + if err != nil { + t.Fatalf("unexpected error creating api server client: %v", err) + } + + resetForTesting(func() { t.Fatal("bad parse") }) + + os.Setenv("POD_NAME", "test") + os.Setenv("POD_NAMESPACE", "test") + defer os.Setenv("POD_NAME", "") + defer os.Setenv("POD_NAMESPACE", "") + + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = []string{"cmd", "--default-backend-service", "ingress-nginx/default-backend-http", "--http-port", "0", "--https-port", "0"} + + _, conf, err := parseFlags() + if err != nil { + t.Errorf("unexpected error creating NGINX controller: %v", err) + } + conf.Client = cli + + fs, err := file.NewFakeFS() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + ngx := controller.NewNGINXController(conf, fs) + + go handleSigterm(ngx, func(code int) { + if code != 1 { + t.Errorf("expected exit code 1 but %v received", code) + } + + return + }) + + time.Sleep(1 * time.Second) + + t.Logf("sending SIGTERM to process PID %v", syscall.Getpid()) + err = syscall.Kill(syscall.Getpid(), syscall.SIGTERM) + if err != nil { + t.Errorf("unexpected error sending SIGTERM signal") + } +} + +func TestRegisterHandlers(t *testing.T) { +} diff --git a/internal/ingress/annotations/class/main.go b/internal/ingress/annotations/class/main.go index 33c7c7629..812c63d91 100644 --- a/internal/ingress/annotations/class/main.go +++ b/internal/ingress/annotations/class/main.go @@ -28,10 +28,20 @@ const ( IngressKey = "kubernetes.io/ingress.class" ) +var ( + // DefaultClass defines the default class used in the nginx ingres controller + DefaultClass = "nginx" + + // IngressClass sets the runtime ingress class to use + // An empty string means accept all ingresses without + // annotation and the ones configured with class nginx + IngressClass = "" +) + // IsValid returns true if the given Ingress either doesn't specify // the ingress.class annotation, or it's set to the configured in the // ingress controller. -func IsValid(ing *extensions.Ingress, controller, defClass string) bool { +func IsValid(ing *extensions.Ingress) bool { ingress, ok := ing.GetAnnotations()[IngressKey] if !ok { glog.V(3).Infof("annotation %v is not present in ingress %v/%v", IngressKey, ing.Namespace, ing.Name) @@ -44,9 +54,9 @@ func IsValid(ing *extensions.Ingress, controller, defClass string) bool { // and 2 invalid combinations // 3 - ingress with default class | fixed annotation on ingress // 4 - ingress with specific class | different annotation on ingress - if ingress == "" && controller == defClass { + if ingress == "" && IngressClass == DefaultClass { return true } - return ingress == controller + return ingress == IngressClass } diff --git a/internal/ingress/annotations/class/main_test.go b/internal/ingress/annotations/class/main_test.go index 45f7c02e7..bb8d2db65 100644 --- a/internal/ingress/annotations/class/main_test.go +++ b/internal/ingress/annotations/class/main_test.go @@ -25,6 +25,14 @@ import ( ) func TestIsValidClass(t *testing.T) { + dc := DefaultClass + ic := IngressClass + // restore original values after the tests + defer func() { + DefaultClass = dc + IngressClass = ic + }() + tests := []struct { ingress string controller string @@ -51,7 +59,11 @@ func TestIsValidClass(t *testing.T) { ing.SetAnnotations(data) for _, test := range tests { ing.Annotations[IngressKey] = test.ingress - b := IsValid(ing, test.controller, test.defClass) + + IngressClass = test.controller + DefaultClass = test.defClass + + b := IsValid(ing) if b != test.isValid { t.Errorf("test %v - expected %v but %v was returned", test, test.isValid, b) } diff --git a/internal/ingress/controller/backend_ssl.go b/internal/ingress/controller/backend_ssl.go index a4f3f138f..9f95bc911 100644 --- a/internal/ingress/controller/backend_ssl.go +++ b/internal/ingress/controller/backend_ssl.go @@ -172,7 +172,7 @@ func (ic *NGINXController) checkMissingSecrets() { for _, obj := range ic.listers.Ingress.List() { ing := obj.(*extensions.Ingress) - if !class.IsValid(ing, ic.cfg.IngressClass, ic.cfg.DefaultIngressClass) { + if !class.IsValid(ing) { continue } diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index 9da69ad59..875ffbedc 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -173,7 +173,7 @@ func (n *NGINXController) syncIngress(item interface{}) error { var ingresses []*extensions.Ingress for _, ingIf := range ings { ing := ingIf.(*extensions.Ingress) - if !class.IsValid(ing, n.cfg.IngressClass, n.cfg.DefaultIngressClass) { + if !class.IsValid(ing) { continue } diff --git a/internal/ingress/controller/listers.go b/internal/ingress/controller/listers.go index adfa71982..a225b7b3e 100644 --- a/internal/ingress/controller/listers.go +++ b/internal/ingress/controller/listers.go @@ -65,7 +65,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLis ingEventHandler := cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { addIng := obj.(*extensions.Ingress) - if !class.IsValid(addIng, n.cfg.IngressClass, defIngressClass) { + if !class.IsValid(addIng) { a, _ := parser.GetStringAnnotation(class.IngressKey, addIng, n) glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", addIng.Name, class.IngressKey, a) return @@ -90,7 +90,7 @@ func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLis return } } - if !class.IsValid(delIng, n.cfg.IngressClass, defIngressClass) { + if !class.IsValid(delIng) { glog.Infof("ignoring delete for ingress %v based on annotation %v", delIng.Name, class.IngressKey) return } @@ -101,8 +101,8 @@ func (n *NGINXController) createListers(stopCh chan struct{}) (*ingress.StoreLis UpdateFunc: func(old, cur interface{}) { oldIng := old.(*extensions.Ingress) curIng := cur.(*extensions.Ingress) - validOld := class.IsValid(oldIng, n.cfg.IngressClass, defIngressClass) - validCur := class.IsValid(curIng, n.cfg.IngressClass, defIngressClass) + validOld := class.IsValid(oldIng) + validCur := class.IsValid(curIng) if !validOld && validCur { glog.Infof("creating ingress %v based on annotation %v", curIng.Name, class.IngressKey) n.recorder.Eventf(curIng, apiv1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name)) diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go index b74c05cb0..b413ac5c3 100644 --- a/internal/ingress/controller/nginx.go +++ b/internal/ingress/controller/nginx.go @@ -69,10 +69,9 @@ const ( ) var ( - tmplPath = "/etc/nginx/template/nginx.tmpl" - cfgPath = "/etc/nginx/nginx.conf" - nginxBinary = "/usr/sbin/nginx" - defIngressClass = "nginx" + tmplPath = "/etc/nginx/template/nginx.tmpl" + cfgPath = "/etc/nginx/nginx.conf" + nginxBinary = "/usr/sbin/nginx" ) // NewNGINXController creates a new NGINX Ingress controller. @@ -246,7 +245,7 @@ func (n *NGINXController) Start() { for _, obj := range n.listers.Ingress.List() { ing := obj.(*extensions.Ingress) - if !class.IsValid(ing, n.cfg.IngressClass, n.cfg.DefaultIngressClass) { + if !class.IsValid(ing) { a, _ := parser.GetStringAnnotation(class.IngressKey, ing, n) glog.Infof("ignoring add for ingress %v based on annotation %v with value %v", ing.Name, class.IngressKey, a) continue diff --git a/internal/ingress/status/status.go b/internal/ingress/status/status.go index 8c18f9c7c..699b0a339 100644 --- a/internal/ingress/status/status.go +++ b/internal/ingress/status/status.go @@ -314,7 +314,7 @@ func (s *statusSync) updateStatus(newIngressPoint []apiv1.LoadBalancerIngress) { for _, cur := range ings { ing := cur.(*extensions.Ingress) - if !class.IsValid(ing, s.Config.IngressClass, s.Config.DefaultIngressClass) { + if !class.IsValid(ing) { continue }