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"
|
2020-12-02 14:23:39 +00:00
|
|
|
"crypto/tls"
|
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"
|
2023-11-03 13:46:32 +00:00
|
|
|
"io/fs"
|
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"
|
2019-08-15 18:57:51 +00:00
|
|
|
"reflect"
|
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"
|
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"
|
2022-07-20 21:43:39 +00:00
|
|
|
"k8s.io/ingress-nginx/pkg/tcpproxy"
|
2019-05-18 10:08:05 +00:00
|
|
|
|
2020-06-19 06:04:38 +00:00
|
|
|
adm_controller "k8s.io/ingress-nginx/internal/admission/controller"
|
2017-11-07 22:02:12 +00:00
|
|
|
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"
|
|
|
|
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"
|
2022-07-22 00:32:48 +00:00
|
|
|
"k8s.io/ingress-nginx/pkg/apis/ingress"
|
2022-07-20 18:53:44 +00:00
|
|
|
|
|
|
|
"k8s.io/ingress-nginx/pkg/util/file"
|
2022-08-21 21:21:51 +00:00
|
|
|
utilingress "k8s.io/ingress-nginx/pkg/util/ingress"
|
|
|
|
|
2022-07-20 18:53:44 +00:00
|
|
|
klog "k8s.io/klog/v2"
|
2016-11-29 01:39:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-01-21 14:29:36 +00:00
|
|
|
tempNginxPattern = "nginx-cfg"
|
2019-09-24 13:53:22 +00:00
|
|
|
emptyUID = "-1"
|
2016-11-10 22:56:29 +00:00
|
|
|
)
|
|
|
|
|
2017-10-06 20:33:32 +00:00
|
|
|
// NewNGINXController creates a new NGINX Ingress controller.
|
2019-08-13 21:14:55 +00:00
|
|
|
func NewNGINXController(config *Configuration, mc metric.Collector) *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
|
|
|
|
2019-08-22 14:25:47 +00:00
|
|
|
ngxErrCh: make(chan error),
|
|
|
|
|
2017-11-05 01:18:28 +00:00
|
|
|
stopLock: &sync.Mutex{},
|
2017-11-06 22:34:30 +00:00
|
|
|
|
2018-06-13 18:15:45 +00:00
|
|
|
runningConfig: new(ingress.Configuration),
|
2018-01-23 20:10:02 +00:00
|
|
|
|
2022-07-20 21:43:39 +00:00
|
|
|
Proxy: &tcpproxy.TCPProxy{},
|
2018-07-07 17:46:18 +00:00
|
|
|
|
|
|
|
metricCollector: mc,
|
2019-02-21 19:45:21 +00:00
|
|
|
|
|
|
|
command: NewNginxCommand(),
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.cfg.ValidationWebhook != "" {
|
|
|
|
n.validationWebhookServer = &http.Server{
|
2022-06-15 17:19:30 +00:00
|
|
|
Addr: config.ValidationWebhook,
|
2023-08-31 07:36:48 +00:00
|
|
|
// G112 (CWE-400): Potential Slowloris Attack
|
2022-06-15 17:19:30 +00:00
|
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
|
|
Handler: adm_controller.NewAdmissionControllerServer(&adm_controller.IngressAdmission{Checker: n}),
|
|
|
|
TLSConfig: ssl.NewTLSListener(n.cfg.ValidationWebhookCertPath, n.cfg.ValidationWebhookKeyPath).TLSConfig(),
|
2020-12-02 14:23:39 +00:00
|
|
|
// disable http/2
|
|
|
|
// https://github.com/kubernetes/kubernetes/issues/80313
|
|
|
|
// https://github.com/kubernetes/ingress-nginx/issues/6323#issuecomment-737239159
|
|
|
|
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
2019-02-21 19:45:21 +00:00
|
|
|
}
|
2017-04-09 23:51:38 +00:00
|
|
|
}
|
|
|
|
|
2018-01-25 13:46:20 +00:00
|
|
|
n.store = store.New(
|
2018-01-18 19:14:42 +00:00
|
|
|
config.Namespace,
|
2021-11-12 19:46:28 +00:00
|
|
|
config.WatchNamespaceSelector,
|
2018-01-18 19:14:42 +00:00
|
|
|
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,
|
2018-06-04 21:48:30 +00:00
|
|
|
n.updateCh,
|
2021-08-21 20:42:00 +00:00
|
|
|
config.DisableCatchAll,
|
2022-04-11 14:06:07 +00:00
|
|
|
config.DeepInspector,
|
2023-01-09 14:37:31 +00:00
|
|
|
config.IngressClassConfiguration,
|
|
|
|
config.DisableSyncEvents)
|
2017-11-07 16:36:51 +00:00
|
|
|
|
2017-11-05 01:18:28 +00:00
|
|
|
n.syncQueue = task.NewTaskQueue(n.syncIngress)
|
2019-03-08 00:20:34 +00:00
|
|
|
|
2017-11-05 01:18:28 +00:00
|
|
|
if config.UpdateStatus {
|
2020-09-26 23:27:19 +00:00
|
|
|
n.syncStatus = status.NewStatusSyncer(status.Config{
|
2017-11-05 01:18:28 +00:00
|
|
|
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
|
|
|
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() {
|
2019-08-13 21:14:55 +00:00
|
|
|
template, err := ngx_template.NewTemplate(nginx.TemplatePath)
|
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
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.ErrorS(err, "Error loading new template")
|
2016-11-10 22:56:29 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
n.t = template
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("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
|
|
|
}
|
|
|
|
|
2019-08-13 21:14:55 +00:00
|
|
|
ngxTpl, err := ngx_template.NewTemplate(nginx.TemplatePath)
|
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
|
|
|
|
2022-07-20 21:15:03 +00:00
|
|
|
_, err = file.NewFileWatcher(nginx.TemplatePath, onTemplateChange)
|
2018-06-16 20:22:59 +00:00
|
|
|
if err != nil {
|
2019-08-13 21:14:55 +00:00
|
|
|
klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, 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{}
|
2023-11-03 13:46:32 +00:00
|
|
|
|
|
|
|
if err := os.Mkdir("/etc/ingress-controller/geoip/", 0o755); err != nil && !os.IsExist(err) {
|
|
|
|
klog.Fatalf("Error creating geoip dir: %v", err)
|
|
|
|
}
|
|
|
|
err = filepath.WalkDir("/etc/ingress-controller/geoip/", func(path string, info fs.DirEntry, 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 {
|
2022-07-20 21:15:03 +00:00
|
|
|
_, err = file.NewFileWatcher(f, func() {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("File changed detected. Reloading NGINX", "path", 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
|
|
|
|
|
2019-03-08 00:20:34 +00:00
|
|
|
syncStatus status.Syncer
|
2017-11-05 01:18:28 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2021-08-12 18:13:50 +00:00
|
|
|
t ngx_template.Writer
|
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
|
|
|
|
|
2022-07-20 21:43:39 +00:00
|
|
|
Proxy *tcpproxy.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
|
|
|
|
2023-07-03 12:50:52 +00:00
|
|
|
metricCollector metric.Collector
|
2019-03-11 16:31:38 +00:00
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
validationWebhookServer *http.Server
|
|
|
|
|
|
|
|
command NginxExecTester
|
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() {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("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
|
|
|
|
2019-03-11 15:57:28 +00:00
|
|
|
// we need to use the defined ingress class to allow multiple leaders
|
|
|
|
// in order to update information about ingress status
|
2021-08-21 20:42:00 +00:00
|
|
|
// TODO: For now, as the the IngressClass logics has changed, is up to the
|
|
|
|
// cluster admin to create different Leader Election IDs.
|
|
|
|
// Should revisit this in a future
|
|
|
|
electionID := n.cfg.ElectionID
|
2019-03-11 15:57:28 +00:00
|
|
|
|
2019-03-08 00:20:34 +00:00
|
|
|
setupLeaderElection(&leaderElectionConfig{
|
|
|
|
Client: n.cfg.Client,
|
2019-03-11 15:57:28 +00:00
|
|
|
ElectionID: electionID,
|
2019-03-08 00:20:34 +00:00
|
|
|
OnStartedLeading: func(stopCh chan struct{}) {
|
|
|
|
if n.syncStatus != nil {
|
|
|
|
go n.syncStatus.Run(stopCh)
|
|
|
|
}
|
2019-03-10 22:12:33 +00:00
|
|
|
|
2019-03-11 15:57:28 +00:00
|
|
|
n.metricCollector.OnStartedLeading(electionID)
|
2019-03-11 16:31:38 +00:00
|
|
|
// manually update SSL expiration metrics
|
|
|
|
// (to not wait for a reload)
|
|
|
|
n.metricCollector.SetSSLExpireTime(n.runningConfig.Servers)
|
Add a certificate info metric (#8253)
When the ingress controller loads certificates (new ones or following a
secret update), it performs a series of check to ensure its validity.
In our systems, we detected a case where, when the secret object is
compromised, for example when the certificate does not match the secret
key, different pods of the ingress controller are serving a different
version of the certificate.
This behaviour is due to the cache mechanism of the ingress controller,
keeping the last known certificate in case of corruption. When this
happens, old ingress-controller pods will keep serving the old one,
while new pods, by failing to load the corrupted certificates, would
use the default certificate, causing invalid certificates for its
clients.
This generates a random error on the client side, depending on the
actual pod instance it reaches.
In order to allow detecting occurences of those situations, add a metric
to expose, for all ingress controlller pods, detailed informations of
the currently loaded certificate.
This will, for example, allow setting an alert when there is a
certificate discrepency across all ingress controller pods using a query
similar to `sum(nginx_ingress_controller_ssl_certificate_info{host="name.tld"})by(serial_number)`
This also allows to catch other exceptions loading certificates (failing
to load the certificate from the k8s API, ...
Co-authored-by: Daniel Ricart <danielricart@users.noreply.github.com>
Co-authored-by: Daniel Ricart <danielricart@users.noreply.github.com>
2022-02-24 15:08:32 +00:00
|
|
|
n.metricCollector.SetSSLInfo(n.runningConfig.Servers)
|
2019-03-08 00:20:34 +00:00
|
|
|
},
|
|
|
|
OnStoppedLeading: func() {
|
2019-03-11 15:57:28 +00:00
|
|
|
n.metricCollector.OnStoppedLeading(electionID)
|
2019-03-08 00:20:34 +00:00
|
|
|
},
|
|
|
|
})
|
2017-11-05 01:18:28 +00:00
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
cmd := n.command.ExecCommand()
|
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()
|
|
|
|
}
|
|
|
|
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("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 {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.ErrorS(err, "Unexpected error removing temporal configuration files")
|
2018-12-03 17:27:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
if n.validationWebhookServer != nil {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("Starting validation webhook", "address", n.validationWebhookServer.Addr,
|
|
|
|
"certPath", n.cfg.ValidationWebhookCertPath, "keyPath", n.cfg.ValidationWebhookKeyPath)
|
2019-02-21 19:45:21 +00:00
|
|
|
go func() {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.ErrorS(n.validationWebhookServer.ListenAndServeTLS("", ""), "Error listening for TLS connections")
|
2019-02-21 19:45:21 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
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 {
|
2020-01-25 17:52:31 +00:00
|
|
|
return
|
2016-11-29 01:39:17 +00:00
|
|
|
}
|
2017-09-28 14:52:12 +00:00
|
|
|
|
2020-06-23 20:07:48 +00:00
|
|
|
// if the nginx master process dies, the workers continue to process requests
|
|
|
|
// until the failure of the configured livenessProbe and restart of the pod.
|
2017-11-05 01:18:28 +00:00
|
|
|
if process.IsRespawnIfRequired(err) {
|
2020-05-31 16:54:51 +00:00
|
|
|
return
|
2017-09-28 14:52:12 +00:00
|
|
|
}
|
2020-05-31 16:54:51 +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
|
|
|
|
}
|
2020-01-25 17:52:31 +00:00
|
|
|
|
2018-02-14 01:46:18 +00:00
|
|
|
if evt, ok := event.(store.Event); ok {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Event received", "type", evt.Type, "object", 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:
|
2020-01-25 17:52:31 +00:00
|
|
|
return
|
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")
|
|
|
|
}
|
|
|
|
|
2020-07-06 19:05:16 +00:00
|
|
|
time.Sleep(time.Duration(n.cfg.ShutdownGracePeriod) * time.Second)
|
|
|
|
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("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
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
if n.validationWebhookServer != nil {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("Stopping admission controller")
|
2019-02-21 19:45:21 +00:00
|
|
|
err := n.validationWebhookServer.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-12 13:04:26 +00:00
|
|
|
// send stop signal to NGINX
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("Stopping NGINX process")
|
2019-02-21 19:45:21 +00:00
|
|
|
cmd := n.command.ExecCommand("-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 {
|
2019-08-31 00:18:11 +00:00
|
|
|
if !nginx.IsRunning() {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("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.
|
2023-08-31 07:36:48 +00:00
|
|
|
func (n *NGINXController) DefaultEndpoint() ingress.Endpoint {
|
2017-08-24 13:33:26 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
// generateTemplate returns the nginx configuration file content
|
2023-08-31 07:36:48 +00:00
|
|
|
//
|
|
|
|
//nolint:gocritic // the cfg shouldn't be changed, and shouldn't be mutated by other processes while being rendered.
|
|
|
|
func (n *NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressCfg ingress.Configuration) ([]byte, error) {
|
2018-01-23 20:10:02 +00:00
|
|
|
if n.cfg.EnableSSLPassthrough {
|
2022-07-20 21:43:39 +00:00
|
|
|
servers := []*tcpproxy.TCPServer{}
|
2018-01-23 20:10:02 +00:00
|
|
|
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
|
|
|
}
|
2020-12-04 12:40:42 +00:00
|
|
|
port, err := strconv.Atoi(pb.Port.String()) // #nosec
|
2018-01-23 20:10:02 +00:00
|
|
|
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 {
|
2023-08-31 07:36:48 +00:00
|
|
|
//nolint:gosec // Ignore G109 error
|
2018-01-23 20:10:02 +00:00
|
|
|
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
|
2022-07-20 21:43:39 +00:00
|
|
|
servers = append(servers, &tcpproxy.TCPServer{
|
2018-01-23 20:10:02 +00:00
|
|
|
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 {
|
2020-01-31 16:01:28 +00:00
|
|
|
hostnameLength := len(srv.Hostname)
|
|
|
|
if srv.RedirectFromToWWW {
|
|
|
|
hostnameLength += 4
|
2017-08-16 07:02:30 +00:00
|
|
|
}
|
2020-01-31 16:01:28 +00:00
|
|
|
if longestName < hostnameLength {
|
|
|
|
longestName = hostnameLength
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range srv.Aliases {
|
|
|
|
if longestName < len(alias) {
|
|
|
|
longestName = len(alias)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
serverNameBytes += hostnameLength
|
2017-08-16 07:02:30 +00:00
|
|
|
}
|
2019-01-09 03:33:16 +00:00
|
|
|
|
2020-01-31 16:01:28 +00:00
|
|
|
nameHashBucketSize := nginxHashBucketSize(longestName)
|
|
|
|
if cfg.ServerNameHashBucketSize < nameHashBucketSize {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Adjusting ServerNameHashBucketSize variable", "value", 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 {
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Adjusting ServerNameHashMaxSize variable", "value", 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
|
2021-08-21 20:42:00 +00:00
|
|
|
maxOpenFiles := rlimitMaxNumFiles() - 1024
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Maximum number of open file descriptors", "value", maxOpenFiles)
|
2018-12-27 16:24:09 +00:00
|
|
|
if maxOpenFiles < 1024 {
|
|
|
|
// this means the value of RLIMIT_NOFILE is too low.
|
|
|
|
maxOpenFiles = 1024
|
|
|
|
}
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Adjusting MaxWorkerOpenFiles variable", "value", maxOpenFiles)
|
2018-12-27 16:24:09 +00:00
|
|
|
cfg.MaxWorkerOpenFiles = maxOpenFiles
|
2017-03-28 16:39:44 +00:00
|
|
|
}
|
2018-12-27 16:24:09 +00:00
|
|
|
|
|
|
|
if cfg.MaxWorkerConnections == 0 {
|
2019-07-08 20:10:38 +00:00
|
|
|
maxWorkerConnections := int(float64(cfg.MaxWorkerOpenFiles * 3.0 / 4))
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Adjusting MaxWorkerConnections variable", "value", maxWorkerConnections)
|
2019-01-15 20:38:30 +00:00
|
|
|
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)
|
2019-04-02 20:39:42 +00:00
|
|
|
} else {
|
|
|
|
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)
|
2019-04-02 20:39:42 +00:00
|
|
|
} else {
|
|
|
|
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)
|
2019-03-05 19:31:33 +00:00
|
|
|
} else {
|
2023-08-31 07:36:48 +00:00
|
|
|
nsSecName := strings.ReplaceAll(secretName, "/", "-")
|
2019-03-05 19:31:33 +00:00
|
|
|
dh, ok := secret.Data["dhparam.pem"]
|
|
|
|
if ok {
|
2019-08-13 21:14:55 +00:00
|
|
|
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh)
|
2019-03-05 19:31:33 +00:00
|
|
|
if err != nil {
|
|
|
|
klog.Warningf("Error adding or updating dhparam file %v: %v", nsSecName, err)
|
|
|
|
} else {
|
|
|
|
sslDHParam = pemFileName
|
|
|
|
}
|
2017-03-08 13:41:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.SSLDHParam = sslDHParam
|
|
|
|
|
2019-08-13 21:14:55 +00:00
|
|
|
cfg.DefaultSSLCertificate = n.getDefaultSSLCertificate()
|
|
|
|
|
2022-04-09 04:48:04 +00:00
|
|
|
if n.cfg.IsChroot {
|
|
|
|
if cfg.AccessLogPath == "/var/log/nginx/access.log" {
|
|
|
|
cfg.AccessLogPath = fmt.Sprintf("syslog:server=%s", n.cfg.InternalLoggerAddress)
|
|
|
|
}
|
|
|
|
if cfg.ErrorLogPath == "/var/log/nginx/error.log" {
|
|
|
|
cfg.ErrorLogPath = fmt.Sprintf("syslog:server=%s", n.cfg.InternalLoggerAddress)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
tc := &ngx_config.TemplateConfig{
|
2019-08-13 21:14:55 +00:00
|
|
|
ProxySetHeaders: setHeaders,
|
|
|
|
AddHeaders: addHeaders,
|
|
|
|
BacklogSize: sysctlSomaxconn(),
|
|
|
|
Backends: ingressCfg.Backends,
|
|
|
|
PassthroughBackends: ingressCfg.PassthroughBackends,
|
|
|
|
Servers: ingressCfg.Servers,
|
|
|
|
TCPBackends: ingressCfg.TCPEndpoints,
|
|
|
|
UDPBackends: ingressCfg.UDPEndpoints,
|
|
|
|
Cfg: cfg,
|
|
|
|
IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6,
|
|
|
|
NginxStatusIpv4Whitelist: cfg.NginxStatusIpv4Whitelist,
|
|
|
|
NginxStatusIpv6Whitelist: cfg.NginxStatusIpv6Whitelist,
|
2022-08-21 21:21:51 +00:00
|
|
|
RedirectServers: utilingress.BuildRedirects(ingressCfg.Servers),
|
2019-08-13 21:14:55 +00:00
|
|
|
IsSSLPassthroughEnabled: n.cfg.EnableSSLPassthrough,
|
|
|
|
ListenPorts: n.cfg.ListenPorts,
|
|
|
|
EnableMetrics: n.cfg.EnableMetrics,
|
2020-03-16 07:26:33 +00:00
|
|
|
MaxmindEditionFiles: n.cfg.MaxmindEditionFiles,
|
|
|
|
HealthzURI: nginx.HealthPath,
|
2020-06-20 06:58:14 +00:00
|
|
|
MonitorMaxBatchSize: n.cfg.MonitorMaxBatchSize,
|
2020-03-16 07:26:33 +00:00
|
|
|
PID: nginx.PID,
|
|
|
|
StatusPath: nginx.StatusPath,
|
|
|
|
StatusPort: nginx.StatusPort,
|
|
|
|
StreamPort: nginx.StreamPort,
|
2021-12-23 19:46:30 +00:00
|
|
|
StreamSnippets: append(ingressCfg.StreamSnippets, cfg.StreamSnippet),
|
2017-08-03 14:51:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-07 17:46:18 +00:00
|
|
|
tc.Cfg.Checksum = ingressCfg.ConfigurationChecksum
|
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
return n.t.Write(tc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// testTemplate checks if the NGINX configuration inside the byte array is valid
|
|
|
|
// running the command "nginx -t" using a temporal file.
|
2023-08-31 07:36:48 +00:00
|
|
|
func (n *NGINXController) testTemplate(cfg []byte) error {
|
2019-02-21 19:45:21 +00:00
|
|
|
if len(cfg) == 0 {
|
|
|
|
return fmt.Errorf("invalid NGINX configuration (empty)")
|
|
|
|
}
|
2022-04-09 04:48:04 +00:00
|
|
|
tmpDir := os.TempDir() + "/nginx"
|
|
|
|
tmpfile, err := os.CreateTemp(tmpDir, tempNginxPattern)
|
2019-02-21 19:45:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer tmpfile.Close()
|
2021-08-06 14:18:17 +00:00
|
|
|
err = os.WriteFile(tmpfile.Name(), cfg, file.ReadWriteByUser)
|
2019-02-21 19:45:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
out, err := n.command.Test(tmpfile.Name())
|
|
|
|
if err != nil {
|
|
|
|
// this error is different from the rest because it must be clear why nginx is not working
|
|
|
|
oe := fmt.Sprintf(`
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
Error: %v
|
|
|
|
%v
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
`, err, string(out))
|
|
|
|
|
|
|
|
return errors.New(oe)
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Remove(tmpfile.Name())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2023-08-31 07:36:48 +00:00
|
|
|
//
|
|
|
|
//nolint:gocritic // the cfg shouldn't be changed, and shouldn't be mutated by other processes while being rendered.
|
2019-02-21 19:45:21 +00:00
|
|
|
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
|
|
|
|
cfg := n.store.GetBackendConfiguration()
|
|
|
|
cfg.Resolver = n.resolver
|
|
|
|
|
|
|
|
content, err := n.generateTemplate(cfg, ingressCfg)
|
2017-06-11 19:56:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
err = createOpentracingCfg(&cfg)
|
2020-01-29 15:20:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-06-21 22:15:18 +00:00
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
err = createOpentelemetryCfg(&cfg)
|
2023-03-22 18:58:22 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-11 19:56:40 +00:00
|
|
|
err = n.testTemplate(content)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-08 23:31:02 +00:00
|
|
|
if klog.V(2).Enabled() {
|
2023-08-31 07:36:48 +00:00
|
|
|
src, err := os.ReadFile(cfgPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-05 01:18:28 +00:00
|
|
|
if !bytes.Equal(src, content) {
|
2021-08-06 14:18:17 +00:00
|
|
|
tmpfile, err := os.CreateTemp("", "new-nginx-cfg")
|
2017-11-05 01:18:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer tmpfile.Close()
|
2021-08-06 14:18:17 +00:00
|
|
|
err = os.WriteFile(tmpfile.Name(), content, file.ReadWriteByUser)
|
2017-11-05 01:18:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-08-31 07:36:48 +00:00
|
|
|
//nolint:gosec //Ignore G204 error
|
2020-02-21 19:41:03 +00:00
|
|
|
diffOutput, err := exec.Command("diff", "-I", "'# Configuration.*'", "-u", cfgPath, tmpfile.Name()).CombinedOutput()
|
2018-12-12 01:08:02 +00:00
|
|
|
if err != nil {
|
2019-08-08 16:53:23 +00:00
|
|
|
if exitError, ok := err.(*exec.ExitError); ok {
|
2023-08-31 07:36:48 +00:00
|
|
|
ws, ok := exitError.Sys().(syscall.WaitStatus)
|
|
|
|
if !ok {
|
|
|
|
klog.Errorf("unexpected type: %T", exitError.Sys())
|
|
|
|
}
|
2019-08-08 16:53:23 +00:00
|
|
|
if ws.ExitStatus() == 2 {
|
|
|
|
klog.Warningf("Failed to executing diff command: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2018-12-12 01:08:02 +00:00
|
|
|
}
|
2017-11-05 01:18:28 +00:00
|
|
|
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("NGINX configuration change", "diff", 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
|
|
|
|
2021-08-06 14:18:17 +00:00
|
|
|
err = os.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
|
|
|
}
|
|
|
|
|
2019-02-21 19:45:21 +00:00
|
|
|
o, err := n.command.ExecCommand("-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
|
|
|
|
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.InfoS("Starting TLS proxy for SSL Passthrough")
|
2022-07-20 21:43:39 +00:00
|
|
|
n.Proxy = &tcpproxy.TCPProxy{
|
|
|
|
Default: &tcpproxy.TCPServer{
|
2018-01-18 19:14:42 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-09-27 20:32:40 +00:00
|
|
|
klog.V(3).InfoS("Handling TCP connection", "remote", conn.RemoteAddr(), "local", 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-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-08-15 18:57:51 +00:00
|
|
|
func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) error {
|
|
|
|
backendsChanged := !reflect.DeepEqual(n.runningConfig.Backends, pcfg.Backends)
|
|
|
|
if backendsChanged {
|
|
|
|
err := configureBackends(pcfg.Backends)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-04-24 18:02:52 +00:00
|
|
|
}
|
2019-08-15 18:57:51 +00:00
|
|
|
}
|
2018-04-24 18:02:52 +00:00
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints)
|
|
|
|
if streamConfigurationChanged {
|
|
|
|
err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-04-24 18:02:52 +00:00
|
|
|
}
|
2018-03-22 02:47:39 +00:00
|
|
|
}
|
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
serversChanged := !reflect.DeepEqual(n.runningConfig.Servers, pcfg.Servers)
|
|
|
|
if serversChanged {
|
|
|
|
err := configureCertificates(pcfg.Servers)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-21 14:29:36 +00:00
|
|
|
}
|
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
func updateStreamConfiguration(tcpEndpoints, udpEndpoints []ingress.L4Service) error {
|
2018-11-16 20:33:56 +00:00
|
|
|
streams := make([]ingress.Backend, 0)
|
2023-08-31 07:36:48 +00:00
|
|
|
for i := range tcpEndpoints {
|
|
|
|
ep := &tcpEndpoints[i]
|
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
|
|
|
})
|
|
|
|
}
|
2023-08-31 07:36:48 +00:00
|
|
|
for i := range udpEndpoints {
|
|
|
|
ep := &udpEndpoints[i]
|
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
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-09-08 21:14:54 +00:00
|
|
|
buf, err := json.Marshal(streams)
|
2018-11-16 20:52:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-03 02:01:13 +00:00
|
|
|
hostPort := net.JoinHostPort("127.0.0.1", fmt.Sprintf("%v", nginx.StreamPort))
|
|
|
|
conn, err := net.Dial("tcp", hostPort)
|
2018-11-27 14:53:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-09-08 21:14:54 +00:00
|
|
|
defer conn.Close()
|
2018-11-27 14:53:51 +00:00
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
_, err = conn.Write(buf)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-01-21 14:29:36 +00:00
|
|
|
}
|
2019-08-15 18:57:51 +00:00
|
|
|
_, err = fmt.Fprintf(conn, "\r\n")
|
2019-08-13 21:14:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-11-16 20:52:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
func configureBackends(rawBackends []*ingress.Backend) error {
|
|
|
|
backends := make([]*ingress.Backend, len(rawBackends))
|
2018-11-16 20:52:46 +00:00
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
for i, backend := range rawBackends {
|
|
|
|
var service *apiv1.Service
|
|
|
|
if backend.Service != nil {
|
|
|
|
service = &apiv1.Service{Spec: backend.Service.Spec}
|
|
|
|
}
|
|
|
|
luaBackend := &ingress.Backend{
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
var endpoints []ingress.Endpoint
|
|
|
|
for _, endpoint := range backend.Endpoints {
|
|
|
|
endpoints = append(endpoints, ingress.Endpoint{
|
|
|
|
Address: endpoint.Address,
|
|
|
|
Port: endpoint.Port,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
luaBackend.Endpoints = endpoints
|
|
|
|
backends[i] = luaBackend
|
2018-11-16 16:48:47 +00:00
|
|
|
}
|
|
|
|
|
2019-08-15 18:57:51 +00:00
|
|
|
statusCode, _, err := nginx.NewPostStatusRequest("/configuration/backends", "application/json", backends)
|
2018-11-16 16:48:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-15 18:57:51 +00:00
|
|
|
|
|
|
|
if statusCode != http.StatusCreated {
|
|
|
|
return fmt.Errorf("unexpected error code: %d", statusCode)
|
2018-11-16 20:33:56 +00:00
|
|
|
}
|
2018-06-04 21:48:30 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-26 14:58:44 +00:00
|
|
|
type sslConfiguration struct {
|
|
|
|
Certificates map[string]string `json:"certificates"`
|
|
|
|
Servers map[string]string `json:"servers"`
|
|
|
|
}
|
|
|
|
|
2018-06-04 21:48:30 +00:00
|
|
|
// configureCertificates JSON encodes certificates and POSTs it to an internal HTTP endpoint
|
|
|
|
// that is handled by Lua
|
2019-08-15 18:57:51 +00:00
|
|
|
func configureCertificates(rawServers []*ingress.Server) error {
|
2019-08-26 14:58:44 +00:00
|
|
|
configuration := &sslConfiguration{
|
|
|
|
Certificates: map[string]string{},
|
|
|
|
Servers: map[string]string{},
|
|
|
|
}
|
2018-06-04 21:48:30 +00:00
|
|
|
|
2019-09-24 13:53:22 +00:00
|
|
|
configure := func(hostname string, sslCert *ingress.SSLCert) {
|
|
|
|
uid := emptyUID
|
2019-06-25 11:49:00 +00:00
|
|
|
|
2019-09-24 13:53:22 +00:00
|
|
|
if sslCert != nil {
|
|
|
|
uid = sslCert.UID
|
2019-03-26 17:24:47 +00:00
|
|
|
|
2019-09-24 13:53:22 +00:00
|
|
|
if _, ok := configuration.Certificates[uid]; !ok {
|
|
|
|
configuration.Certificates[uid] = sslCert.PemCertKey
|
|
|
|
}
|
2019-08-26 14:58:44 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 13:53:22 +00:00
|
|
|
configuration.Servers[hostname] = uid
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rawServer := range rawServers {
|
|
|
|
configure(rawServer.Hostname, rawServer.SSLCert)
|
2019-08-26 14:58:44 +00:00
|
|
|
|
|
|
|
for _, alias := range rawServer.Aliases {
|
2019-09-24 13:53:22 +00:00
|
|
|
if rawServer.SSLCert != nil && ssl.IsValidHostname(alias, rawServer.SSLCert.CN) {
|
|
|
|
configuration.Servers[alias] = rawServer.SSLCert.UID
|
|
|
|
} else {
|
|
|
|
configuration.Servers[alias] = emptyUID
|
2019-08-26 14:58:44 +00:00
|
|
|
}
|
2019-03-26 17:24:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-21 21:21:51 +00:00
|
|
|
redirects := utilingress.BuildRedirects(rawServers)
|
2019-03-26 17:24:47 +00:00
|
|
|
for _, redirect := range redirects {
|
2019-09-24 13:53:22 +00:00
|
|
|
configure(redirect.From, redirect.SSLCert)
|
2018-06-04 21:48:30 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 14:58:44 +00:00
|
|
|
statusCode, _, err := nginx.NewPostStatusRequest("/configuration/servers", "application/json", configuration)
|
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 }}",
|
2021-03-23 23:43:34 +00:00
|
|
|
"propagation_format": "{{ .JaegerPropagationFormat }}",
|
2018-06-21 22:15:18 +00:00
|
|
|
"sampler": {
|
|
|
|
"type": "{{ .JaegerSamplerType }}",
|
2019-05-21 03:06:41 +00:00
|
|
|
"param": {{ .JaegerSamplerParam }},
|
|
|
|
"samplingServerURL": "{{ .JaegerSamplerHost }}:{{ .JaegerSamplerPort }}/sampling"
|
2018-06-21 22:15:18 +00:00
|
|
|
},
|
|
|
|
"reporter": {
|
2021-02-18 19:40:04 +00:00
|
|
|
"endpoint": "{{ .JaegerEndpoint }}",
|
2018-06-21 22:15:18 +00:00
|
|
|
"localAgentHostPort": "{{ .JaegerCollectorHost }}:{{ .JaegerCollectorPort }}"
|
2019-09-17 09:35:53 +00:00
|
|
|
},
|
|
|
|
"headers": {
|
|
|
|
"TraceContextHeaderName": "{{ .JaegerTraceContextHeaderName }}",
|
|
|
|
"jaegerDebugHeader": "{{ .JaegerDebugHeader }}",
|
|
|
|
"jaegerBaggageHeader": "{{ .JaegerBaggageHeader }}",
|
|
|
|
"traceBaggageHeaderPrefix": "{{ .JaegerTraceBaggageHeaderPrefix }}"
|
2021-02-01 15:52:02 +00:00
|
|
|
}
|
2018-06-21 22:15:18 +00:00
|
|
|
}`
|
|
|
|
|
2023-03-22 18:58:22 +00:00
|
|
|
const otelTmpl = `
|
|
|
|
exporter = "otlp"
|
|
|
|
processor = "batch"
|
|
|
|
|
|
|
|
[exporters.otlp]
|
|
|
|
# Alternatively the OTEL_EXPORTER_OTLP_ENDPOINT environment variable can also be used.
|
|
|
|
host = "{{ .OtlpCollectorHost }}"
|
|
|
|
port = {{ .OtlpCollectorPort }}
|
|
|
|
|
|
|
|
[processors.batch]
|
|
|
|
max_queue_size = {{ .OtelMaxQueueSize }}
|
|
|
|
schedule_delay_millis = {{ .OtelScheduleDelayMillis }}
|
|
|
|
max_export_batch_size = {{ .OtelMaxExportBatchSize }}
|
|
|
|
|
|
|
|
[service]
|
|
|
|
name = "{{ .OtelServiceName }}" # Opentelemetry resource name
|
|
|
|
|
|
|
|
[sampler]
|
|
|
|
name = "{{ .OtelSampler }}" # Also: AlwaysOff, TraceIdRatioBased
|
|
|
|
ratio = {{ .OtelSamplerRatio }}
|
|
|
|
parent_based = {{ .OtelSamplerParentBased }}
|
|
|
|
`
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
func datadogOpentracingCfg(cfg *ngx_config.Configuration) (string, error) {
|
2023-07-06 23:51:04 +00:00
|
|
|
m := map[string]interface{}{
|
|
|
|
"service": cfg.DatadogServiceName,
|
|
|
|
"agent_host": cfg.DatadogCollectorHost,
|
|
|
|
"agent_port": cfg.DatadogCollectorPort,
|
|
|
|
"environment": cfg.DatadogEnvironment,
|
|
|
|
"operation_name_override": cfg.DatadogOperationNameOverride,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Omit "sample_rate" if the configuration's sample rate is unset (nil).
|
|
|
|
// Omitting "sample_rate" from the plugin JSON indicates to the tracer that
|
|
|
|
// it should use dynamic rates instead of a configured rate.
|
|
|
|
if cfg.DatadogSampleRate != nil {
|
|
|
|
m["sample_rate"] = *cfg.DatadogSampleRate
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := json.Marshal(m)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(buf), nil
|
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
func opentracingCfgFromTemplate(cfg *ngx_config.Configuration, tmplName, tmplText string) (string, error) {
|
2023-07-06 23:51:04 +00:00
|
|
|
tmpl, err := template.New(tmplName).Parse(tmplText)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
tmplBuf := bytes.NewBuffer(make([]byte, 0))
|
|
|
|
err = tmpl.Execute(tmplBuf, cfg)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmplBuf.String(), nil
|
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
func createOpentracingCfg(cfg *ngx_config.Configuration) error {
|
2023-07-06 23:51:04 +00:00
|
|
|
var configData string
|
2018-06-21 22:15:18 +00:00
|
|
|
var err error
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
switch {
|
|
|
|
case cfg.ZipkinCollectorHost != "":
|
2023-07-06 23:51:04 +00:00
|
|
|
configData, err = opentracingCfgFromTemplate(cfg, "zipkin", zipkinTmpl)
|
2023-08-31 07:36:48 +00:00
|
|
|
case cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "":
|
2023-07-06 23:51:04 +00:00
|
|
|
configData, err = opentracingCfgFromTemplate(cfg, "jaeger", jaegerTmpl)
|
2023-08-31 07:36:48 +00:00
|
|
|
case cfg.DatadogCollectorHost != "":
|
2023-07-06 23:51:04 +00:00
|
|
|
configData, err = datadogOpentracingCfg(cfg)
|
2023-08-31 07:36:48 +00:00
|
|
|
default:
|
2023-07-06 23:51:04 +00:00
|
|
|
configData = "{}"
|
2018-06-21 22:15:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-02-10 15:59:05 +00:00
|
|
|
// Expand possible environment variables before writing the configuration to file.
|
2023-07-06 23:51:04 +00:00
|
|
|
expanded := os.ExpandEnv(configData)
|
2019-02-10 15:59:05 +00:00
|
|
|
|
2023-11-03 13:46:32 +00:00
|
|
|
return os.WriteFile("/etc/ingress-controller/telemetry/opentracing.json", []byte(expanded), file.ReadWriteByUser)
|
2018-06-21 22:15:18 +00:00
|
|
|
}
|
2018-12-03 17:27:29 +00:00
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
func createOpentelemetryCfg(cfg *ngx_config.Configuration) error {
|
2023-03-22 18:58:22 +00:00
|
|
|
tmpl, err := template.New("otel").Parse(otelTmpl)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tmplBuf := bytes.NewBuffer(make([]byte, 0))
|
|
|
|
err = tmpl.Execute(tmplBuf, cfg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.WriteFile(cfg.OpentelemetryConfig, tmplBuf.Bytes(), file.ReadWriteByUser)
|
|
|
|
}
|
|
|
|
|
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 {
|
2021-01-21 02:17:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-03 17:27:29 +00:00
|
|
|
if info.IsDir() && os.TempDir() != path {
|
|
|
|
return filepath.SkipDir
|
|
|
|
}
|
|
|
|
|
2023-08-31 07:36:48 +00:00
|
|
|
dur, err := time.ParseDuration("-5m")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-03 17:27:29 +00:00
|
|
|
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
|
|
|
|
}
|