@@ -17,6 +17,7 @@ limitations under the License.
1717package nginx
1818
1919import (
20+ "encoding/json"
2021 "fmt"
2122 "reflect"
2223
@@ -47,89 +48,188 @@ var (
4748 "limit-conn-zone-variable" : "" ,
4849 "whitelist-source-range" : "" ,
4950 }
51+
52+ annotationExternalConfigMaps string = "external-configs"
5053)
5154
52- func merge (base , del , add map [string ]string ) map [string ]string {
55+ func mapDel (base map [ string ] string , dels ... map [string ]string ) map [string ]string {
5356 ret := make (map [string ]string )
54-
5557 for k , v := range base {
56- if del != nil {
57- if _ , has := del [k ]; has {
58- continue
58+ has := false
59+ for _ , del := range dels {
60+ if _ , has = del [k ]; has {
61+ break
5962 }
6063 }
61- ret [k ] = v
64+ if ! has {
65+ ret [k ] = v
66+ }
6267 }
6368
64- for k , v := range add {
69+ return ret
70+ }
71+ func mapAdd (base map [string ]string , adds ... map [string ]string ) map [string ]string {
72+ ret := make (map [string ]string )
73+ for k , v := range base {
6574 ret [k ] = v
6675 }
6776
77+ for _ , add := range adds {
78+ for k , v := range add {
79+ ret [k ] = v
80+ }
81+ }
82+
6883 return ret
6984}
7085
7186func (f * nginx ) ensureConfigMaps (lb * lbapi.LoadBalancer ) error {
7287 labels := f .selector (lb )
7388
89+ // For ingress-nginx configuration
7490 cmName := fmt .Sprintf (configMapName , lb .Name )
75- config := merge (defaultConfig , nil , lb .Spec .Proxy .Config )
76- err := f .ensureConfigMap (cmName , lb .Namespace , labels , managedConfig , config )
91+ cm , err := f .ensureConfigMap (cmName , lb .Namespace , labels )
92+ if err != nil {
93+ return err
94+ }
95+
96+ err = f .updateConfig (lb , cm )
7797 if err != nil {
7898 return err
7999 }
100+
101+ // For L4 TCP rules
80102 tcpcmName := fmt .Sprintf (tcpConfigMapName , lb .Name )
81- err = f .ensureConfigMap (tcpcmName , lb .Namespace , labels , nil , nil )
103+ _ , err = f .ensureConfigMap (tcpcmName , lb .Namespace , labels )
82104 if err != nil {
83105 return err
84106 }
107+
108+ // For L4 UDP rules
85109 udpcmName := fmt .Sprintf (udpConfigMapName , lb .Name )
86- err = f .ensureConfigMap (udpcmName , lb .Namespace , labels , nil , nil )
110+ _ , err = f .ensureConfigMap (udpcmName , lb .Namespace , labels )
87111
88112 return err
89113}
90114
91- func (f * nginx ) ensureConfigMap (name , namespace string , labels , del , data map [string ]string ) error {
115+ func (f * nginx ) ensureConfigMap (name , namespace string , labels map [string ]string ) ( * v1. ConfigMap , error ) {
92116 cm , err := f .client .CoreV1 ().ConfigMaps (namespace ).Get (name , metav1.GetOptions {})
93117
94- if err != nil && ! errors .IsNotFound (err ) {
118+ if err == nil {
119+ return cm , nil
120+ }
121+
122+ if ! errors .IsNotFound (err ) {
123+ return nil , err
124+ }
125+ cm = & v1.ConfigMap {
126+ ObjectMeta : metav1.ObjectMeta {
127+ Name : name ,
128+ Labels : labels ,
129+ },
130+ }
131+ log .Infof ("About to craete ConfigMap %v/%v for proxy" , namespace , cm .Name )
132+ return f .client .CoreV1 ().ConfigMaps (namespace ).Create (cm )
133+ }
134+
135+ func (f * nginx ) updateConfig (lb * lbapi.LoadBalancer , cm * v1.ConfigMap ) error {
136+ // 1. if cm has unmanaged config, we should generate cm.Data with old method
137+ done , err := f .updateIfHasUnmanagedConfig (lb , cm )
138+ if err != nil || done {
95139 return err
96140 }
97141
98- if errors .IsNotFound (err ) {
99- cm = & v1.ConfigMap {
100- ObjectMeta : metav1.ObjectMeta {
101- Name : name ,
102- Labels : labels ,
103- },
104- Data : data ,
105- }
106- log .Infof ("About to craete ConfigMap %v/%v for proxy" , namespace , cm .Name )
107- _ , nerr := f .client .CoreV1 ().ConfigMaps (namespace ).Create (cm )
108- if nerr != nil {
109- return nerr
110- }
142+ // 2. otherwise, use new method that generate cm.Data by external configs
143+ //externalConfigMaps, preset, override, err := f.getExternalConfig(lb)
144+ externalConfigMaps , preset , override , err := f .getExternalConfig (lb )
145+ if err != nil {
146+ return err
111147 }
148+ newConfig := mapAdd (preset , defaultConfig , lb .Spec .Proxy .Config , override )
149+ bs , _ := json .Marshal (externalConfigMaps )
150+ externalConfigMapsStr := string (bs )
112151
113- if data == nil {
114- // do not update data if data == nil
115- // tcp and udp config map will be changed by others
116- // the controller only need to create it
152+ if reflect .DeepEqual (cm .Data , newConfig ) && externalConfigMapsStr == cm .Annotations [annotationExternalConfigMaps ] {
117153 return nil
118154 }
119155
120- data = merge (cm .Data , del , data )
156+ if cm .Annotations == nil {
157+ cm .Annotations = make (map [string ]string )
158+ }
159+ cm .Annotations [annotationExternalConfigMaps ] = externalConfigMapsStr
160+ cm .Data = newConfig
161+ log .Infof ("About to update ConfigMap %v/%v data, with exnternal configs: %v" , cm .Namespace , cm .Name , externalConfigMapsStr )
162+ _ , err = f .client .CoreV1 ().ConfigMaps (cm .Namespace ).Update (cm )
163+ return err
164+ }
121165
122- if reflect .DeepEqual (cm .Data , data ) {
123- return nil
166+ func (f * nginx ) updateIfHasUnmanagedConfig (lb * lbapi.LoadBalancer , cm * v1.ConfigMap ) (bool , error ) {
167+ var err error
168+
169+ var oldExternalConfigMaps []string
170+ if value , ok := cm .Annotations [annotationExternalConfigMaps ]; ok {
171+ err = json .Unmarshal ([]byte (value ), & oldExternalConfigMaps )
172+ if err != nil {
173+ return false , err
174+ }
175+ }
176+
177+ var unmanagedConifgs map [string ]string
178+ // we consider that configs may contains unmanaged config only if cm is not marked (oldExternalConfigMaps is empty).
179+ if len (oldExternalConfigMaps ) == 0 {
180+ unmanagedConifgs = mapDel (cm .Data , defaultConfig , managedConfig )
181+ }
182+
183+ // if unmanagedConifgs is empty, we can update configs with new method safetly.
184+ // otherwise, we should keep using old method to not lose user's unmanaged conifgs.
185+ if len (unmanagedConifgs ) == 0 {
186+ return false , nil
124187 }
125188
126- // replace cm.Data of data
127- // the data follows the priority
128- // 1. lb.Spec.Proxy.Config
129- // 2. default config
130- cm .Data = data
131- log .Infof ("About to update ConfigMap %v/%v data" , namespace , cm .Name )
132- _ , err = f .client .CoreV1 ().ConfigMaps (namespace ).Update (cm )
189+ log .Warningf ("Found unmanaged configs in %s/%s: %v" , lb .Namespace , lb .Name , unmanagedConifgs )
190+ newConfig := mapAdd (unmanagedConifgs , defaultConfig , lb .Spec .Proxy .Config )
133191
134- return err
192+ if ! reflect .DeepEqual (cm .Data , newConfig ) {
193+ cm .Data = newConfig
194+ log .Warningf ("About to update ConfigMap %v/%v data with old method" , cm .Namespace , cm .Name )
195+ _ , err = f .client .CoreV1 ().ConfigMaps (cm .Namespace ).Update (cm )
196+ }
197+ return true , err
198+ }
199+
200+ func (f * nginx ) getExternalConfig (lb * lbapi.LoadBalancer ) ([]string , map [string ]string , map [string ]string , error ) {
201+
202+ presetConfigMaps := []string {}
203+ overrideConfigMaps := []string {}
204+ presetConfig := make (map [string ]string )
205+ overrideConfig := make (map [string ]string )
206+
207+ for _ , scope := range []string {"platform" , "cluster" , "instance-%s" } {
208+
209+ for _ , postfix := range []string {"" , "-override" } {
210+ name := fmt .Sprintf ("cfg-lb-nginx-%s%s" , scope , postfix )
211+ if scope == "instance-%s" {
212+ name = fmt .Sprintf (name , lb .Name )
213+ }
214+ cm , err := f .client .CoreV1 ().ConfigMaps (lb .Namespace ).Get (name , metav1.GetOptions {})
215+ if errors .IsNotFound (err ) {
216+ continue
217+ }
218+ if err != nil {
219+ return nil , nil , nil , err
220+ }
221+
222+ if postfix == "" {
223+ presetConfig = mapAdd (presetConfig , cm .Data )
224+ presetConfigMaps = append (presetConfigMaps , cm .Name )
225+ continue
226+ }
227+ overrideConfig = mapAdd (overrideConfig , cm .Data )
228+ overrideConfigMaps = append (overrideConfigMaps , cm .Name )
229+ }
230+ }
231+
232+ presetConfigMaps = append (presetConfigMaps , "" ) // empty string delimits 'preset' and 'override'
233+ presetConfigMaps = append (presetConfigMaps , overrideConfigMaps ... )
234+ return presetConfigMaps , presetConfig , overrideConfig , nil
135235}
0 commit comments