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 .
* /
2017-10-06 20:33:32 +00:00
package controller
2016-11-10 22:56:29 +00:00
import (
"bytes"
2018-03-18 13:13:41 +00:00
"encoding/json"
2016-11-29 01:39:17 +00:00
"errors"
2016-11-10 22:56:29 +00:00
"fmt"
"io/ioutil"
2019-01-15 20:38:30 +00:00
"math"
2016-11-29 01:39:17 +00:00
"net"
2018-03-18 13:13:41 +00:00
"net/http"
2016-11-10 22:56:29 +00:00
"os"
"os/exec"
2018-04-24 18:02:52 +00:00
"path/filepath"
2017-03-28 16:39:44 +00:00
"strconv"
2017-04-09 23:51:38 +00:00
"strings"
2017-11-05 01:18:28 +00:00
"sync"
2016-11-29 01:39:17 +00:00
"syscall"
2018-06-21 22:15:18 +00:00
"text/template"
2016-11-29 01:39:17 +00:00
"time"
2016-11-10 22:56:29 +00:00
2018-01-18 19:14:42 +00:00
proxyproto "github.com/armon/go-proxyproto"
2018-02-14 01:46:18 +00:00
"github.com/eapache/channels"
2017-09-17 18:42:31 +00:00
apiv1 "k8s.io/api/core/v1"
2018-11-16 16:48:47 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
2019-01-09 03:33:16 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2017-11-05 01:18:28 +00:00
"k8s.io/client-go/kubernetes/scheme"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/flowcontrol"
2018-12-05 16:27:55 +00:00
"k8s.io/klog"
2017-11-06 22:34:30 +00:00
"k8s.io/kubernetes/pkg/util/filesystem"
2016-11-16 18:24:26 +00:00
2017-11-22 13:40:54 +00:00
"k8s.io/ingress-nginx/internal/file"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/ingress/controller/process"
2018-01-18 19:14:42 +00:00
"k8s.io/ingress-nginx/internal/ingress/controller/store"
2017-11-07 22:02:12 +00:00
ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template"
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/ingress/status"
2018-11-20 20:29:20 +00:00
"k8s.io/ingress-nginx/internal/k8s"
2017-11-07 22:02:12 +00:00
ing_net "k8s.io/ingress-nginx/internal/net"
"k8s.io/ingress-nginx/internal/net/dns"
"k8s.io/ingress-nginx/internal/net/ssl"
2019-01-21 14:29:36 +00:00
"k8s.io/ingress-nginx/internal/nginx"
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/task"
2017-11-22 13:40:54 +00:00
"k8s.io/ingress-nginx/internal/watch"
2016-11-29 01:39:17 +00:00
)
const (
2019-01-21 14:29:36 +00:00
tempNginxPattern = "nginx-cfg"
2016-11-10 22:56:29 +00:00
)
var (
2018-06-14 00:55:07 +00:00
tmplPath = "/etc/nginx/template/nginx.tmpl"
2016-11-10 22:56:29 +00:00
)
2017-10-06 20:33:32 +00:00
// NewNGINXController creates a new NGINX Ingress controller.
2018-07-07 17:46:18 +00:00
func NewNGINXController ( config * Configuration , mc metric . Collector , fs file . Filesystem ) * NGINXController {
2017-11-05 01:18:28 +00:00
eventBroadcaster := record . NewBroadcaster ( )
2018-12-05 16:27:55 +00:00
eventBroadcaster . StartLogging ( klog . Infof )
2017-11-05 01:18:28 +00:00
eventBroadcaster . StartRecordingToSink ( & v1core . EventSinkImpl {
Interface : config . Client . CoreV1 ( ) . Events ( config . Namespace ) ,
} )
2017-04-11 14:47:49 +00:00
h , err := dns . GetSystemNameServers ( )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Error reading system nameservers: %v" , err )
2017-04-11 14:47:49 +00:00
}
2017-03-12 15:27:05 +00:00
n := & NGINXController {
2017-11-05 01:18:28 +00:00
isIPV6Enabled : ing_net . IsIPv6Enabled ( ) ,
2017-08-29 19:40:03 +00:00
resolver : h ,
2017-11-05 01:18:28 +00:00
cfg : config ,
2017-12-05 19:07:34 +00:00
syncRateLimiter : flowcontrol . NewTokenBucketRateLimiter ( config . SyncRateLimit , 1 ) ,
2017-11-05 01:18:28 +00:00
recorder : eventBroadcaster . NewRecorder ( scheme . Scheme , apiv1 . EventSource {
Component : "nginx-ingress-controller" ,
} ) ,
stopCh : make ( chan struct { } ) ,
2018-02-14 01:46:18 +00:00
updateCh : channels . NewRingChannel ( 1024 ) ,
2018-01-18 19:14:42 +00:00
2017-11-05 01:18:28 +00:00
stopLock : & sync . Mutex { } ,
2017-11-06 22:34:30 +00:00
2017-11-22 13:40:54 +00:00
fileSystem : fs ,
2017-11-27 22:22:59 +00:00
2018-06-13 18:15:45 +00:00
runningConfig : new ( ingress . Configuration ) ,
2018-01-23 20:10:02 +00:00
Proxy : & TCPProxy { } ,
2018-07-07 17:46:18 +00:00
metricCollector : mc ,
2017-04-09 23:51:38 +00:00
}
2018-11-20 20:29:20 +00:00
pod , err := k8s . GetPodDetails ( config . Client )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "unexpected error obtaining pod information: %v" , err )
2018-11-20 20:29:20 +00:00
}
2018-01-25 13:46:20 +00:00
n . store = store . New (
config . EnableSSLChainCompletion ,
2018-01-18 19:14:42 +00:00
config . Namespace ,
config . ConfigMapName ,
2018-11-16 16:48:47 +00:00
config . TCPConfigMapName ,
config . UDPConfigMapName ,
2018-01-25 13:46:20 +00:00
config . DefaultSSLCertificate ,
2018-01-18 19:14:42 +00:00
config . ResyncPeriod ,
config . Client ,
fs ,
2018-06-04 21:48:30 +00:00
n . updateCh ,
2018-11-20 20:29:20 +00:00
config . DynamicCertificatesEnabled ,
2018-12-11 21:57:46 +00:00
pod ,
config . DisableCatchAll )
2017-11-07 16:36:51 +00:00
2017-11-05 01:18:28 +00:00
n . syncQueue = task . NewTaskQueue ( n . syncIngress )
if config . UpdateStatus {
n . syncStatus = status . NewStatusSyncer ( status . Config {
Client : config . Client ,
2017-11-06 01:22:49 +00:00
PublishService : config . PublishService ,
2018-02-27 03:02:19 +00:00
PublishStatusAddress : config . PublishStatusAddress ,
2018-01-18 19:14:42 +00:00
IngressLister : n . store ,
2017-11-05 01:18:28 +00:00
ElectionID : config . ElectionID ,
2017-11-22 13:40:54 +00:00
IngressClass : class . IngressClass ,
DefaultIngressClass : class . DefaultClass ,
2017-11-05 01:18:28 +00:00
UpdateStatusOnShutdown : config . UpdateStatusOnShutdown ,
2017-11-06 01:22:49 +00:00
UseNodeInternalIP : config . UseNodeInternalIP ,
2017-11-05 01:18:28 +00:00
} )
} else {
2018-12-05 16:27:55 +00:00
klog . Warning ( "Update of Ingress status is disabled (flag --update-status)" )
2017-11-05 01:18:28 +00:00
}
2018-04-25 21:53:49 +00:00
onTemplateChange := func ( ) {
2017-11-22 13:40:54 +00:00
template , err := ngx_template . NewTemplate ( tmplPath , fs )
2016-11-10 22:56:29 +00:00
if err != nil {
// this error is different from the rest because it must be clear why nginx is not working
2018-12-05 16:27:55 +00:00
klog . Errorf ( `
2016-11-10 22:56:29 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
2018-06-13 18:15:45 +00:00
Error loading new template : % v
2016-11-10 22:56:29 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
` , err )
return
}
n . t = template
2018-12-05 16:27:55 +00:00
klog . Info ( "New NGINX configuration template loaded." )
2018-06-21 14:50:57 +00:00
n . syncQueue . EnqueueTask ( task . GetDummyObject ( "template-change" ) )
2016-11-10 22:56:29 +00:00
}
2017-11-22 13:40:54 +00:00
ngxTpl , err := ngx_template . NewTemplate ( tmplPath , fs )
2016-11-10 22:56:29 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Invalid NGINX configuration template: %v" , err )
2016-11-10 22:56:29 +00:00
}
n . t = ngxTpl
2016-11-29 01:39:17 +00:00
2017-11-22 13:40:54 +00:00
if _ , ok := fs . ( filesystem . DefaultFs ) ; ! ok {
2018-06-16 20:22:59 +00:00
// do not setup watchers on tests
return n
}
_ , err = watch . NewFileWatcher ( tmplPath , onTemplateChange )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Error creating file watcher for %v: %v" , tmplPath , err )
2018-06-16 20:22:59 +00:00
}
2018-02-17 20:24:50 +00:00
2018-06-16 20:22:59 +00:00
filesToWatch := [ ] string { }
err = filepath . Walk ( "/etc/nginx/geoip/" , func ( path string , info os . FileInfo , err error ) error {
2018-02-17 20:24:50 +00:00
if err != nil {
2018-06-16 20:22:59 +00:00
return err
}
if info . IsDir ( ) {
return nil
2018-02-17 20:24:50 +00:00
}
2018-06-16 20:22:59 +00:00
filesToWatch = append ( filesToWatch , path )
return nil
} )
2018-02-20 16:27:02 +00:00
2018-06-16 20:22:59 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Error creating file watchers: %v" , err )
2018-06-16 20:22:59 +00:00
}
2018-02-17 20:24:50 +00:00
2018-06-16 20:22:59 +00:00
for _ , f := range filesToWatch {
_ , err = watch . NewFileWatcher ( f , func ( ) {
2018-12-05 16:27:55 +00:00
klog . Infof ( "File %v changed. Reloading NGINX" , f )
2018-06-21 14:50:57 +00:00
n . syncQueue . EnqueueTask ( task . GetDummyObject ( "file-change" ) )
2018-02-17 20:24:50 +00:00
} )
2017-11-22 13:40:54 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "Error creating file watcher for %v: %v" , f , err )
2018-02-17 20:24:50 +00:00
}
2017-11-22 13:40:54 +00:00
}
2017-08-28 16:06:58 +00:00
return n
2016-11-10 22:56:29 +00:00
}
2018-06-13 18:15:45 +00:00
// NGINXController describes a NGINX Ingress controller.
2016-11-10 22:56:29 +00:00
type NGINXController struct {
2017-11-05 01:18:28 +00:00
cfg * Configuration
recorder record . EventRecorder
syncQueue * task . Queue
syncStatus status . Sync
syncRateLimiter flowcontrol . RateLimiter
2018-06-13 18:15:45 +00:00
// stopLock is used to enforce that only a single call to Stop send at
// a given time. We allow stopping through an HTTP endpoint and
2017-11-05 01:18:28 +00:00
// allowing concurrent stoppers leads to stack traces.
stopLock * sync . Mutex
2018-01-18 19:14:42 +00:00
stopCh chan struct { }
2018-02-14 01:46:18 +00:00
updateCh * channels . RingChannel
2017-11-05 01:18:28 +00:00
2018-06-13 18:15:45 +00:00
// ngxErrCh is used to detect errors with the NGINX processes
2017-11-05 01:18:28 +00:00
ngxErrCh chan error
// runningConfig contains the running configuration in the Backend
runningConfig * ingress . Configuration
t * ngx_template . Template
2016-11-10 22:56:29 +00:00
2017-04-09 23:51:38 +00:00
resolver [ ] net . IP
2017-03-10 13:01:26 +00:00
2017-04-09 18:03:27 +00:00
isIPV6Enabled bool
2017-04-11 14:47:49 +00:00
2017-08-28 16:06:58 +00:00
isShuttingDown bool
2017-11-05 01:18:28 +00:00
Proxy * TCPProxy
2017-08-26 03:46:17 +00:00
2018-01-18 19:14:42 +00:00
store store . Storer
2017-11-06 22:34:30 +00:00
fileSystem filesystem . Filesystem
2018-07-07 17:46:18 +00:00
metricCollector metric . Collector
2016-11-10 22:56:29 +00:00
}
2018-06-13 18:15:45 +00:00
// Start starts a new NGINX master process running in the foreground.
2017-03-12 15:27:05 +00:00
func ( n * NGINXController ) Start ( ) {
2018-12-05 16:27:55 +00:00
klog . Info ( "Starting NGINX Ingress controller" )
2017-11-05 01:18:28 +00:00
2018-01-18 19:14:42 +00:00
n . store . Run ( n . stopCh )
2017-11-13 01:43:28 +00:00
2017-11-05 01:18:28 +00:00
if n . syncStatus != nil {
2018-10-29 16:01:41 +00:00
go n . syncStatus . Run ( )
2017-11-05 01:18:28 +00:00
}
2018-06-14 00:55:07 +00:00
cmd := nginxExecCommand ( )
2017-08-28 16:06:58 +00:00
2018-06-13 18:15:45 +00:00
// put NGINX in another process group to prevent it
2017-08-28 16:06:58 +00:00
// to receive signals meant for the controller
cmd . SysProcAttr = & syscall . SysProcAttr {
Setpgid : true ,
Pgid : 0 ,
}
2018-01-18 19:14:42 +00:00
if n . cfg . EnableSSLPassthrough {
n . setupSSLProxy ( )
}
2018-12-05 16:27:55 +00:00
klog . Info ( "Starting NGINX process" )
2017-11-05 01:18:28 +00:00
n . start ( cmd )
2016-11-29 01:39:17 +00:00
2017-11-27 22:22:59 +00:00
go n . syncQueue . Run ( time . Second , n . stopCh )
2017-11-05 01:18:28 +00:00
// force initial sync
2018-06-21 14:50:57 +00:00
n . syncQueue . EnqueueTask ( task . GetDummyObject ( "initial-sync" ) )
2017-08-28 16:06:58 +00:00
2018-12-03 17:27:29 +00:00
// In case of error the temporal configuration file will
// be available up to five minutes after the error
go func ( ) {
for {
time . Sleep ( 5 * time . Minute )
err := cleanTempNginxCfg ( )
if err != nil {
klog . Infof ( "Unexpected error removing temporal configuration files: %v" , err )
}
}
} ( )
2017-11-05 01:18:28 +00:00
for {
select {
2018-03-03 12:23:06 +00:00
case err := <- n . ngxErrCh :
2017-11-05 01:18:28 +00:00
if n . isShuttingDown {
2016-11-29 01:39:17 +00:00
break
}
2017-09-28 14:52:12 +00:00
2017-11-05 01:18:28 +00:00
// if the nginx master process dies the workers continue to process requests,
// passing checks but in case of updates in ingress no updates will be
// reflected in the nginx configuration which can lead to confusion and report
// issues because of this behavior.
// To avoid this issue we restart nginx in case of errors.
if process . IsRespawnIfRequired ( err ) {
process . WaitUntilPortIsAvailable ( n . cfg . ListenPorts . HTTP )
// release command resources
cmd . Process . Release ( )
// start a new nginx master process if the controller is not being stopped
2018-06-14 00:55:07 +00:00
cmd = nginxExecCommand ( )
2018-03-08 14:58:54 +00:00
cmd . SysProcAttr = & syscall . SysProcAttr {
Setpgid : true ,
Pgid : 0 ,
}
2017-11-05 01:18:28 +00:00
n . start ( cmd )
2017-09-28 14:52:12 +00:00
}
2018-02-14 01:46:18 +00:00
case event := <- n . updateCh . Out ( ) :
2018-01-18 19:14:42 +00:00
if n . isShuttingDown {
break
}
2018-02-14 01:46:18 +00:00
if evt , ok := event . ( store . Event ) ; ok {
2018-12-05 16:27:55 +00:00
klog . V ( 3 ) . Infof ( "Event %v received - object %v" , evt . Type , evt . Obj )
2018-02-14 01:46:18 +00:00
if evt . Type == store . ConfigurationEvent {
2018-06-21 14:50:57 +00:00
// TODO: is this necessary? Consider removing this special case
n . syncQueue . EnqueueTask ( task . GetDummyObject ( "configmap-change" ) )
continue
2018-02-14 01:46:18 +00:00
}
2018-06-21 14:50:57 +00:00
n . syncQueue . EnqueueSkippableTask ( evt . Obj )
2018-02-14 01:46:18 +00:00
} else {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Unexpected event type received %T" , event )
2018-01-18 23:04:40 +00:00
}
2017-11-05 01:18:28 +00:00
case <- n . stopCh :
break
2016-11-29 01:39:17 +00:00
}
}
}
2017-08-28 16:06:58 +00:00
// Stop gracefully stops the NGINX master process.
func ( n * NGINXController ) Stop ( ) error {
n . isShuttingDown = true
2017-11-05 01:18:28 +00:00
n . stopLock . Lock ( )
defer n . stopLock . Unlock ( )
if n . syncQueue . IsShuttingDown ( ) {
return fmt . Errorf ( "shutdown already in progress" )
}
2018-12-05 16:27:55 +00:00
klog . Info ( "Shutting down controller queues" )
2017-11-05 01:18:28 +00:00
close ( n . stopCh )
go n . syncQueue . Shutdown ( )
if n . syncStatus != nil {
n . syncStatus . Shutdown ( )
}
2017-08-28 16:06:58 +00:00
2018-06-12 13:04:26 +00:00
// send stop signal to NGINX
2018-12-05 16:27:55 +00:00
klog . Info ( "Stopping NGINX process" )
2018-06-14 00:55:07 +00:00
cmd := nginxExecCommand ( "-s" , "quit" )
2017-08-28 16:06:58 +00:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
err := cmd . Run ( )
if err != nil {
return err
}
2018-06-13 18:15:45 +00:00
// wait for the NGINX process to terminate
2017-11-05 01:18:28 +00:00
timer := time . NewTicker ( time . Second * 1 )
2017-11-06 22:38:16 +00:00
for range timer . C {
2017-11-05 01:18:28 +00:00
if ! process . IsNginxRunning ( ) {
2018-12-05 16:27:55 +00:00
klog . Info ( "NGINX process has stopped" )
2017-11-05 01:18:28 +00:00
timer . Stop ( )
break
}
}
2017-08-28 16:06:58 +00:00
return nil
}
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) start ( cmd * exec . Cmd ) {
2016-11-10 22:56:29 +00:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Start ( ) ; err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "NGINX error: %v" , err )
2017-11-05 01:18:28 +00:00
n . ngxErrCh <- err
2016-11-29 01:39:17 +00:00
return
2016-11-10 22:56:29 +00:00
}
2017-03-10 13:01:26 +00:00
2016-11-29 01:39:17 +00:00
go func ( ) {
2017-11-05 01:18:28 +00:00
n . ngxErrCh <- cmd . Wait ( )
2016-11-29 01:39:17 +00:00
} ( )
2016-11-10 22:56:29 +00:00
}
2017-08-24 13:33:26 +00:00
// DefaultEndpoint returns the default endpoint to be use as default server that returns 404.
func ( n NGINXController ) DefaultEndpoint ( ) ingress . Endpoint {
return ingress . Endpoint {
Address : "127.0.0.1" ,
2017-11-05 01:18:28 +00:00
Port : fmt . Sprintf ( "%v" , n . cfg . ListenPorts . Default ) ,
2017-09-17 18:42:31 +00:00
Target : & apiv1 . ObjectReference { } ,
2017-08-24 13:33:26 +00:00
}
}
2016-11-10 22:56:29 +00:00
// testTemplate checks if the NGINX configuration inside the byte array is valid
// running the command "nginx -t" using a temporal file.
func ( n NGINXController ) testTemplate ( cfg [ ] byte ) error {
2017-04-27 02:34:36 +00:00
if len ( cfg ) == 0 {
2018-08-30 15:09:04 +00:00
return fmt . Errorf ( "invalid NGINX configuration (empty)" )
2017-04-27 02:34:36 +00:00
}
2018-12-03 17:27:29 +00:00
tmpfile , err := ioutil . TempFile ( "" , tempNginxPattern )
2016-11-10 22:56:29 +00:00
if err != nil {
return err
}
defer tmpfile . Close ( )
2018-06-12 12:40:40 +00:00
err = ioutil . WriteFile ( tmpfile . Name ( ) , cfg , file . ReadWriteByUser )
2017-03-03 07:17:32 +00:00
if err != nil {
return err
}
2018-06-14 00:55:07 +00:00
out , err := nginxTestCommand ( tmpfile . Name ( ) ) . CombinedOutput ( )
2017-05-16 20:06:33 +00:00
if err != nil {
2016-11-10 22:56:29 +00:00
// this error is different from the rest because it must be clear why nginx is not working
2016-11-16 18:24:26 +00:00
oe := fmt . Sprintf ( `
2016-11-10 22:56:29 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
Error : % v
% v
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
` , err , string ( out ) )
2018-12-03 17:27:29 +00:00
2016-11-16 18:24:26 +00:00
return errors . New ( oe )
2016-11-10 22:56:29 +00:00
}
os . Remove ( tmpfile . Name ( ) )
return nil
}
2018-06-13 18:15:45 +00:00
// OnUpdate is called by the synchronization loop whenever configuration
// changes were detected. The received backend Configuration is merged with the
// configuration ConfigMap before generating the final configuration file.
// Returns nil in case the backend was successfully reloaded.
2017-06-11 19:56:40 +00:00
func ( n * NGINXController ) OnUpdate ( ingressCfg ingress . Configuration ) error {
2018-01-18 19:14:42 +00:00
cfg := n . store . GetBackendConfiguration ( )
2017-04-11 14:47:49 +00:00
cfg . Resolver = n . resolver
2017-03-10 13:01:26 +00:00
2018-01-23 20:10:02 +00:00
if n . cfg . EnableSSLPassthrough {
servers := [ ] * TCPServer { }
for _ , pb := range ingressCfg . PassthroughBackends {
svc := pb . Service
if svc == nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Missing Service for SSL Passthrough backend %q" , pb . Backend )
2018-01-23 20:10:02 +00:00
continue
2017-05-26 18:25:06 +00:00
}
2018-01-23 20:10:02 +00:00
port , err := strconv . Atoi ( pb . Port . String ( ) )
if err != nil {
for _ , sp := range svc . Spec . Ports {
if sp . Name == pb . Port . String ( ) {
port = int ( sp . Port )
break
}
}
} else {
for _ , sp := range svc . Spec . Ports {
if sp . Port == int32 ( port ) {
port = int ( sp . Port )
break
}
2017-05-26 18:25:06 +00:00
}
}
2018-01-23 20:10:02 +00:00
2018-06-13 18:15:45 +00:00
// TODO: Allow PassthroughBackends to specify they support proxy-protocol
2018-01-23 20:10:02 +00:00
servers = append ( servers , & TCPServer {
Hostname : pb . Hostname ,
IP : svc . Spec . ClusterIP ,
Port : port ,
ProxyProtocol : false ,
} )
2017-05-26 18:25:06 +00:00
}
2018-01-23 20:10:02 +00:00
n . Proxy . ServerList = servers
2017-05-26 18:25:06 +00:00
}
2018-06-12 13:04:26 +00:00
// NGINX cannot resize the hash tables used to store server names. For
// this reason we check if the current size is correct for the host
// names defined in the Ingress rules and adjust the value if
// necessary.
2016-11-10 22:56:29 +00:00
// https://trac.nginx.org/nginx/ticket/352
// https://trac.nginx.org/nginx/ticket/631
2017-08-16 07:02:30 +00:00
var longestName int
var serverNameBytes int
2019-01-09 03:33:16 +00:00
2017-08-16 07:02:30 +00:00
for _ , srv := range ingressCfg . Servers {
if longestName < len ( srv . Hostname ) {
longestName = len ( srv . Hostname )
}
serverNameBytes += len ( srv . Hostname )
}
2019-01-09 03:33:16 +00:00
2017-04-14 23:59:10 +00:00
if cfg . ServerNameHashBucketSize == 0 {
2017-08-16 07:02:30 +00:00
nameHashBucketSize := nginxHashBucketSize ( longestName )
2018-12-05 16:27:55 +00:00
klog . V ( 3 ) . Infof ( "Adjusting ServerNameHashBucketSize variable to %d" , nameHashBucketSize )
2016-11-10 22:56:29 +00:00
cfg . ServerNameHashBucketSize = nameHashBucketSize
}
2019-01-09 03:33:16 +00:00
2017-05-24 00:02:12 +00:00
serverNameHashMaxSize := nextPowerOf2 ( serverNameBytes )
if cfg . ServerNameHashMaxSize < serverNameHashMaxSize {
2018-12-05 16:27:55 +00:00
klog . V ( 3 ) . Infof ( "Adjusting ServerNameHashMaxSize variable to %d" , serverNameHashMaxSize )
2016-11-10 22:56:29 +00:00
cfg . ServerNameHashMaxSize = serverNameHashMaxSize
}
2018-12-27 16:24:09 +00:00
if cfg . MaxWorkerOpenFiles == 0 {
// the limit of open files is per worker process
// and we leave some room to avoid consuming all the FDs available
wp , err := strconv . Atoi ( cfg . WorkerProcesses )
klog . V ( 3 ) . Infof ( "Number of worker processes: %d" , wp )
if err != nil {
wp = 1
}
2019-01-15 20:34:17 +00:00
maxOpenFiles := ( rlimitMaxNumFiles ( ) / wp ) - 1024
2018-12-27 16:24:09 +00:00
klog . V ( 3 ) . Infof ( "Maximum number of open file descriptors: %d" , maxOpenFiles )
if maxOpenFiles < 1024 {
// this means the value of RLIMIT_NOFILE is too low.
maxOpenFiles = 1024
}
klog . V ( 3 ) . Infof ( "Adjusting MaxWorkerOpenFiles variable to %d" , maxOpenFiles )
cfg . MaxWorkerOpenFiles = maxOpenFiles
2017-03-28 16:39:44 +00:00
}
2018-12-27 16:24:09 +00:00
if cfg . MaxWorkerConnections == 0 {
2019-01-15 20:38:30 +00:00
maxWorkerConnections := int ( math . Ceil ( float64 ( cfg . MaxWorkerOpenFiles * 3.0 / 4 ) ) )
klog . V ( 3 ) . Infof ( "Adjusting MaxWorkerConnections variable to %d" , maxWorkerConnections )
cfg . MaxWorkerConnections = maxWorkerConnections
2017-03-30 13:10:47 +00:00
}
2017-01-19 02:31:33 +00:00
2017-02-07 18:13:08 +00:00
setHeaders := map [ string ] string { }
if cfg . ProxySetHeaders != "" {
2018-01-18 19:14:42 +00:00
cmap , err := n . store . GetConfigMap ( cfg . ProxySetHeaders )
2017-02-07 18:13:08 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Error reading ConfigMap %q from local store: %v" , cfg . ProxySetHeaders , err )
2017-02-07 18:13:08 +00:00
}
2018-01-18 19:14:42 +00:00
setHeaders = cmap . Data
2017-02-07 18:13:08 +00:00
}
2017-05-18 10:21:03 +00:00
addHeaders := map [ string ] string { }
if cfg . AddHeaders != "" {
2018-01-18 19:14:42 +00:00
cmap , err := n . store . GetConfigMap ( cfg . AddHeaders )
2017-05-18 10:21:03 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Error reading ConfigMap %q from local store: %v" , cfg . AddHeaders , err )
2017-05-18 10:21:03 +00:00
}
2018-01-18 19:14:42 +00:00
addHeaders = cmap . Data
2017-05-18 10:21:03 +00:00
}
2017-03-08 13:41:55 +00:00
sslDHParam := ""
if cfg . SSLDHParam != "" {
secretName := cfg . SSLDHParam
2018-01-18 19:14:42 +00:00
secret , err := n . store . GetSecret ( secretName )
2017-03-08 13:41:55 +00:00
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Error reading Secret %q from local store: %v" , secretName , err )
2017-03-08 13:41:55 +00:00
}
2018-01-18 19:14:42 +00:00
nsSecName := strings . Replace ( secretName , "/" , "-" , - 1 )
dh , ok := secret . Data [ "dhparam.pem" ]
if ok {
pemFileName , err := ssl . AddOrUpdateDHParam ( nsSecName , dh , n . fileSystem )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Error adding or updating dhparam file %v: %v" , nsSecName , err )
2018-01-18 19:14:42 +00:00
} else {
sslDHParam = pemFileName
2017-03-08 13:41:55 +00:00
}
}
}
cfg . SSLDHParam = sslDHParam
2017-11-05 01:18:28 +00:00
tc := ngx_config . TemplateConfig {
2018-10-09 22:36:10 +00:00
ProxySetHeaders : setHeaders ,
AddHeaders : addHeaders ,
BacklogSize : sysctlSomaxconn ( ) ,
Backends : ingressCfg . Backends ,
PassthroughBackends : ingressCfg . PassthroughBackends ,
Servers : ingressCfg . Servers ,
2018-11-16 16:48:47 +00:00
TCPBackends : ingressCfg . TCPEndpoints ,
UDPBackends : ingressCfg . UDPEndpoints ,
2018-10-09 22:36:10 +00:00
Cfg : cfg ,
IsIPV6Enabled : n . isIPV6Enabled && ! cfg . DisableIpv6 ,
NginxStatusIpv4Whitelist : cfg . NginxStatusIpv4Whitelist ,
NginxStatusIpv6Whitelist : cfg . NginxStatusIpv6Whitelist ,
2019-01-09 03:33:16 +00:00
RedirectServers : buildRedirects ( ingressCfg . Servers ) ,
2018-10-09 22:36:10 +00:00
IsSSLPassthroughEnabled : n . cfg . EnableSSLPassthrough ,
L istenPorts : n . cfg . ListenPorts ,
PublishService : n . GetPublishService ( ) ,
DynamicCertificatesEnabled : n . cfg . DynamicCertificatesEnabled ,
2018-12-04 19:59:54 +00:00
EnableMetrics : n . cfg . EnableMetrics ,
2019-01-21 14:29:36 +00:00
HealthzURI : nginx . HealthPath ,
PID : nginx . PID ,
StatusSocket : nginx . StatusSocket ,
StatusPath : nginx . StatusPath ,
StreamSocket : nginx . StreamSocket ,
2017-08-03 14:51:39 +00:00
}
2018-07-07 17:46:18 +00:00
tc . Cfg . Checksum = ingressCfg . ConfigurationChecksum
2017-08-03 14:51:39 +00:00
content , err := n . t . Write ( tc )
2017-06-11 19:56:40 +00:00
if err != nil {
return err
}
2018-06-21 22:15:18 +00:00
if cfg . EnableOpentracing {
err := createOpentracingCfg ( cfg )
if err != nil {
return err
}
}
2017-06-11 19:56:40 +00:00
err = n . testTemplate ( content )
if err != nil {
return err
}
2018-12-05 16:27:55 +00:00
if klog . V ( 2 ) {
2017-11-05 01:18:28 +00:00
src , _ := ioutil . ReadFile ( cfgPath )
if ! bytes . Equal ( src , content ) {
tmpfile , err := ioutil . TempFile ( "" , "new-nginx-cfg" )
if err != nil {
return err
}
defer tmpfile . Close ( )
2018-06-12 12:40:40 +00:00
err = ioutil . WriteFile ( tmpfile . Name ( ) , content , file . ReadWriteByUser )
2017-11-05 01:18:28 +00:00
if err != nil {
return err
}
2018-12-12 01:08:02 +00:00
diffOutput , err := exec . Command ( "diff" , "-u" , cfgPath , tmpfile . Name ( ) ) . CombinedOutput ( )
if err != nil {
klog . Warningf ( "Failed to executing diff command: %v" , err )
}
2017-11-05 01:18:28 +00:00
2018-12-05 16:27:55 +00:00
klog . Infof ( "NGINX configuration diff:\n%v" , string ( diffOutput ) )
2017-11-05 01:18:28 +00:00
2018-06-13 18:15:45 +00:00
// we do not defer the deletion of temp files in order
// to keep them around for inspection in case of error
2017-11-05 01:18:28 +00:00
os . Remove ( tmpfile . Name ( ) )
}
}
2017-06-11 19:56:40 +00:00
2018-06-12 12:40:40 +00:00
err = ioutil . WriteFile ( cfgPath , content , file . ReadWriteByUser )
2017-02-20 02:34:05 +00:00
if err != nil {
2017-06-11 19:56:40 +00:00
return err
2017-02-20 02:34:05 +00:00
}
2018-06-14 00:55:07 +00:00
o , err := nginxExecCommand ( "-s" , "reload" ) . CombinedOutput ( )
2017-06-11 19:56:40 +00:00
if err != nil {
2017-06-23 13:55:45 +00:00
return fmt . Errorf ( "%v\n%v" , err , string ( o ) )
2017-02-20 02:34:05 +00:00
}
2017-06-11 19:56:40 +00:00
return nil
2016-11-10 22:56:29 +00:00
}
2018-06-13 18:15:45 +00:00
// nginxHashBucketSize computes the correct NGINX hash_bucket_size for a hash
// with the given longest key.
2017-04-19 02:29:51 +00:00
func nginxHashBucketSize ( longestString int ) int {
2018-06-13 18:15:45 +00:00
// see https://github.com/kubernetes/ingress-nginxs/issues/623 for an explanation
2017-04-19 02:29:51 +00:00
wordSize := 8 // Assume 64 bit CPU
n := longestString + 2
aligned := ( n + wordSize - 1 ) & ^ ( wordSize - 1 )
rawSize := wordSize + wordSize + aligned
return nextPowerOf2 ( rawSize )
}
2016-11-10 22:56:29 +00:00
// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
// https://play.golang.org/p/TVSyCcdxUh
func nextPowerOf2 ( v int ) int {
v --
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
v ++
return v
}
2018-01-18 19:14:42 +00:00
func ( n * NGINXController ) setupSSLProxy ( ) {
2018-06-04 01:10:41 +00:00
cfg := n . store . GetBackendConfiguration ( )
2018-01-18 19:14:42 +00:00
sslPort := n . cfg . ListenPorts . HTTPS
proxyPort := n . cfg . ListenPorts . SSLProxy
2018-12-05 16:27:55 +00:00
klog . Info ( "Starting TLS proxy for SSL Passthrough" )
2018-01-18 19:14:42 +00:00
n . Proxy = & TCPProxy {
Default : & TCPServer {
Hostname : "localhost" ,
IP : "127.0.0.1" ,
Port : proxyPort ,
ProxyProtocol : true ,
} ,
}
listener , err := net . Listen ( "tcp" , fmt . Sprintf ( ":%v" , sslPort ) )
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Fatalf ( "%v" , err )
2018-01-18 19:14:42 +00:00
}
2018-06-04 01:10:41 +00:00
proxyList := & proxyproto . Listener { Listener : listener , ProxyHeaderTimeout : cfg . ProxyProtocolHeaderTimeout }
2018-01-18 19:14:42 +00:00
2018-06-13 18:15:45 +00:00
// accept TCP connections on the configured HTTPS port
2018-01-18 19:14:42 +00:00
go func ( ) {
for {
var conn net . Conn
var err error
if n . store . GetBackendConfiguration ( ) . UseProxyProtocol {
2018-06-13 18:15:45 +00:00
// wrap the listener in order to decode Proxy
// Protocol before handling the connection
2018-01-18 19:14:42 +00:00
conn , err = proxyList . Accept ( )
} else {
conn , err = listener . Accept ( )
}
if err != nil {
2018-12-05 16:27:55 +00:00
klog . Warningf ( "Error accepting TCP connection: %v" , err )
2018-01-18 19:14:42 +00:00
continue
}
2018-12-05 16:27:55 +00:00
klog . V ( 3 ) . Infof ( "Handling connection from remote address %s to local %s" , conn . RemoteAddr ( ) , conn . LocalAddr ( ) )
2018-01-18 19:14:42 +00:00
go n . Proxy . Handle ( conn )
}
} ( )
}
2018-03-18 13:13:41 +00:00
2018-06-04 21:48:30 +00:00
// Helper function to clear Certificates from the ingress configuration since they should be ignored when
// checking if the new configuration changes can be applied dynamically if dynamic certificates is on
func clearCertificates ( config * ingress . Configuration ) {
var clearedServers [ ] * ingress . Server
for _ , server := range config . Servers {
copyOfServer := * server
2018-06-05 13:51:22 +00:00
copyOfServer . SSLCert = ingress . SSLCert { PemFileName : copyOfServer . SSLCert . PemFileName }
2018-06-04 21:48:30 +00:00
clearedServers = append ( clearedServers , & copyOfServer )
}
config . Servers = clearedServers
}
2019-01-24 14:03:33 +00:00
// Helper function to clear endpoints from the ingress configuration since they should be ignored when
// checking if the new configuration changes can be applied dynamically.
func clearL4serviceEndpoints ( config * ingress . Configuration ) {
var clearedTCPL4Services [ ] ingress . L4Service
var clearedUDPL4Services [ ] ingress . L4Service
for _ , service := range config . TCPEndpoints {
copyofService := ingress . L4Service {
Port : service . Port ,
Backend : service . Backend ,
Endpoints : [ ] ingress . Endpoint { } ,
Service : nil ,
}
clearedTCPL4Services = append ( clearedTCPL4Services , copyofService )
}
for _ , service := range config . UDPEndpoints {
copyofService := ingress . L4Service {
Port : service . Port ,
Backend : service . Backend ,
Endpoints : [ ] ingress . Endpoint { } ,
Service : nil ,
}
clearedUDPL4Services = append ( clearedUDPL4Services , copyofService )
}
config . TCPEndpoints = clearedTCPL4Services
config . UDPEndpoints = clearedUDPL4Services
}
2018-06-13 18:15:45 +00:00
// IsDynamicConfigurationEnough returns whether a Configuration can be
// dynamically applied, without reloading the backend.
2018-04-01 20:09:27 +00:00
func ( n * NGINXController ) IsDynamicConfigurationEnough ( pcfg * ingress . Configuration ) bool {
2018-04-24 18:02:52 +00:00
copyOfRunningConfig := * n . runningConfig
copyOfPcfg := * pcfg
2018-03-18 13:13:41 +00:00
copyOfRunningConfig . Backends = [ ] * ingress . Backend { }
copyOfPcfg . Backends = [ ] * ingress . Backend { }
2019-01-24 14:03:33 +00:00
clearL4serviceEndpoints ( & copyOfRunningConfig )
clearL4serviceEndpoints ( & copyOfPcfg )
2018-11-27 14:53:51 +00:00
copyOfRunningConfig . ControllerPodsCount = 0
copyOfPcfg . ControllerPodsCount = 0
2018-03-18 13:13:41 +00:00
2018-06-04 21:48:30 +00:00
if n . cfg . DynamicCertificatesEnabled {
clearCertificates ( & copyOfRunningConfig )
clearCertificates ( & copyOfPcfg )
}
2018-03-18 13:13:41 +00:00
return copyOfRunningConfig . Equal ( & copyOfPcfg )
}
2018-06-13 18:15:45 +00:00
// configureDynamically encodes new Backends in JSON format and POSTs the
// payload to an internal HTTP endpoint handled by Lua.
2019-01-21 14:29:36 +00:00
func configureDynamically ( pcfg * ingress . Configuration , isDynamicCertificatesEnabled bool ) error {
2018-03-22 02:47:39 +00:00
backends := make ( [ ] * ingress . Backend , len ( pcfg . Backends ) )
for i , backend := range pcfg . Backends {
2018-08-03 13:50:53 +00:00
var service * apiv1 . Service
if backend . Service != nil {
service = & apiv1 . Service { Spec : backend . Service . Spec }
}
2018-04-24 18:02:52 +00:00
luaBackend := & ingress . Backend {
2018-09-18 18:05:32 +00:00
Name : backend . Name ,
Port : backend . Port ,
SSLPassthrough : backend . SSLPassthrough ,
SessionAffinity : backend . SessionAffinity ,
UpstreamHashBy : backend . UpstreamHashBy ,
LoadBalancing : backend . LoadBalancing ,
Service : service ,
NoServer : backend . NoServer ,
TrafficShapingPolicy : backend . TrafficShapingPolicy ,
AlternativeBackends : backend . AlternativeBackends ,
2018-04-24 18:02:52 +00:00
}
var endpoints [ ] ingress . Endpoint
for _ , endpoint := range backend . Endpoints {
endpoints = append ( endpoints , ingress . Endpoint {
2018-10-09 01:29:58 +00:00
Address : endpoint . Address ,
Port : endpoint . Port ,
2018-04-24 18:02:52 +00:00
} )
}
luaBackend . Endpoints = endpoints
backends [ i ] = luaBackend
2018-03-22 02:47:39 +00:00
}
2019-01-21 14:29:36 +00:00
statusCode , _ , err := nginx . NewPostStatusRequest ( "/configuration/backends" , "application/json" , backends )
2018-03-18 13:13:41 +00:00
if err != nil {
return err
}
2019-01-21 14:29:36 +00:00
if statusCode != http . StatusCreated {
return fmt . Errorf ( "unexpected error code: %d" , statusCode )
}
2018-11-16 20:33:56 +00:00
streams := make ( [ ] ingress . Backend , 0 )
2018-11-16 16:48:47 +00:00
for _ , ep := range pcfg . TCPEndpoints {
2019-01-02 17:32:57 +00:00
var service * apiv1 . Service
if ep . Service != nil {
service = & apiv1 . Service { Spec : ep . Service . Spec }
}
2018-11-16 16:48:47 +00:00
key := fmt . Sprintf ( "tcp-%v-%v-%v" , ep . Backend . Namespace , ep . Backend . Name , ep . Backend . Port . String ( ) )
2018-11-16 20:33:56 +00:00
streams = append ( streams , ingress . Backend {
2018-11-16 16:48:47 +00:00
Name : key ,
Endpoints : ep . Endpoints ,
Port : intstr . FromInt ( ep . Port ) ,
2019-01-02 17:32:57 +00:00
Service : service ,
2018-11-16 16:48:47 +00:00
} )
}
for _ , ep := range pcfg . UDPEndpoints {
2019-01-02 17:32:57 +00:00
var service * apiv1 . Service
if ep . Service != nil {
service = & apiv1 . Service { Spec : ep . Service . Spec }
}
2018-11-16 16:48:47 +00:00
key := fmt . Sprintf ( "udp-%v-%v-%v" , ep . Backend . Namespace , ep . Backend . Name , ep . Backend . Port . String ( ) )
2018-11-16 20:33:56 +00:00
streams = append ( streams , ingress . Backend {
2018-11-16 16:48:47 +00:00
Name : key ,
Endpoints : ep . Endpoints ,
Port : intstr . FromInt ( ep . Port ) ,
2019-01-02 17:32:57 +00:00
Service : service ,
2018-11-16 16:48:47 +00:00
} )
}
2018-11-16 20:52:46 +00:00
err = updateStreamConfiguration ( streams )
if err != nil {
return err
}
2019-01-21 14:29:36 +00:00
statusCode , _ , err = nginx . NewPostStatusRequest ( "/configuration/general" , "application/json" , ingress . GeneralConfig {
2018-11-27 14:53:51 +00:00
ControllerPodsCount : pcfg . ControllerPodsCount ,
} )
if err != nil {
return err
}
2019-01-21 14:29:36 +00:00
if statusCode != http . StatusCreated {
return fmt . Errorf ( "unexpected error code: %d" , statusCode )
}
2018-11-16 20:52:46 +00:00
if isDynamicCertificatesEnabled {
2019-01-21 14:29:36 +00:00
err = configureCertificates ( pcfg )
2018-11-16 20:52:46 +00:00
if err != nil {
return err
}
}
return nil
}
func updateStreamConfiguration ( streams [ ] ingress . Backend ) error {
2019-01-21 14:29:36 +00:00
conn , err := net . Dial ( "unix" , nginx . StreamSocket )
2018-11-16 20:52:46 +00:00
if err != nil {
return err
}
defer conn . Close ( )
2018-11-16 16:48:47 +00:00
buf , err := json . Marshal ( streams )
if err != nil {
return err
}
_ , err = conn . Write ( buf )
if err != nil {
return err
}
2018-11-16 20:33:56 +00:00
_ , err = fmt . Fprintf ( conn , "\r\n" )
if err != nil {
return err
}
2018-06-04 21:48:30 +00:00
return nil
}
// configureCertificates JSON encodes certificates and POSTs it to an internal HTTP endpoint
// that is handled by Lua
2019-01-21 14:29:36 +00:00
func configureCertificates ( pcfg * ingress . Configuration ) error {
2018-06-04 21:48:30 +00:00
var servers [ ] * ingress . Server
for _ , server := range pcfg . Servers {
servers = append ( servers , & ingress . Server {
Hostname : server . Hostname ,
SSLCert : ingress . SSLCert {
PemCertKey : server . SSLCert . PemCertKey ,
} ,
} )
}
2019-01-21 14:29:36 +00:00
statusCode , _ , err := nginx . NewPostStatusRequest ( "/configuration/servers" , "application/json" , servers )
2018-03-18 13:13:41 +00:00
if err != nil {
return err
}
2019-01-21 14:29:36 +00:00
if statusCode != http . StatusCreated {
return fmt . Errorf ( "unexpected error code: %d" , statusCode )
2018-03-18 13:13:41 +00:00
}
return nil
}
2018-06-21 22:15:18 +00:00
const zipkinTmpl = ` {
"service_name" : "{{ .ZipkinServiceName }}" ,
"collector_host" : "{{ .ZipkinCollectorHost }}" ,
2018-06-28 14:42:32 +00:00
"collector_port" : { { . ZipkinCollectorPort } } ,
"sample_rate" : { { . ZipkinSampleRate } }
2018-06-21 22:15:18 +00:00
} `
const jaegerTmpl = ` {
"service_name" : "{{ .JaegerServiceName }}" ,
"sampler" : {
"type" : "{{ .JaegerSamplerType }}" ,
"param" : { { . JaegerSamplerParam } }
} ,
"reporter" : {
"localAgentHostPort" : "{{ .JaegerCollectorHost }}:{{ .JaegerCollectorPort }}"
}
} `
2019-02-15 20:20:10 +00:00
const datadogTmpl = ` {
"service" : "{{ .DatadogServiceName }}" ,
"agent_host" : "{{ .DatadogCollectorHost }}" ,
"agent_port" : { { . DatadogCollectorPort } } ,
"operation_name_override" : "{{ .DatadogOperationNameOverride }}"
} `
2018-06-21 22:15:18 +00:00
func createOpentracingCfg ( cfg ngx_config . Configuration ) error {
var tmpl * template . Template
var err error
if cfg . ZipkinCollectorHost != "" {
tmpl , err = template . New ( "zipkin" ) . Parse ( zipkinTmpl )
if err != nil {
return err
}
} else if cfg . JaegerCollectorHost != "" {
2019-02-10 15:59:05 +00:00
tmpl , err = template . New ( "jaeger" ) . Parse ( jaegerTmpl )
2018-06-21 22:15:18 +00:00
if err != nil {
return err
}
2019-02-15 20:20:10 +00:00
} else if cfg . DatadogCollectorHost != "" {
tmpl , err = template . New ( "datadog" ) . Parse ( datadogTmpl )
if err != nil {
return err
}
2018-06-21 22:15:18 +00:00
} else {
tmpl , _ = template . New ( "empty" ) . Parse ( "{}" )
}
tmplBuf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
err = tmpl . Execute ( tmplBuf , cfg )
if err != nil {
return err
}
2019-02-10 15:59:05 +00:00
// Expand possible environment variables before writing the configuration to file.
expanded := os . ExpandEnv ( string ( tmplBuf . Bytes ( ) ) )
return ioutil . WriteFile ( "/etc/nginx/opentracing.json" , [ ] byte ( expanded ) , file . ReadWriteByUser )
2018-06-21 22:15:18 +00:00
}
2018-12-03 17:27:29 +00:00
func cleanTempNginxCfg ( ) error {
var files [ ] string
err := filepath . Walk ( os . TempDir ( ) , func ( path string , info os . FileInfo , err error ) error {
if info . IsDir ( ) && os . TempDir ( ) != path {
return filepath . SkipDir
}
dur , _ := time . ParseDuration ( "-5m" )
fiveMinutesAgo := time . Now ( ) . Add ( dur )
if strings . HasPrefix ( info . Name ( ) , tempNginxPattern ) && info . ModTime ( ) . Before ( fiveMinutesAgo ) {
files = append ( files , path )
}
return nil
} )
if err != nil {
return err
}
for _ , file := range files {
err := os . Remove ( file )
if err != nil {
return err
}
}
return nil
}
2019-01-09 03:33:16 +00:00
type redirect struct {
From string
To string
SSLCert ingress . SSLCert
}
func buildRedirects ( servers [ ] * ingress . Server ) [ ] * redirect {
names := sets . String { }
redirectServers := make ( [ ] * redirect , 0 )
for _ , srv := range servers {
if ! srv . RedirectFromToWWW {
continue
}
to := srv . Hostname
var from string
if strings . HasPrefix ( to , "www." ) {
from = strings . TrimPrefix ( to , "www." )
} else {
from = fmt . Sprintf ( "www.%v" , to )
}
if names . Has ( to ) {
continue
}
klog . V ( 3 ) . Infof ( "Creating redirect from %q to %q" , from , to )
found := false
for _ , esrv := range servers {
if esrv . Hostname == from {
found = true
break
}
}
if found {
klog . Warningf ( "Already exists an Ingress with %q hostname. Skipping creation of redirection from %q to %q." , from , from , to )
continue
}
r := & redirect {
From : from ,
To : to ,
}
if srv . SSLCert . PemSHA != "" {
if ssl . IsValidHostname ( from , srv . SSLCert . CN ) {
r . SSLCert = srv . SSLCert
} else {
klog . Warningf ( "the server %v has SSL configured but the SSL certificate does not contains a CN for %v. Redirects will not work for HTTPS to HTTPS" , from , to )
}
}
redirectServers = append ( redirectServers , r )
names . Insert ( to )
}
return redirectServers
}