2016-11-10 22:56:29 +00:00
/ *
Copyright 2015 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 (
2017-11-05 01:18:28 +00:00
"encoding/json"
"fmt"
2018-01-17 12:26:32 +00:00
"math/rand"
2017-11-05 01:18:28 +00:00
"net/http"
"net/http/pprof"
2016-11-10 22:56:29 +00:00
"os"
"os/signal"
"syscall"
"time"
2018-07-07 17:46:18 +00:00
"github.com/prometheus/client_golang/prometheus"
2017-11-05 01:18:28 +00:00
"github.com/prometheus/client_golang/prometheus/promhttp"
2018-12-07 07:42:52 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2017-11-05 01:18:28 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-02-17 20:25:04 +00:00
"k8s.io/apimachinery/pkg/util/wait"
discovery "k8s.io/apimachinery/pkg/version"
2017-11-05 01:18:28 +00:00
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
2018-12-05 16:27:55 +00:00
"k8s.io/klog"
2017-11-05 01:18:28 +00:00
2017-11-22 13:35:47 +00:00
"k8s.io/ingress-nginx/internal/file"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/ingress/controller"
2018-07-07 17:46:18 +00:00
"k8s.io/ingress-nginx/internal/ingress/metric"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/net/ssl"
2017-11-05 01:18:28 +00:00
"k8s.io/ingress-nginx/version"
2016-11-10 22:56:29 +00:00
)
2018-06-11 09:17:50 +00:00
const (
// High enough QPS to fit all expected use cases. QPS=0 is not set here, because
// client code is overriding it.
defaultQPS = 1e6
// High enough Burst to fit all expected use cases. Burst=0 is not set here, because
// client code is overriding it.
defaultBurst = 1e6
fakeCertificate = "default-fake-certificate"
)
2016-11-10 22:56:29 +00:00
func main ( ) {
2018-12-05 16:27:55 +00:00
klog . InitFlags ( nil )
2018-01-17 12:26:32 +00:00
rand . Seed ( time . Now ( ) . UnixNano ( ) )
2017-11-05 01:18:28 +00:00
fmt . Println ( version . String ( ) )
showVersion , conf , err := parseFlags ( )
if showVersion {
os . Exit ( 0 )
}
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatal ( err )
2017-11-05 01:18:28 +00:00
}
2018-05-27 23:51:07 +00:00
nginxVersion ( )
2017-11-22 13:35:47 +00:00
fs , err := file . NewLocalFS ( )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatal ( err )
2017-11-22 13:35:47 +00:00
}
2017-11-05 01:18:28 +00:00
kubeClient , err := createApiserverClient ( conf . APIServerHost , conf . KubeConfigFile )
if err != nil {
handleFatalInitError ( err )
}
2018-09-25 03:33:13 +00:00
if len ( conf . DefaultService ) > 0 {
defSvcNs , defSvcName , err := k8s . ParseNameNS ( conf . DefaultService )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatal ( err )
2018-09-25 03:33:13 +00:00
}
2017-11-05 01:18:28 +00:00
2018-09-25 03:33:13 +00:00
_ , err = kubeClient . CoreV1 ( ) . Services ( defSvcNs ) . Get ( defSvcName , metav1 . GetOptions { } )
if err != nil {
2018-12-07 07:42:52 +00:00
if errors . IsUnauthorized ( err ) || errors . IsForbidden ( err ) {
2018-12-05 16:27:55 +00:00
klog . Fatal ( "✖ The cluster seems to be running with a restrictive Authorization mode and the Ingress controller does not have the required permissions to operate normally." )
2018-09-25 03:33:13 +00:00
}
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "No service with name %v found: %v" , conf . DefaultService , err )
2017-11-05 01:18:28 +00:00
}
2018-12-05 16:27:55 +00:00
klog . Infof ( "Validated %v as the default backend." , conf . DefaultService )
2017-11-05 01:18:28 +00:00
}
if conf . Namespace != "" {
_ , err = kubeClient . CoreV1 ( ) . Namespaces ( ) . Get ( conf . Namespace , metav1 . GetOptions { } )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "No namespace with name %v found: %v" , conf . Namespace , err )
2017-11-05 01:18:28 +00:00
}
}
// create the default SSL certificate (dummy)
2017-11-18 10:18:17 +00:00
defCert , defKey := ssl . GetFakeSSLCert ( )
2018-01-18 19:14:42 +00:00
c , err := ssl . AddOrUpdateCertAndKey ( fakeCertificate , defCert , defKey , [ ] byte { } , fs )
2017-11-18 10:18:17 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Error generating self-signed certificate: %v" , err )
2017-11-18 10:18:17 +00:00
}
conf . FakeCertificatePath = c . PemFileName
conf . FakeCertificateSHA = c . PemSHA
2017-11-05 01:18:28 +00:00
conf . Client = kubeClient
2018-07-07 17:46:18 +00:00
reg := prometheus . NewRegistry ( )
2018-07-12 17:18:43 +00:00
reg . MustRegister ( prometheus . NewGoCollector ( ) )
2018-09-22 17:54:11 +00:00
reg . MustRegister ( prometheus . NewProcessCollector ( prometheus . ProcessCollectorOpts {
PidFn : func ( ) ( int , error ) { return os . Getpid ( ) , nil } ,
ReportErrors : true ,
} ) )
2018-07-12 17:18:43 +00:00
2018-12-04 19:59:54 +00:00
mc := metric . NewDummyCollector ( )
if conf . EnableMetrics {
mc , err = metric . NewCollector ( conf . ListenPorts . Status , reg )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Error creating prometheus collector: %v" , err )
2018-12-04 19:59:54 +00:00
}
2018-07-07 17:46:18 +00:00
}
mc . Start ( )
2017-11-05 01:18:28 +00:00
2018-07-07 17:46:18 +00:00
ngx := controller . NewNGINXController ( conf , mc , fs )
2017-11-22 13:35:47 +00:00
go handleSigterm ( ngx , func ( code int ) {
os . Exit ( code )
} )
2017-11-05 01:18:28 +00:00
mux := http . NewServeMux ( )
2018-07-12 17:18:43 +00:00
if conf . EnableProfiling {
registerProfiler ( mux )
}
registerHealthz ( ngx , mux )
registerMetrics ( reg , mux )
registerHandlers ( mux )
go startHTTPServer ( conf . ListenPorts . Health , mux )
2018-06-14 00:55:07 +00:00
2017-08-28 16:06:58 +00:00
ngx . Start ( )
2016-11-10 22:56:29 +00:00
}
2017-11-22 13:35:47 +00:00
type exiter func ( code int )
func handleSigterm ( ngx * controller . NGINXController , exit exiter ) {
2016-11-10 22:56:29 +00:00
signalChan := make ( chan os . Signal , 1 )
signal . Notify ( signalChan , syscall . SIGTERM )
<- signalChan
2018-12-05 16:27:55 +00:00
klog . Info ( "Received SIGTERM, shutting down" )
2016-11-10 22:56:29 +00:00
exitCode := 0
2017-08-28 16:06:58 +00:00
if err := ngx . Stop ( ) ; err != nil {
2018-12-05 16:27:55 +00:00
klog . Infof ( "Error during shutdown: %v" , err )
2016-11-10 22:56:29 +00:00
exitCode = 1
}
2018-12-05 16:27:55 +00:00
klog . Info ( "Handled quit, awaiting Pod deletion" )
2017-11-05 01:18:28 +00:00
time . Sleep ( 10 * time . Second )
2018-12-05 16:27:55 +00:00
klog . Infof ( "Exiting with %v" , exitCode )
2017-11-22 13:35:47 +00:00
exit ( exitCode )
2016-11-10 22:56:29 +00:00
}
2017-11-05 01:18:28 +00:00
2018-06-11 09:17:50 +00:00
// createApiserverClient creates a new Kubernetes REST client. apiserverHost is
// the URL of the API server in the format protocol://address:port/pathPrefix,
// kubeConfig is the location of a kubeconfig file. If defined, the kubeconfig
// file is loaded first, the URL of the API server read from the file is then
2018-07-09 21:47:48 +00:00
// optionally overridden by the value of apiserverHost.
2018-09-21 08:19:16 +00:00
// If neither apiserverHost nor kubeConfig is passed in, we assume the
2018-06-11 09:17:50 +00:00
// controller runs inside Kubernetes and fallback to the in-cluster config. If
// the in-cluster config is missing or fails, we fallback to the default config.
func createApiserverClient ( apiserverHost , kubeConfig string ) ( * kubernetes . Clientset , error ) {
2018-03-01 13:27:53 +00:00
cfg , err := clientcmd . BuildConfigFromFlags ( apiserverHost , kubeConfig )
2017-11-05 01:18:28 +00:00
if err != nil {
return nil , err
}
cfg . QPS = defaultQPS
cfg . Burst = defaultBurst
cfg . ContentType = "application/vnd.kubernetes.protobuf"
2018-12-05 16:27:55 +00:00
klog . Infof ( "Creating API client for %s" , cfg . Host )
2017-11-05 01:18:28 +00:00
client , err := kubernetes . NewForConfig ( cfg )
if err != nil {
return nil , err
}
2018-02-17 20:25:04 +00:00
var v * discovery . Info
2018-06-11 09:17:50 +00:00
// The client may fail to connect to the API server in the first request.
2018-02-17 20:25:04 +00:00
// https://github.com/kubernetes/ingress-nginx/issues/1968
defaultRetry := wait . Backoff {
Steps : 10 ,
Duration : 1 * time . Second ,
Factor : 1.5 ,
Jitter : 0.1 ,
}
var lastErr error
retries := 0
2018-12-05 16:27:55 +00:00
klog . V ( 2 ) . Info ( "Trying to discover Kubernetes version" )
2018-02-17 20:25:04 +00:00
err = wait . ExponentialBackoff ( defaultRetry , func ( ) ( bool , error ) {
v , err = client . Discovery ( ) . ServerVersion ( )
if err == nil {
return true , nil
}
lastErr = err
2018-12-05 16:27:55 +00:00
klog . V ( 2 ) . Infof ( "Unexpected error discovering Kubernetes version (attempt %v): %v" , retries , err )
2018-02-17 20:25:04 +00:00
retries ++
return false , nil
} )
2018-06-11 09:17:50 +00:00
// err is returned in case of timeout in the exponential backoff (ErrWaitTimeout)
2017-11-05 01:18:28 +00:00
if err != nil {
2018-02-17 20:25:04 +00:00
return nil , lastErr
}
// this should not happen, warn the user
if retries > 0 {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Initial connection to the Kubernetes API server was retried %d times." , retries )
2017-11-05 01:18:28 +00:00
}
2018-12-05 16:27:55 +00:00
klog . Infof ( "Running in Kubernetes cluster version v%v.%v (%v) - git (%v) commit %v - platform %v" ,
2017-11-05 01:18:28 +00:00
v . Major , v . Minor , v . GitVersion , v . GitTreeState , v . GitCommit , v . Platform )
return client , nil
}
2018-06-11 09:17:50 +00:00
// Handler for fatal init errors. Prints a verbose error message and exits.
2017-11-05 01:18:28 +00:00
func handleFatalInitError ( err error ) {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Error while initiating a connection to the Kubernetes API server. " +
2018-06-11 09:17:50 +00:00
"This could mean the cluster is misconfigured (e.g. it has invalid API server certificates " +
"or Service Accounts configuration). Reason: %s\n" +
2017-11-05 01:18:28 +00:00
"Refer to the troubleshooting guide for more information: " +
2018-06-11 09:17:50 +00:00
"https://kubernetes.github.io/ingress-nginx/troubleshooting/" ,
err )
2017-11-05 01:18:28 +00:00
}
2018-07-12 17:18:43 +00:00
func registerHandlers ( mux * http . ServeMux ) {
2017-11-05 01:18:28 +00:00
mux . HandleFunc ( "/build" , func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( http . StatusOK )
b , _ := json . Marshal ( version . String ( ) )
w . Write ( b )
} )
mux . HandleFunc ( "/stop" , func ( w http . ResponseWriter , r * http . Request ) {
err := syscall . Kill ( syscall . Getpid ( ) , syscall . SIGTERM )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Errorf ( "Unexpected error: %v" , err )
2017-11-05 01:18:28 +00:00
}
} )
2018-07-12 17:18:43 +00:00
}
2017-11-05 01:18:28 +00:00
2018-07-12 17:18:43 +00:00
func registerHealthz ( ic * controller . NGINXController , mux * http . ServeMux ) {
// expose health check endpoint (/healthz)
healthz . InstallHandler ( mux ,
healthz . PingHealthz ,
ic ,
)
}
func registerMetrics ( reg * prometheus . Registry , mux * http . ServeMux ) {
mux . Handle (
"/metrics" ,
promhttp . InstrumentMetricHandler (
reg ,
promhttp . HandlerFor ( reg , promhttp . HandlerOpts { } ) ,
) ,
)
}
func registerProfiler ( mux * http . ServeMux ) {
mux . HandleFunc ( "/debug/pprof/" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/heap" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/mutex" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/goroutine" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/threadcreate" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/block" , pprof . Index )
mux . HandleFunc ( "/debug/pprof/cmdline" , pprof . Cmdline )
mux . HandleFunc ( "/debug/pprof/profile" , pprof . Profile )
mux . HandleFunc ( "/debug/pprof/symbol" , pprof . Symbol )
mux . HandleFunc ( "/debug/pprof/trace" , pprof . Trace )
}
2017-11-05 01:18:28 +00:00
2018-07-12 17:18:43 +00:00
func startHTTPServer ( port int , mux * http . ServeMux ) {
2017-11-05 01:18:28 +00:00
server := & http . Server {
2018-01-18 20:55:56 +00:00
Addr : fmt . Sprintf ( ":%v" , port ) ,
Handler : mux ,
ReadTimeout : 10 * time . Second ,
ReadHeaderTimeout : 10 * time . Second ,
WriteTimeout : 300 * time . Second ,
IdleTimeout : 120 * time . Second ,
2017-11-05 01:18:28 +00:00
}
2018-12-05 16:27:55 +00:00
klog . Fatal ( server . ListenAndServe ( ) )
2017-11-05 01:18:28 +00:00
}