Improve event handling using a workqueue

This commit is contained in:
Manuel de Brito Fontes 2016-03-22 15:01:04 -03:00
parent f5892e06fe
commit 13c21386e2
18 changed files with 1384 additions and 206 deletions

View file

@ -54,7 +54,9 @@ func (ngx *Manager) Start() {
// shut down, stop accepting new connections and continue to service current requests
// until all such requests are serviced. After that, the old worker processes exit.
// http://nginx.org/en/docs/beginners_guide.html#control
func (ngx *Manager) CheckAndReload(cfg *nginxConfiguration, ingressCfg IngressConfig) {
func (ngx *Manager) CheckAndReload(cfg nginxConfiguration, ingressCfg IngressConfig) {
ngx.reloadRateLimiter.Accept()
ngx.reloadLock.Lock()
defer ngx.reloadLock.Unlock()

View file

@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/util"
)
const (
@ -42,7 +43,7 @@ const (
// http://nginx.org/en/docs/ngx_core_module.html#error_log
// Configures logging level [debug | info | notice | warn | error | crit | alert | emerg]
// Log levels above are listed in the order of increasing severity
errorLevel = "info"
errorLevel = "notice"
// HTTP Strict Transport Security (often abbreviated as HSTS) is a security feature (HTTP header)
// that tell browsers that it should only be communicated with using HTTPS, instead of using HTTP.
@ -84,135 +85,137 @@ const (
type nginxConfiguration struct {
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
// Sets the maximum allowed size of the client request body
BodySize string `json:"bodySize,omitempty" structs:"bodySize,omitempty"`
BodySize string `structs:"body-size,omitempty"`
// http://nginx.org/en/docs/ngx_core_module.html#error_log
// Configures logging level [debug | info | notice | warn | error | crit | alert | emerg]
// Log levels above are listed in the order of increasing severity
ErrorLogLevel string `json:"errorLogLevel,omitempty" structs:"errorLogLevel,omitempty"`
ErrorLogLevel string `structs:"error-log-level,omitempty"`
// Enables or disables the header HTS in servers running SSL
UseHTS bool `json:"useHTS,omitempty" structs:"useHTS,omitempty"`
UseHTS bool `structs:"use-hts,omitempty"`
// Enables or disables the use of HTS in all the subdomains of the servername
HTSIncludeSubdomains bool `json:"htsIncludeSubdomains,omitempty" structs:"htsIncludeSubdomains,omitempty"`
HTSIncludeSubdomains bool `structs:"hts-include-subdomains,omitempty"`
// HTTP Strict Transport Security (often abbreviated as HSTS) is a security feature (HTTP header)
// that tell browsers that it should only be communicated with using HTTPS, instead of using HTTP.
// https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security
// max-age is the time, in seconds, that the browser should remember that this site is only to be
// accessed using HTTPS.
HTSMaxAge string `json:"htsMaxAge,omitempty" structs:"htsMaxAge,omitempty"`
HTSMaxAge string `structs:"hts-max-age,omitempty"`
// Time during which a keep-alive client connection will stay open on the server side.
// The zero value disables keep-alive client connections
// http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
KeepAlive int `json:"keepAlive,omitempty" structs:"keepAlive,omitempty"`
KeepAlive int `structs:"keep-alive,omitempty"`
// Maximum number of simultaneous connections that can be opened by each worker process
// http://nginx.org/en/docs/ngx_core_module.html#worker_connections
MaxWorkerConnections int `json:"maxWorkerConnections,omitempty" structs:"maxWorkerConnections,omitempty"`
MaxWorkerConnections int `structs:"max-worker-connections,omitempty"`
// Defines a timeout for establishing a connection with a proxied server.
// It should be noted that this timeout cannot usually exceed 75 seconds.
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_connect_timeout
ProxyConnectTimeout int `json:"proxyConnectTimeout,omitempty" structs:"proxyConnectTimeout,omitempty"`
ProxyConnectTimeout int `structs:"proxy-connect-timeout,omitempty"`
// If UseProxyProtocol is enabled ProxyRealIPCIDR defines the default the IP/network address
// of your external load balancer
ProxyRealIPCIDR string `json:"proxyRealIPCIDR,omitempty" structs:"proxyRealIPCIDR,omitempty"`
ProxyRealIPCIDR string `structs:"proxy-real-ip-cidr,omitempty"`
// Timeout in seconds for reading a response from the proxied server. The timeout is set only between
// two successive read operations, not for the transmission of the whole response
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
ProxyReadTimeout int `json:"proxyReadTimeout,omitempty" structs:"proxyReadTimeout,omitempty"`
ProxyReadTimeout int `structs:"proxy-read-timeout,omitempty"`
// Timeout in seconds for transmitting a request to the proxied server. The timeout is set only between
// two successive write operations, not for the transmission of the whole request.
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_send_timeout
ProxySendTimeout int `json:"proxySendTimeout,omitempty" structs:"proxySendTimeout,omitempty"`
ProxySendTimeout int `structs:"proxy-send-timeout,omitempty"`
// Configures name servers used to resolve names of upstream servers into addresses
// http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver
Resolver string `json:"resolver,omitempty" structs:"resolver,omitempty"`
Resolver string `structs:"resolver,omitempty"`
// Maximum size of the server names hash tables used in server names, map directives values,
// MIME types, names of request header strings, etcd.
// http://nginx.org/en/docs/hash.html
// http://nginx.org/en/docs/http/ngx_http_core_module.html#server_names_hash_max_size
ServerNameHashMaxSize int `json:"serverNameHashMaxSize,omitempty" structs:"serverNameHashMaxSize,omitempty"`
ServerNameHashMaxSize int `structs:"server-name-hash-max-size,omitempty"`
// Size of the bucker for the server names hash tables
// http://nginx.org/en/docs/hash.html
// http://nginx.org/en/docs/http/ngx_http_core_module.html#server_names_hash_bucket_size
ServerNameHashBucketSize int `json:"serverNameHashBucketSize,omitempty" structs:"serverNameHashBucketSize,omitempty"`
ServerNameHashBucketSize int `structs:"server-name-hash-bucket-size,omitempty"`
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_buffer_size
// Sets the size of the buffer used for sending data.
// 4k helps NGINX to improve TLS Time To First Byte (TTTFB)
// https://www.igvita.com/2013/12/16/optimizing-nginx-tls-time-to-first-byte/
SSLBufferSize string `json:"sslBufferSize,omitempty" structs:"sslBufferSize,omitempty"`
SSLBufferSize string `structs:"ssl-buffer-size,omitempty"`
// Enabled ciphers list to enabled. The ciphers are specified in the format understood by
// the OpenSSL library
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers
SSLCiphers string `json:"sslCiphers,omitempty" structs:"sslCiphers,omitempty"`
SSLCiphers string `structs:"ssl-ciphers,omitempty"`
// Base64 string that contains Diffie-Hellman key to help with "Perfect Forward Secrecy"
// https://www.openssl.org/docs/manmaster/apps/dhparam.html
// https://wiki.mozilla.org/Security/Server_Side_TLS#DHE_handshake_and_dhparam
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
SSLDHParam string `json:"sslDHParam,omitempty" structs:"sslDHParam,omitempty"`
SSLDHParam string `structs:"ssl-dh-param,omitempty"`
// SSL enabled protocols to use
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols
SSLProtocols string `json:"sslProtocols,omitempty" structs:"sslProtocols,omitempty"`
SSLProtocols string `structs:"ssl-protocols,omitempty"`
// Enables or disables the use of shared SSL cache among worker processes.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache
SSLSessionCache bool `json:"sslSessionCache,omitempty" structs:"sslSessionCache,omitempty"`
SSLSessionCache bool `structs:"ssl-session-cache,omitempty"`
// Size of the SSL shared cache between all worker processes.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache
SSLSessionCacheSize string `json:"sslSessionCacheSize,omitempty" structs:"sslSessionCacheSize,omitempty"`
SSLSessionCacheSize string `structs:"ssl-session-cache-size,omitempty"`
// Enables or disables session resumption through TLS session tickets.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_tickets
SSLSessionTickets bool `json:"sslSessionTickets,omitempty" structs:"sslSessionTickets,omitempty"`
SSLSessionTickets bool `structs:"ssl-session-tickets,omitempty"`
// Time during which a client may reuse the session parameters stored in a cache.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_timeout
SSLSessionTimeout string `json:"sslSessionTimeout,omitempty" structs:"sslSessionTimeout,omitempty"`
SSLSessionTimeout string `structs:"ssl-session-timeout,omitempty"`
// Enables or disables the use of the PROXY protocol to receive client connection
// (real IP address) information passed through proxy servers and load balancers
// such as HAproxy and Amazon Elastic Load Balancer (ELB).
// https://www.nginx.com/resources/admin-guide/proxy-protocol/
UseProxyProtocol bool `json:"useProxyProtocol,omitempty" structs:"useProxyProtocol,omitempty"`
UseProxyProtocol bool `structs:"use-proxy-protocol,omitempty"`
// Enables or disables the use of the nginx module that compresses responses using the "gzip" method
// http://nginx.org/en/docs/http/ngx_http_gzip_module.html
UseGzip bool `json:"useGzip,omitempty" structs:"useGzip,omitempty"`
UseGzip bool `structs:"use-gzip,omitempty"`
// MIME types in addition to "text/html" to compress. The special value “*” matches any MIME type.
// Responses with the “text/html” type are always compressed if UseGzip is enabled
GzipTypes string `json:"gzipTypes,omitempty" structs:"gzipTypes,omitempty"`
GzipTypes string `structs:"gzip-types,omitempty"`
// Defines the number of worker processes. By default auto means number of available CPU cores
// http://nginx.org/en/docs/ngx_core_module.html#worker_processes
WorkerProcesses string `json:"workerProcesses,omitempty" structs:"workerProcesses,omitempty"`
WorkerProcesses string `structs:"worker-processes,omitempty"`
}
// Manager ...
type Manager struct {
ConfigFile string
defCfg *nginxConfiguration
defCfg nginxConfiguration
defResolver string
sslDHParam string
reloadRateLimiter util.RateLimiter
// template loaded ready to be used to generate the nginx configuration file
template *template.Template
@ -221,7 +224,7 @@ type Manager struct {
// defaultConfiguration returns the default configuration contained
// in the file default-conf.json
func newDefaultNginxCfg() *nginxConfiguration {
func newDefaultNginxCfg() nginxConfiguration {
cfg := nginxConfiguration{
BodySize: bodySize,
ErrorLogLevel: errorLevel,
@ -231,10 +234,10 @@ func newDefaultNginxCfg() *nginxConfiguration {
GzipTypes: gzipTypes,
KeepAlive: 75,
MaxWorkerConnections: 16384,
ProxyConnectTimeout: 30,
ProxyConnectTimeout: 5,
ProxyRealIPCIDR: defIPCIDR,
ProxyReadTimeout: 30,
ProxySendTimeout: 30,
ProxyReadTimeout: 60,
ProxySendTimeout: 60,
ServerNameHashMaxSize: 512,
ServerNameHashBucketSize: 64,
SSLBufferSize: sslBufferSize,
@ -253,16 +256,17 @@ func newDefaultNginxCfg() *nginxConfiguration {
cfg.ErrorLogLevel = "debug"
}
return &cfg
return cfg
}
// NewManager ...
func NewManager(kubeClient *client.Client) *Manager {
ngx := &Manager{
ConfigFile: "/etc/nginx/nginx.conf",
defCfg: newDefaultNginxCfg(),
defResolver: strings.Join(getDNSServers(), " "),
reloadLock: &sync.Mutex{},
ConfigFile: "/etc/nginx/nginx.conf",
defCfg: newDefaultNginxCfg(),
defResolver: strings.Join(getDNSServers(), " "),
reloadLock: &sync.Mutex{},
reloadRateLimiter: util.NewTokenBucketRateLimiter(0.1, 1),
}
ngx.createCertsDir(sslDirectory)

View file

@ -47,8 +47,7 @@ func (nginx *Manager) AddOrUpdateCertAndKey(name string, cert string, key string
// CheckSSLCertificate checks if the certificate and key file are valid
// returning the result of the validation and the list of hostnames
// contained in the common name/s
func (nginx *Manager) CheckSSLCertificate(secretName string) ([]string, error) {
pemFileName := sslDirectory + "/" + secretName + ".pem"
func (nginx *Manager) CheckSSLCertificate(pemFileName string) ([]string, error) {
pemCerts, err := ioutil.ReadFile(pemFileName)
if err != nil {
return []string{}, err

View file

@ -20,29 +20,34 @@ import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"text/template"
"github.com/fatih/structs"
"github.com/golang/glog"
)
var funcMap = template.FuncMap{
"empty": func(input interface{}) bool {
check, ok := input.(string)
if ok {
return len(check) == 0
}
var (
camelRegexp = regexp.MustCompile("[0-9A-Za-z]+")
return true
},
}
funcMap = template.FuncMap{
"empty": func(input interface{}) bool {
check, ok := input.(string)
if ok {
return len(check) == 0
}
return true
},
}
)
func (ngx *Manager) loadTemplate() {
tmpl, _ := template.New("nginx.tmpl").Funcs(funcMap).ParseFiles("./nginx.tmpl")
ngx.template = tmpl
}
func (ngx *Manager) writeCfg(cfg *nginxConfiguration, ingressCfg IngressConfig) (bool, error) {
func (ngx *Manager) writeCfg(cfg nginxConfiguration, ingressCfg IngressConfig) (bool, error) {
fromMap := structs.Map(cfg)
toMap := structs.Map(ngx.defCfg)
curNginxCfg := merge(toMap, fromMap)
@ -53,7 +58,7 @@ func (ngx *Manager) writeCfg(cfg *nginxConfiguration, ingressCfg IngressConfig)
conf["tcpUpstreams"] = ingressCfg.TCPUpstreams
conf["defResolver"] = ngx.defResolver
conf["sslDHParam"] = ngx.sslDHParam
conf["cfg"] = curNginxCfg
conf["cfg"] = fixKeyNames(curNginxCfg)
buffer := new(bytes.Buffer)
err := ngx.template.Execute(buffer, conf)
@ -77,3 +82,23 @@ func (ngx *Manager) writeCfg(cfg *nginxConfiguration, ingressCfg IngressConfig)
return changed, nil
}
func fixKeyNames(data map[string]interface{}) map[string]interface{} {
fixed := make(map[string]interface{})
for k, v := range data {
fixed[toCamelCase(k)] = v
}
return fixed
}
func toCamelCase(src string) string {
byteSrc := []byte(src)
chunks := camelRegexp.FindAll(byteSrc, -1)
for idx, val := range chunks {
if idx > 0 {
chunks[idx] = bytes.Title(val)
}
}
return string(bytes.Join(chunks, nil))
}

View file

@ -18,7 +18,6 @@ package nginx
import (
"bytes"
"encoding/json"
"io/ioutil"
"os"
"os/exec"
@ -27,7 +26,7 @@ import (
"github.com/golang/glog"
"github.com/imdario/mergo"
"github.com/mitchellh/mapstructure"
"k8s.io/kubernetes/pkg/api"
)
@ -61,22 +60,25 @@ func getDNSServers() []string {
}
// ReadConfig obtains the configuration defined by the user merged with the defaults.
func (ngx *Manager) ReadConfig(config *api.ConfigMap) (*nginxConfiguration, error) {
func (ngx *Manager) ReadConfig(config *api.ConfigMap) nginxConfiguration {
if len(config.Data) == 0 {
return newDefaultNginxCfg(), nil
return newDefaultNginxCfg()
}
cfg := newDefaultNginxCfg()
data, err := json.Marshal(config.Data)
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "structs",
Result: &cfg,
WeaklyTypedInput: true,
})
err = decoder.Decode(config.Data)
if err != nil {
err = mergo.Merge(cfg, data)
if err != nil {
return cfg, nil
}
glog.Infof("%v", err)
}
return cfg, nil
return cfg
}
func (ngx *Manager) needsReload(data *bytes.Buffer) (bool, error) {