Skip to content

Commit 1984678

Browse files
author
Jim Ryan
authored
Chore/add events to configmap (#6819)
* add configmap validation events * add events to configmap, add python test * error messages if config is invalid * remove v1 * add keep alive requests event
1 parent 582198a commit 1984678

File tree

7 files changed

+268
-34
lines changed

7 files changed

+268
-34
lines changed

cmd/nginx-ingress/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func main() {
151151
mustProcessGlobalConfiguration(ctx)
152152

153153
cfgParams := configs.NewDefaultConfigParams(ctx, *nginxPlus)
154-
cfgParams = processConfigMaps(kubeClient, cfgParams, nginxManager, templateExecutor)
154+
cfgParams = processConfigMaps(kubeClient, cfgParams, nginxManager, templateExecutor, eventRecorder)
155155

156156
staticCfgParams := &configs.StaticConfigParams{
157157
DisableIPV6: *disableIPV6,
@@ -854,7 +854,7 @@ func mustProcessGlobalConfiguration(ctx context.Context) {
854854
}
855855
}
856856

857-
func processConfigMaps(kubeClient *kubernetes.Clientset, cfgParams *configs.ConfigParams, nginxManager nginx.Manager, templateExecutor *version1.TemplateExecutor) *configs.ConfigParams {
857+
func processConfigMaps(kubeClient *kubernetes.Clientset, cfgParams *configs.ConfigParams, nginxManager nginx.Manager, templateExecutor *version1.TemplateExecutor, eventLog record.EventRecorder) *configs.ConfigParams {
858858
l := nl.LoggerFromContext(cfgParams.Context)
859859
if *nginxConfigMaps != "" {
860860
ns, name, err := k8s.ParseNamespaceName(*nginxConfigMaps)
@@ -865,7 +865,7 @@ func processConfigMaps(kubeClient *kubernetes.Clientset, cfgParams *configs.Conf
865865
if err != nil {
866866
nl.Fatalf(l, "Error when getting %v: %v", *nginxConfigMaps, err)
867867
}
868-
cfgParams = configs.ParseConfigMap(cfgParams.Context, cfm, *nginxPlus, *appProtect, *appProtectDos, *enableTLSPassthrough)
868+
cfgParams, _ = configs.ParseConfigMap(cfgParams.Context, cfm, *nginxPlus, *appProtect, *appProtectDos, *enableTLSPassthrough, eventLog)
869869
if cfgParams.MainServerSSLDHParamFileContent != nil {
870870
fileName, err := nginxManager.CreateDHParam(*cfgParams.MainServerSSLDHParamFileContent)
871871
if err != nil {

internal/configs/configmaps.go

Lines changed: 147 additions & 22 deletions
Large diffs are not rendered by default.

internal/configs/configmaps_test.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66

77
v1 "k8s.io/api/core/v1"
8+
"k8s.io/client-go/tools/record"
89
)
910

1011
func TestParseConfigMapWithAppProtectCompressedRequestsAction(t *testing.T) {
@@ -45,7 +46,7 @@ func TestParseConfigMapWithAppProtectCompressedRequestsAction(t *testing.T) {
4546
"app-protect-compressed-requests-action": test.action,
4647
},
4748
}
48-
result := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough)
49+
result, _ := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
4950
if result.MainAppProtectCompressedRequestsAction != test.expect {
5051
t.Errorf("ParseConfigMap() returned %q but expected %q for the case %s", result.MainAppProtectCompressedRequestsAction, test.expect, test.msg)
5152
}
@@ -114,7 +115,7 @@ func TestParseConfigMapWithAppProtectReconnectPeriod(t *testing.T) {
114115
"app-protect-reconnect-period-seconds": test.period,
115116
},
116117
}
117-
result := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough)
118+
result, _ := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
118119
if result.MainAppProtectReconnectPeriod != test.expect {
119120
t.Errorf("ParseConfigMap() returned %q but expected %q for the case %s", result.MainAppProtectReconnectPeriod, test.expect, test.msg)
120121
}
@@ -155,7 +156,7 @@ func TestParseConfigMapWithTLSPassthroughProxyProtocol(t *testing.T) {
155156
"real-ip-header": test.realIPheader,
156157
},
157158
}
158-
result := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough)
159+
result, _ := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
159160
if result.RealIPHeader != test.want {
160161
t.Errorf("want %q, got %q", test.want, result.RealIPHeader)
161162
}
@@ -197,7 +198,7 @@ func TestParseConfigMapWithoutTLSPassthroughProxyProtocol(t *testing.T) {
197198
"real-ip-header": test.realIPheader,
198199
},
199200
}
200-
result := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough)
201+
result, _ := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
201202
if result.RealIPHeader != test.want {
202203
t.Errorf("want %q, got %q", test.want, result.RealIPHeader)
203204
}
@@ -244,7 +245,7 @@ func TestParseConfigMapAccessLog(t *testing.T) {
244245
"access-log-off": test.accessLogOff,
245246
},
246247
}
247-
result := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough)
248+
result, _ := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
248249
if result.MainAccessLog != test.want {
249250
t.Errorf("want %q, got %q", test.want, result.MainAccessLog)
250251
}
@@ -276,10 +277,14 @@ func TestParseConfigMapAccessLogDefault(t *testing.T) {
276277
"access-log-off": "False",
277278
},
278279
}
279-
result := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough)
280+
result, _ := ParseConfigMap(context.Background(), cm, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
280281
if result.MainAccessLog != test.want {
281282
t.Errorf("want %q, got %q", test.want, result.MainAccessLog)
282283
}
283284
})
284285
}
285286
}
287+
288+
func makeEventLogger() record.EventRecorder {
289+
return record.NewFakeRecorder(1024)
290+
}

internal/k8s/controller.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -841,9 +841,10 @@ func (lbc *LoadBalancerController) createExtendedResources(resources []Resource)
841841
func (lbc *LoadBalancerController) updateAllConfigs() {
842842
ctx := nl.ContextWithLogger(context.Background(), lbc.Logger)
843843
cfgParams := configs.NewDefaultConfigParams(ctx, lbc.isNginxPlus)
844+
var isNGINXConfigValid bool
844845

845846
if lbc.configMap != nil {
846-
cfgParams = configs.ParseConfigMap(ctx, lbc.configMap, lbc.isNginxPlus, lbc.appProtectEnabled, lbc.appProtectDosEnabled, lbc.configuration.isTLSPassthroughEnabled)
847+
cfgParams, isNGINXConfigValid = configs.ParseConfigMap(ctx, lbc.configMap, lbc.isNginxPlus, lbc.appProtectEnabled, lbc.appProtectDosEnabled, lbc.configuration.isTLSPassthroughEnabled, lbc.recorder)
847848
}
848849

849850
resources := lbc.configuration.GetResources()
@@ -869,8 +870,11 @@ func (lbc *LoadBalancerController) updateAllConfigs() {
869870
}
870871

871872
if lbc.configMap != nil {
872-
key := getResourceKey(&lbc.configMap.ObjectMeta)
873-
lbc.recorder.Eventf(lbc.configMap, eventType, eventTitle, "Configuration from %v was updated %s", key, eventWarningMessage)
873+
if isNGINXConfigValid {
874+
lbc.recorder.Event(lbc.configMap, api_v1.EventTypeNormal, "Updated", fmt.Sprintf("ConfigMap %s/%s updated without error", lbc.configMap.GetNamespace(), lbc.configMap.GetName()))
875+
} else {
876+
lbc.recorder.Event(lbc.configMap, api_v1.EventTypeWarning, "UpdatedWithError", fmt.Sprintf("ConfigMap %s/%s updated with errors. Ignoring invalid values", lbc.configMap.GetNamespace(), lbc.configMap.GetName()))
877+
}
874878
}
875879

876880
gc := lbc.configuration.GetGlobalConfiguration()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: nginx-config
5+
namespace: nginx-ingress
6+
data:
7+
proxy-buffering: "invalid" # Invalid boolean
8+
proxy-read-timeout: "60s"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: nginx-config
5+
namespace: nginx-ingress
6+
data:
7+
proxy-buffering: "on"
8+
proxy-read-timeout: "60s"

tests/suite/test_virtual_server_configmap_keys.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from settings import DEPLOYMENTS, TEST_DATA
33
from suite.utils.resources_utils import (
44
get_events,
5+
get_events_for_object,
56
get_file_contents,
67
get_first_pod_name,
78
get_pods_amount,
@@ -124,6 +125,23 @@ def assert_defaults_of_ssl_keys(config):
124125
assert "http2 on;" not in config
125126

126127

128+
def assert_event(event_list, event_type, reason, message_substring):
129+
"""
130+
Assert that an event with specific type, reason, and message substring exists.
131+
132+
:param event_list: List of events
133+
:param event_type: 'Normal' or 'Warning'
134+
:param reason: Event reason
135+
:param message_substring: Substring expected in the event message
136+
"""
137+
for event in event_list:
138+
if event.type == event_type and event.reason == reason and message_substring in event.message:
139+
return
140+
assert (
141+
False
142+
), f"Expected event with type '{event_type}', reason '{reason}', and message containing '{message_substring}' not found."
143+
144+
127145
@pytest.fixture(scope="function")
128146
def clean_up(request, kube_apis, ingress_controller_prerequisites, test_namespace) -> None:
129147
"""
@@ -404,3 +422,69 @@ def test_ssl_keys(
404422
)
405423
assert_update_event_count_increased(virtual_server_setup, step_2_events, step_1_events)
406424
assert_defaults_of_ssl_keys(step_2_config)
425+
426+
def test_configmap_events(
427+
self,
428+
kube_apis,
429+
ingress_controller_prerequisites,
430+
crd_ingress_controller,
431+
virtual_server_setup,
432+
clean_up,
433+
):
434+
ingress_controller_prerequisites.namespace
435+
configmap_name = ingress_controller_prerequisites.config_map["metadata"]["name"]
436+
configmap_namespace = ingress_controller_prerequisites.config_map["metadata"]["namespace"]
437+
438+
# Step 1: Update ConfigMap with valid parameters
439+
print("Updating ConfigMap with valid parameters")
440+
replace_configmap_from_yaml(
441+
kube_apis.v1,
442+
configmap_name,
443+
configmap_namespace,
444+
f"{TEST_DATA}/virtual-server-configmap-keys/configmap-valid.yaml",
445+
)
446+
wait_before_test(1)
447+
448+
# Get events for the ConfigMap
449+
events = get_events_for_object(
450+
kube_apis.v1,
451+
configmap_namespace,
452+
configmap_name,
453+
)
454+
455+
assert len(events) >= 1
456+
457+
# Assert that the 'updated without error' event is present
458+
assert_event(
459+
events,
460+
"Normal",
461+
"Updated",
462+
f"ConfigMap {configmap_namespace}/{configmap_name} updated without error",
463+
)
464+
465+
# Step 2: Update ConfigMap with invalid parameters
466+
print("Updating ConfigMap with invalid parameters")
467+
replace_configmap_from_yaml(
468+
kube_apis.v1,
469+
configmap_name,
470+
configmap_namespace,
471+
f"{TEST_DATA}/virtual-server-configmap-keys/configmap-invalid.yaml",
472+
)
473+
wait_before_test(1)
474+
475+
# Get events for the ConfigMap
476+
events = get_events_for_object(
477+
kube_apis.v1,
478+
configmap_namespace,
479+
configmap_name,
480+
)
481+
482+
assert len(events) >= 1
483+
484+
# Assert that the 'updated with errors' event is present
485+
assert_event(
486+
events,
487+
"Warning",
488+
"UpdatedWithError",
489+
f"ConfigMap {configmap_namespace}/{configmap_name} updated with errors. Ignoring invalid values",
490+
)

0 commit comments

Comments
 (0)