Skip to content

Commit

Permalink
add global config parsing
Browse files Browse the repository at this point in the history
Global config is both configurations of global section from haproxy config file - eg nbproc, nbthread, DH param config, and also default values for frontends and backends - eg timeouts.

Updater knows both sides - k8s objects or maps with configs from the user, and haproxy objects used to populate the templates.

Legacy controller code is always used via interface - the only coupling is from `pkg/controller` code.
  • Loading branch information
jcmoraisjr committed Jan 29, 2019
1 parent 9a543c8 commit 696c06f
Show file tree
Hide file tree
Showing 17 changed files with 341 additions and 28 deletions.
4 changes: 3 additions & 1 deletion pkg/common/ingress/controller/backend_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ func (ic *GenericController) getPemCertificate(secret *apiv1.Secret) (*ingress.S
ca := secret.Data["ca.crt"]

// namespace/secretName -> namespace-secretName
nsSecName := strings.Replace(secretName, "/", "-", -1)
// use `_` instead if v0.8+
sep := map[bool]string{true: "-", false: "_"}
nsSecName := strings.Replace(secretName, "/", sep[ic.cfg.V07], -1)

var s *ingress.SSLCert
var err error
Expand Down
23 changes: 21 additions & 2 deletions pkg/controller/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/jcmoraisjr/haproxy-ingress/pkg/common/ingress"
"github.com/jcmoraisjr/haproxy-ingress/pkg/common/ingress/controller"
"github.com/jcmoraisjr/haproxy-ingress/pkg/common/net/ssl"
)

type cache struct {
Expand Down Expand Up @@ -61,7 +62,7 @@ func (c *cache) GetTLSSecretPath(secretName string) (string, error) {
return "", err
}
if sslCert.PemFileName == "" {
return "", fmt.Errorf("secret '%s' does not have tls/key pair", secretName)
return "", fmt.Errorf("secret '%s' does not have keys 'tls.crt' and 'tls.key'", secretName)
}
return sslCert.PemFileName, nil
}
Expand All @@ -72,11 +73,29 @@ func (c *cache) GetCASecretPath(secretName string) (string, error) {
return "", err
}
if sslCert.CAFileName == "" {
return "", fmt.Errorf("secret '%s' does not have ca.crt key", secretName)
return "", fmt.Errorf("secret '%s' does not have key 'ca.crt'", secretName)
}
return sslCert.CAFileName, nil
}

func (c *cache) GetDHSecretPath(secretName string) (string, error) {
secret, err := c.listers.Secret.GetByName(secretName)
if err != nil {
return "", err
}
dh, found := secret.Data[dhparamFilename]
if !found {
return "", fmt.Errorf("secret '%s' does not have key '%s'", secretName, dhparamFilename)
}
pem := strings.Replace(secretName, "/", "_", -1)
pemFileName, err := ssl.AddOrUpdateDHParam(pem, dh)
if err != nil {
return "", fmt.Errorf("error creating dh-param file '%s': %v", pem, err)
}
// file.SHA1(pemFileName)
return pemFileName, nil
}

func (c *cache) GetSecretContent(secretName, keyName string) ([]byte, error) {
secret, err := c.listers.Secret.GetByName(secretName)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions pkg/converters/ingress/annotations/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
)

func (c *updater) buildAffinity(d *backData) {
func (c *updater) buildBackendAffinity(d *backData) {
if d.ann.Affinity != "cookie" {
if d.ann.Affinity != "" {
c.logger.Error("unsupported affinity type on %v: %s", d.ann.Source, d.ann.Affinity)
Expand All @@ -50,7 +50,7 @@ func (c *updater) buildAffinity(d *backData) {
d.backend.Cookie.Key = d.ann.CookieKey
}

func (c *updater) buildAuthHTTP(d *backData) {
func (c *updater) buildBackendAuthHTTP(d *backData) {
if d.ann.AuthType != "basic" {
if d.ann.AuthType != "" {
c.logger.Error("unsupported authentication type on %v: %s", d.ann.Source, d.ann.AuthType)
Expand All @@ -71,7 +71,7 @@ func (c *updater) buildAuthHTTP(d *backData) {
return
}
userstr := string(userb)
users, errs := c.buildAuthHTTPExtractUserlist(d.ann.Source.Name, secretName, userstr)
users, errs := c.buildBackendAuthHTTPExtractUserlist(d.ann.Source.Name, secretName, userstr)
for _, err := range errs {
c.logger.Warn("ignoring malformed usr/passwd on secret '%s', declared on %v: %v", secretName, d.ann.Source, err)
}
Expand All @@ -83,7 +83,7 @@ func (c *updater) buildAuthHTTP(d *backData) {
d.backend.HreqValidateUserlist(userlist)
}

func (c *updater) buildAuthHTTPExtractUserlist(source, secret, users string) ([]hatypes.User, []error) {
func (c *updater) buildBackendAuthHTTPExtractUserlist(source, secret, users string) ([]hatypes.User, []error) {
var userlist []hatypes.User
var err []error
for i, usr := range strings.Split(users, "\n") {
Expand Down Expand Up @@ -125,7 +125,7 @@ func (c *updater) buildAuthHTTPExtractUserlist(source, secret, users string) ([]
return userlist, err
}

func (c *updater) buildBlueGreen(d *backData) {
func (c *updater) buildBackendBlueGreen(d *backData) {
balance := d.ann.BlueGreenBalance
if balance == "" {
balance = d.ann.BlueGreenDeploy
Expand Down
6 changes: 3 additions & 3 deletions pkg/converters/ingress/annotations/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestAffinity(t *testing.T) {
c := setup(t)
u := c.createUpdater()
d := c.createBackendData("default", "ing1", &test.ann)
u.buildAffinity(d)
u.buildBackendAffinity(d)
if !reflect.DeepEqual(test.expCookie, d.backend.Cookie) {
t.Errorf("config %d differs - expected: %+v - actual: %+v", i, test.expCookie, d.backend.Cookie)
}
Expand Down Expand Up @@ -208,7 +208,7 @@ usr2::clearpwd2`)}},
}
c.cache.SecretContent = test.secrets
d := c.createBackendData(test.namespace, test.ingname, &test.ann)
u.buildAuthHTTP(d)
u.buildBackendAuthHTTP(d)
userlists := u.haproxy.Userlists()
httpRequests := d.backend.HTTPRequests
if len(userlists)+len(test.expUserlists) > 0 && !reflect.DeepEqual(test.expUserlists, userlists) {
Expand Down Expand Up @@ -489,7 +489,7 @@ INFO-V(3) blue/green balance label 'v=3' on ingress 'default/ing1' does not refe
d := c.createBackendData("default", "ing1", &test.ann)
d.backend.Endpoints = test.endpoints
u := c.createUpdater()
u.buildBlueGreen(d)
u.buildBackendBlueGreen(d)
weights := make([]int, len(d.backend.Endpoints))
for j, ep := range d.backend.Endpoints {
weights[j] = ep.Weight
Expand Down
4 changes: 2 additions & 2 deletions pkg/converters/ingress/annotations/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

package annotations

func (c *updater) buildAuthTLS(d *frontData) {
func (c *updater) buildFrontendAuthTLS(d *frontData) {
if d.ann.AuthTLSSecret == "" {
return
}
Expand All @@ -27,7 +27,7 @@ func (c *updater) buildAuthTLS(d *frontData) {
}
}

func (c *updater) buildSSLPassthrough(d *frontData) {
func (c *updater) buildFrontendSSLPassthrough(d *frontData) {
if !d.ann.SSLPassthrough {
return
}
Expand Down
115 changes: 115 additions & 0 deletions pkg/converters/ingress/annotations/global.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright 2019 The HAProxy Ingress Controller Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package annotations

import (
"fmt"
"strings"
)

func (c *updater) buildGlobalProc(d *globalData) {
balance := d.config.NbprocBalance
if balance < 1 {
c.logger.Warn("invalid value of nbproc-balance configmap option (%v), using 1", balance)
balance = 1
}
if balance > 1 {
// need to visit (at least) statistics and healthz bindings as well
// as admin socket before using more than one balance backend
c.logger.Warn("nbproc-balance configmap option (%v) greater than 1 is not yet supported, using 1", balance)
balance = 1
}
ssl := d.config.NbprocSSL
if ssl < 0 {
c.logger.Warn("invalid value of nbproc-ssl configmap option (%v), using 0", ssl)
ssl = 0
}
procs := balance + ssl
threads := d.config.Nbthread
if threads < 1 {
c.logger.Warn("invalid value of nbthread configmap option (%v), using 1", threads)
threads = 1
}
bindprocBalance := "1"
if balance > 1 {
bindprocBalance = fmt.Sprintf("1-%v", balance)
}
bindprocSSL := ""
if ssl == 0 {
bindprocSSL = bindprocBalance
} else if ssl == 1 {
bindprocSSL = fmt.Sprintf("%v", balance+1)
} else if ssl > 1 {
bindprocSSL = fmt.Sprintf("%v-%v", balance+1, procs)
}
cpumap := ""
if threads > 1 {
if procs == 1 {
cpumap = fmt.Sprintf("auto:1/1-%v 0-%v", threads, threads-1)
}
} else if procs > 1 {
cpumap = fmt.Sprintf("auto:1-%v 0-%v", procs, procs-1)
}
d.global.Procs.Nbproc = procs
d.global.Procs.Nbthread = threads
d.global.Procs.NbprocBalance = balance
d.global.Procs.NbprocSSL = ssl
d.global.Procs.BindprocBalance = bindprocBalance
d.global.Procs.BindprocSSL = bindprocSSL
d.global.Procs.CPUMap = cpumap
}

func (c *updater) buildGlobalTimeout(d *globalData) {
copyHAProxyTime(&d.global.Timeout.Client, d.config.TimeoutClient)
copyHAProxyTime(&d.global.Timeout.ClientFin, d.config.TimeoutClientFin)
copyHAProxyTime(&d.global.Timeout.Connect, d.config.TimeoutConnect)
copyHAProxyTime(&d.global.Timeout.HTTPRequest, d.config.TimeoutHTTPRequest)
copyHAProxyTime(&d.global.Timeout.KeepAlive, d.config.TimeoutKeepAlive)
copyHAProxyTime(&d.global.Timeout.Queue, d.config.TimeoutQueue)
copyHAProxyTime(&d.global.Timeout.Server, d.config.TimeoutServer)
copyHAProxyTime(&d.global.Timeout.ServerFin, d.config.TimeoutServerFin)
copyHAProxyTime(&d.global.Timeout.Tunnel, d.config.TimeoutTunnel)
copyHAProxyTime(&d.global.Timeout.Stop, d.config.TimeoutStop)
}

func (c *updater) buildGlobalSSL(d *globalData) {
d.global.SSL.Ciphers = d.config.SSLCiphers
d.global.SSL.Options = d.config.SSLOptions
if d.config.SSLDHParam != "" {
if dhFilename, err := c.cache.GetDHSecretPath(d.config.SSLDHParam); err == nil {
d.global.SSL.DHParam.Filename = dhFilename
} else {
c.logger.Error("error reading DH params: %v", err)
}
}
d.global.SSL.DHParam.DefaultMaxSize = d.config.SSLDHDefaultMaxSize
d.global.SSL.Engine = d.config.SSLEngine
d.global.SSL.ModeAsync = d.config.SSLModeAsync
}

func (c *updater) buildGlobalModSecurity(d *globalData) {
d.global.ModSecurity.Endpoints = strings.Split(d.config.ModsecurityEndpoints, ",")
d.global.ModSecurity.Timeout.Hello = d.config.ModsecurityTimeoutHello
d.global.ModSecurity.Timeout.Idle = d.config.ModsecurityTimeoutIdle
d.global.ModSecurity.Timeout.Processing = d.config.ModsecurityTimeoutProcessing
}

func (c *updater) buildGlobalCustomConfig(d *globalData) {
if d.config.ConfigGlobal != "" {
d.global.CustomConfig = strings.Split(strings.TrimRight(d.config.ConfigGlobal, "\n"), "\n")
}
}
40 changes: 35 additions & 5 deletions pkg/converters/ingress/annotations/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

// Updater ...
type Updater interface {
UpdateGlobalConfig(global *hatypes.Global, config *ingtypes.Config)
UpdateFrontendConfig(frontend *hatypes.Frontend, ann *ingtypes.FrontendAnnotations)
UpdateBackendConfig(backend *hatypes.Backend, ann *ingtypes.BackendAnnotations)
}
Expand All @@ -44,6 +45,11 @@ type updater struct {
logger types.Logger
}

type globalData struct {
global *hatypes.Global
config *ingtypes.Config
}

type frontData struct {
frontend *hatypes.Frontend
ann *ingtypes.FrontendAnnotations
Expand All @@ -54,6 +60,30 @@ type backData struct {
ann *ingtypes.BackendAnnotations
}

func copyHAProxyTime(dst *string, src string) {
// TODO validate
*dst = src
}

func (c *updater) UpdateGlobalConfig(global *hatypes.Global, config *ingtypes.Config) {
data := &globalData{
global: global,
config: config,
}
global.Syslog.Endpoint = config.SyslogEndpoint
global.Syslog.Format = config.SyslogFormat
global.Syslog.Tag = config.SyslogTag
global.MaxConn = config.MaxConnections
global.DrainSupport = config.DrainSupport
global.LoadServerState = config.LoadServerState
global.StatsSocket = "/var/run/haproxy-stats.sock"
c.buildGlobalProc(data)
c.buildGlobalTimeout(data)
c.buildGlobalSSL(data)
c.buildGlobalModSecurity(data)
c.buildGlobalCustomConfig(data)
}

func (c *updater) UpdateFrontendConfig(frontend *hatypes.Frontend, ann *ingtypes.FrontendAnnotations) {
data := &frontData{
frontend: frontend,
Expand All @@ -64,8 +94,8 @@ func (c *updater) UpdateFrontendConfig(frontend *hatypes.Frontend, ann *ingtypes
frontend.Alias.AliasRegex = ann.ServerAliasRegex
frontend.Timeout.Client = ann.TimeoutClient
frontend.Timeout.ClientFin = ann.TimeoutClientFin
c.buildAuthTLS(data)
c.buildSSLPassthrough(data)
c.buildFrontendAuthTLS(data)
c.buildFrontendSSLPassthrough(data)
}

func (c *updater) UpdateBackendConfig(backend *hatypes.Backend, ann *ingtypes.BackendAnnotations) {
Expand All @@ -78,7 +108,7 @@ func (c *updater) UpdateBackendConfig(backend *hatypes.Backend, ann *ingtypes.Ba
backend.MaxconnServer = ann.MaxconnServer
backend.ProxyBodySize = ann.ProxyBodySize
backend.SSLRedirect = ann.SSLRedirect
c.buildAffinity(data)
c.buildAuthHTTP(data)
c.buildBlueGreen(data)
c.buildBackendAffinity(data)
c.buildBackendAuthHTTP(data)
c.buildBackendBlueGreen(data)
}
4 changes: 3 additions & 1 deletion pkg/converters/ingress/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ func createDefaults() *types.Config {
StatsProxyProtocol: false,
StatsSSLCert: "",
StrictHost: true,
Syslog: "",
SyslogEndpoint: "",
SyslogFormat: "rfc5424",
SyslogTag: "ingress",
TCPLogFormat: "",
TimeoutStop: "",
UseProxyProtocol: false,
Expand Down
9 changes: 9 additions & 0 deletions pkg/converters/ingress/helper_test/cachemock.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type CacheMock struct {
PodList map[string]*api.Pod
SecretTLSPath map[string]string
SecretCAPath map[string]string
SecretDHPath map[string]string
SecretContent SecretContent
}

Expand Down Expand Up @@ -82,6 +83,14 @@ func (c *CacheMock) GetCASecretPath(secretName string) (string, error) {
return "", fmt.Errorf("secret not found: '%s'", secretName)
}

// GetDHSecretPath ...
func (c *CacheMock) GetDHSecretPath(secretName string) (string, error) {
if path, found := c.SecretDHPath[secretName]; found {
return path, nil
}
return "", fmt.Errorf("secret not found: '%s'", secretName)
}

// GetSecretContent ...
func (c *CacheMock) GetSecretContent(secretName, keyName string) ([]byte, error) {
if content, found := c.SecretContent[secretName]; found {
Expand Down
Loading

0 comments on commit 696c06f

Please sign in to comment.