From a63e41d16a22cb2256004672b0d93330d4f2f514 Mon Sep 17 00:00:00 2001 From: Ruixian Song Date: Tue, 2 Aug 2022 16:38:33 -0700 Subject: [PATCH] Add controller logic for custom response headers adding unit test for custom response headers --- .../features/customresponseheaders.go | 47 +++++++++ .../features/customresponseheaders_test.go | 99 +++++++++++++++++++ pkg/backends/syncer.go | 1 + 3 files changed, 147 insertions(+) create mode 100644 pkg/backends/features/customresponseheaders.go create mode 100644 pkg/backends/features/customresponseheaders_test.go diff --git a/pkg/backends/features/customresponseheaders.go b/pkg/backends/features/customresponseheaders.go new file mode 100644 index 0000000000..878d70c845 --- /dev/null +++ b/pkg/backends/features/customresponseheaders.go @@ -0,0 +1,47 @@ +/* +Copyright 2022 The Kubernetes 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 features + +import ( + "reflect" + + "k8s.io/ingress-gce/pkg/composite" + "k8s.io/ingress-gce/pkg/utils" + "k8s.io/klog/v2" +) + +// EnsureCustomResponseHeaders reads the CustomResponseHeaders configuration specified in the ServicePort.BackendConfig +// and applies it to the BackendService. It returns true if there was a difference between +// current settings on the BackendService and ServicePort.BackendConfig. + +func EnsureCustomResponseHeaders(sp utils.ServicePort, be *composite.BackendService) bool { + desiredHeaders := []string{} + if sp.BackendConfig.Spec.CustomResponseHeaders != nil { + desiredHeaders = sp.BackendConfig.Spec.CustomResponseHeaders.Headers + } + currentHeaders := []string{} + if be.CustomResponseHeaders != nil { + currentHeaders = be.CustomResponseHeaders + } + + if !reflect.DeepEqual(desiredHeaders, currentHeaders) { + be.CustomResponseHeaders = desiredHeaders + klog.V(2).Infof("Updated Custom Response Headers for service %v/%v.", sp.ID.Service.Namespace, sp.ID.Service.Name) + return true + } + + return false +} diff --git a/pkg/backends/features/customresponseheaders_test.go b/pkg/backends/features/customresponseheaders_test.go new file mode 100644 index 0000000000..0c663e5aa8 --- /dev/null +++ b/pkg/backends/features/customresponseheaders_test.go @@ -0,0 +1,99 @@ +/* +Copyright 2022 The Kubernetes 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 features + +import ( + "testing" + + backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + "k8s.io/ingress-gce/pkg/composite" + "k8s.io/ingress-gce/pkg/utils" +) + +var testCustomResponseHeader = []string{"X-TEST-HEADER:{test-value}"} + +func TestEnsureCustomResponseHeaders(t *testing.T) { + testCases := []struct { + desc string + sp utils.ServicePort + be *composite.BackendService + updateExpected bool + }{ + { + desc: "Custom Response Headers missing from both ends, no update needed", + sp: utils.ServicePort{BackendConfig: &backendconfigv1.BackendConfig{}}, + be: &composite.BackendService{}, + updateExpected: false, + }, + { + desc: "No header in current setting, new headers in backendConfig, update needed", + sp: utils.ServicePort{BackendConfig: &backendconfigv1.BackendConfig{ + Spec: backendconfigv1.BackendConfigSpec{ + CustomResponseHeaders: &backendconfigv1.CustomResponseHeadersConfig{ + Headers: testCustomResponseHeader, + }, + }, + }}, + be: &composite.BackendService{}, + updateExpected: true, + }, + { + desc: "Having headers in current setting, no header in backendConfig, update needed", + sp: utils.ServicePort{BackendConfig: &backendconfigv1.BackendConfig{}}, + be: &composite.BackendService{ + CustomResponseHeaders: testCustomResponseHeader, + }, + updateExpected: true, + }, + { + desc: "Having headers in current setting, new headers in backendConfig, update needed", + sp: utils.ServicePort{BackendConfig: &backendconfigv1.BackendConfig{ + Spec: backendconfigv1.BackendConfigSpec{ + CustomResponseHeaders: &backendconfigv1.CustomResponseHeadersConfig{ + Headers: append(testCustomResponseHeader, "X-TEST-HEADER2:{test-value2}"), + }, + }, + }}, + be: &composite.BackendService{ + CustomResponseHeaders: testCustomResponseHeader, + }, + updateExpected: true, + }, + { + desc: "Identical headers, no update", + sp: utils.ServicePort{BackendConfig: &backendconfigv1.BackendConfig{ + Spec: backendconfigv1.BackendConfigSpec{ + CustomResponseHeaders: &backendconfigv1.CustomResponseHeadersConfig{ + Headers: testCustomResponseHeader, + }, + }, + }}, + be: &composite.BackendService{ + CustomResponseHeaders: testCustomResponseHeader, + }, + updateExpected: false, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + result := EnsureCustomResponseHeaders(tc.sp, tc.be) + if result != tc.updateExpected { + t.Errorf("Expected %v but got %v", tc.updateExpected, result) + } + }) + } +} diff --git a/pkg/backends/syncer.go b/pkg/backends/syncer.go index 3d286f91ab..049c5aed3b 100644 --- a/pkg/backends/syncer.go +++ b/pkg/backends/syncer.go @@ -108,6 +108,7 @@ func (s *backendSyncer) ensureBackendService(sp utils.ServicePort) error { needUpdate = features.EnsureDraining(sp, be) || needUpdate needUpdate = features.EnsureAffinity(sp, be) || needUpdate needUpdate = features.EnsureCustomRequestHeaders(sp, be) || needUpdate + needUpdate = features.EnsureCustomResponseHeaders(sp, be) || needUpdate needUpdate = features.EnsureLogging(sp, be) || needUpdate }