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
"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"
2019-12-05 22:12:54 +00:00
"k8s.io/client-go/rest"
2017-11-05 01:18:28 +00:00
"k8s.io/client-go/tools/clientcmd"
2019-12-05 22:12:54 +00:00
certutil "k8s.io/client-go/util/cert"
2018-12-05 16:27:55 +00:00
"k8s.io/klog"
2017-11-05 01:18:28 +00:00
2019-08-16 00:25:35 +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"
2019-09-28 20:30:57 +00:00
"k8s.io/ingress-nginx/internal/nginx"
2017-11-05 01:18:28 +00:00
"k8s.io/ingress-nginx/version"
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
}
2019-08-16 00:25:35 +00:00
err = file . CreateRequiredDirectories ( )
if err != nil {
klog . Fatal ( err )
}
2019-12-05 22:12:54 +00:00
kubeClient , err := createApiserverClient ( conf . APIServerHost , conf . RootCAFile , conf . KubeConfigFile )
2017-11-05 01:18:28 +00:00
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
}
}
2019-08-13 21:14:55 +00:00
conf . FakeCertificate = ssl . GetFakeSSLCert ( )
klog . Infof ( "SSL fake certificate created %v" , conf . FakeCertificate . PemFileName )
2017-11-05 01:18:28 +00:00
2019-06-09 22:49:59 +00:00
k8s . IsNetworkingIngressAvailable = k8s . NetworkingIngressAvailable ( kubeClient )
if ! k8s . IsNetworkingIngressAvailable {
klog . Warningf ( "Using deprecated \"k8s.io/api/extensions/v1beta1\" package because Kubernetes version is < v1.14.0" )
}
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 {
2019-01-21 14:29:36 +00:00
mc , err = metric . NewCollector ( conf . MetricsPerHost , reg )
2018-12-04 19:59:54 +00:00
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-12 17:18:43 +00:00
if conf . EnableProfiling {
2019-09-01 18:21:24 +00:00
go registerProfiler ( )
2018-07-12 17:18:43 +00:00
}
2020-01-25 17:52:31 +00:00
ngx := controller . NewNGINXController ( conf , mc )
mux := http . NewServeMux ( )
2019-09-29 17:37:57 +00:00
registerHealthz ( nginx . HealthPath , ngx , mux )
2018-07-12 17:18:43 +00:00
registerMetrics ( reg , mux )
go startHTTPServer ( conf . ListenPorts . Health , mux )
2020-01-25 17:52:31 +00:00
go ngx . Start ( )
2018-06-14 00:55:07 +00:00
2020-01-25 17:52:31 +00:00
handleSigterm ( ngx , func ( code int ) {
os . Exit ( code )
} )
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.
2019-12-05 22:12:54 +00:00
func createApiserverClient ( apiserverHost , rootCAFile , 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
}
2019-12-05 22:12:54 +00:00
if apiserverHost != "" && rootCAFile != "" {
tlsClientConfig := rest . TLSClientConfig { }
if _ , err := certutil . NewPool ( rootCAFile ) ; err != nil {
klog . Errorf ( "Expected to load root CA config from %s, but got err: %v" , rootCAFile , err )
} else {
tlsClientConfig . CAFile = rootCAFile
}
cfg . TLSClientConfig = tlsClientConfig
}
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
}
2019-09-29 17:37:57 +00:00
func registerHealthz ( healthPath string , ic * controller . NGINXController , mux * http . ServeMux ) {
2018-07-12 17:18:43 +00:00
// expose health check endpoint (/healthz)
2019-09-29 17:37:57 +00:00
healthz . InstallPathHandler ( mux ,
healthPath ,
2018-07-12 17:18:43 +00:00
healthz . PingHealthz ,
ic ,
)
}
func registerMetrics ( reg * prometheus . Registry , mux * http . ServeMux ) {
mux . Handle (
"/metrics" ,
promhttp . InstrumentMetricHandler (
reg ,
promhttp . HandlerFor ( reg , promhttp . HandlerOpts { } ) ,
) ,
)
}
2019-09-01 18:21:24 +00:00
func registerProfiler ( ) {
mux := http . NewServeMux ( )
2018-07-12 17:18:43 +00:00
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 )
2019-09-01 18:21:24 +00:00
server := & http . Server {
2019-09-28 20:30:57 +00:00
Addr : fmt . Sprintf ( "127.0.0.1:%v" , nginx . ProfilerPort ) ,
2019-09-01 18:21:24 +00:00
Handler : mux ,
}
klog . Fatal ( server . ListenAndServe ( ) )
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 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
}