Fix nginx ingress controller bug around config map merging
* a config map bool value of false cannot overwritte a true value from defaults * implement merging in ReadConfig * remove helper function merge * adds tests to ensure config is read properly
This commit is contained in:
parent
4159a40da4
commit
94e6702385
3 changed files with 130 additions and 23 deletions
|
@ -48,10 +48,6 @@ func (ngx *Manager) loadTemplate() {
|
|||
}
|
||||
|
||||
func (ngx *Manager) writeCfg(cfg nginxConfiguration, ingressCfg IngressConfig) (bool, error) {
|
||||
fromMap := structs.Map(cfg)
|
||||
toMap := structs.Map(ngx.defCfg)
|
||||
curNginxCfg := merge(toMap, fromMap)
|
||||
|
||||
conf := make(map[string]interface{})
|
||||
conf["upstreams"] = ingressCfg.Upstreams
|
||||
conf["servers"] = ingressCfg.Servers
|
||||
|
@ -59,7 +55,7 @@ func (ngx *Manager) writeCfg(cfg nginxConfiguration, ingressCfg IngressConfig) (
|
|||
conf["udpUpstreams"] = ingressCfg.UDPUpstreams
|
||||
conf["defResolver"] = ngx.defResolver
|
||||
conf["sslDHParam"] = ngx.sslDHParam
|
||||
conf["cfg"] = fixKeyNames(curNginxCfg)
|
||||
conf["cfg"] = fixKeyNames(structs.Map(cfg))
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
err := ngx.template.Execute(buffer, conf)
|
||||
|
|
|
@ -59,18 +59,36 @@ func getDNSServers() []string {
|
|||
return nameservers
|
||||
}
|
||||
|
||||
// getConfigKeyToStructKeyMap returns a map with the ConfigMapKey as key and the StructName as value.
|
||||
func getConfigKeyToStructKeyMap() map[string]string {
|
||||
keyMap := map[string]string{}
|
||||
n := &nginxConfiguration{}
|
||||
val := reflect.Indirect(reflect.ValueOf(n))
|
||||
for i := 0; i < val.Type().NumField(); i++ {
|
||||
fieldSt := val.Type().Field(i)
|
||||
configMapKey := strings.Split(fieldSt.Tag.Get("structs"), ",")[0]
|
||||
structKey := fieldSt.Name
|
||||
keyMap[configMapKey] = structKey
|
||||
}
|
||||
return keyMap
|
||||
}
|
||||
|
||||
// ReadConfig obtains the configuration defined by the user merged with the defaults.
|
||||
func (ngx *Manager) ReadConfig(config *api.ConfigMap) nginxConfiguration {
|
||||
if len(config.Data) == 0 {
|
||||
return newDefaultNginxCfg()
|
||||
}
|
||||
|
||||
cfg := newDefaultNginxCfg()
|
||||
cfgCM := nginxConfiguration{}
|
||||
cfgDefault := newDefaultNginxCfg()
|
||||
|
||||
metadata := &mapstructure.Metadata{}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
TagName: "structs",
|
||||
Result: &cfg,
|
||||
Result: &cfgCM,
|
||||
WeaklyTypedInput: true,
|
||||
Metadata: metadata,
|
||||
})
|
||||
|
||||
err = decoder.Decode(config.Data)
|
||||
|
@ -78,7 +96,26 @@ func (ngx *Manager) ReadConfig(config *api.ConfigMap) nginxConfiguration {
|
|||
glog.Infof("%v", err)
|
||||
}
|
||||
|
||||
return cfg
|
||||
keyMap := getConfigKeyToStructKeyMap()
|
||||
|
||||
valCM := reflect.Indirect(reflect.ValueOf(cfgCM))
|
||||
|
||||
for _, key := range metadata.Keys {
|
||||
fieldName, ok := keyMap[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
valDefault := reflect.ValueOf(&cfgDefault).Elem().FieldByName(fieldName)
|
||||
|
||||
fieldCM := valCM.FieldByName(fieldName)
|
||||
|
||||
if valDefault.IsValid() {
|
||||
valDefault.Set(fieldCM)
|
||||
}
|
||||
}
|
||||
|
||||
return cfgDefault
|
||||
}
|
||||
|
||||
func (ngx *Manager) needsReload(data *bytes.Buffer) (bool, error) {
|
||||
|
@ -143,21 +180,6 @@ func diff(b1, b2 []byte) (data []byte, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func merge(dst, src map[string]interface{}) map[string]interface{} {
|
||||
for key, srcVal := range src {
|
||||
if dstVal, ok := dst[key]; ok {
|
||||
srcMap, srcMapOk := toMap(srcVal)
|
||||
dstMap, dstMapOk := toMap(dstVal)
|
||||
if srcMapOk && dstMapOk {
|
||||
srcVal = merge(dstMap, srcMap)
|
||||
}
|
||||
}
|
||||
dst[key] = srcVal
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func toMap(iface interface{}) (map[string]interface{}, bool) {
|
||||
value := reflect.ValueOf(iface)
|
||||
if value.Kind() == reflect.Map {
|
||||
|
|
89
controllers/nginx/nginx/utils_test.go
Normal file
89
controllers/nginx/nginx/utils_test.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 nginx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func getConfigNginxBool(data map[string]string) nginxConfiguration {
|
||||
manager := &Manager{}
|
||||
configMap := &api.ConfigMap{
|
||||
Data: data,
|
||||
}
|
||||
return manager.ReadConfig(configMap)
|
||||
}
|
||||
|
||||
func TestManagerReadConfigBoolFalse(t *testing.T) {
|
||||
configNginx := getConfigNginxBool(map[string]string{
|
||||
"hts-include-subdomains": "false",
|
||||
"use-proxy-protocol": "false",
|
||||
})
|
||||
if configNginx.HTSIncludeSubdomains {
|
||||
t.Error("Failed to config boolean value (default true) to false")
|
||||
}
|
||||
if configNginx.UseProxyProtocol {
|
||||
t.Error("Failed to config boolean value (default false) to false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerReadConfigBoolTrue(t *testing.T) {
|
||||
configNginx := getConfigNginxBool(map[string]string{
|
||||
"hts-include-subdomains": "true",
|
||||
"use-proxy-protocol": "true",
|
||||
})
|
||||
if !configNginx.HTSIncludeSubdomains {
|
||||
t.Error("Failed to config boolean value (default true) to true")
|
||||
}
|
||||
if !configNginx.UseProxyProtocol {
|
||||
t.Error("Failed to config boolean value (default false) to true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerReadConfigBoolNothing(t *testing.T) {
|
||||
configNginx := getConfigNginxBool(map[string]string{
|
||||
"invaild-key": "true",
|
||||
})
|
||||
if !configNginx.HTSIncludeSubdomains {
|
||||
t.Error("Failed to get default boolean value true")
|
||||
}
|
||||
if configNginx.UseProxyProtocol {
|
||||
t.Error("Failed to get default boolean value false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerReadConfigStringSet(t *testing.T) {
|
||||
configNginx := getConfigNginxBool(map[string]string{
|
||||
"ssl-protocols": "TLSv1.2",
|
||||
})
|
||||
exp := "TLSv1.2"
|
||||
if configNginx.SSLProtocols != exp {
|
||||
t.Error("Failed to set string value true actual='%s' expected='%s'", configNginx.SSLProtocols, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerReadConfigStringNothing(t *testing.T) {
|
||||
configNginx := getConfigNginxBool(map[string]string{
|
||||
"not-existing": "TLSv1.2",
|
||||
})
|
||||
exp := "10m"
|
||||
if configNginx.SSLSessionTimeout != exp {
|
||||
t.Error("Failed to set string value true actual='%s' expected='%s'", configNginx.SSLSessionTimeout, exp)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue