From f926cbbf64a6dd58b2f3bf320702846ea92bab59 Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Wed, 11 Sep 2024 17:55:26 -0700 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20util:=20Warning=20handler=20tha?= =?UTF-8?q?t=20discards=20a=20default=20set=20of=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util/apiwarnings/default.go | 49 +++++++++++++++++++++++++ util/apiwarnings/default_test.go | 61 ++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 util/apiwarnings/default.go create mode 100644 util/apiwarnings/default_test.go diff --git a/util/apiwarnings/default.go b/util/apiwarnings/default.go new file mode 100644 index 000000000000..3ef6c8928a9e --- /dev/null +++ b/util/apiwarnings/default.go @@ -0,0 +1,49 @@ +/* +Copyright 2024 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 apiwarnings + +import ( + "fmt" + "regexp" + + "github.com/go-logr/logr" + + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// DomainQualifiedFinalizerWarning is a regular expression that matches a +// warning that the API server returns when a finalizer is not domain-qualified. +func DomainQualifiedFinalizerWarning(domain string) *regexp.Regexp { + return regexp.MustCompile( + fmt.Sprintf("^metadata.finalizers:.*%s.*prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers$", domain), + ) +} + +// DefaultHandler is a handler that discards warnings that are the result of +// decisions made by the Cluster API project, but cannot be immediately +// addressed, and are therefore not helpful to the user. +func DefaultHandler(l logr.Logger) *DiscardMatchingHandler { + return NewDiscardMatchingHandler( + l, + DiscardMatchingHandlerOptions{ + Deduplicate: true, + Expressions: []regexp.Regexp{ + *DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), + }, + }, + ) +} diff --git a/util/apiwarnings/default_test.go b/util/apiwarnings/default_test.go new file mode 100644 index 000000000000..0653ce8c8a30 --- /dev/null +++ b/util/apiwarnings/default_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2024 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 apiwarnings + +import ( + "testing" + + "github.com/go-logr/logr/funcr" + . "github.com/onsi/gomega" +) + +func TestDefaultHandler(t *testing.T) { + tests := []struct { + name string + code int + message string + wantLogged bool + }{ + { + name: "log, if warning does not match any expression", + code: 299, + message: `metadata.finalizers: "foo.example.com": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers`, + wantLogged: true, + }, + { + name: "do not log, if warning matches at least one expression", + code: 299, + message: `metadata.finalizers: "dockermachine.infrastructure.cluster.x-k8s.io": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers`, + wantLogged: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + logged := false + h := DefaultHandler( + funcr.New(func(_, _ string) { + logged = true + }, + funcr.Options{}, + ), + ) + h.HandleWarningHeader(tt.code, "", tt.message) + g.Expect(logged).To(Equal(tt.wantLogged)) + }) + } +} From 1c932b77c904abf6c6efcb40dfcd86e3c1dbd76d Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Fri, 6 Sep 2024 11:36:08 -0700 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20clusterctl:=20Suppress=20API=20?= =?UTF-8?q?warnings=20in=20"move"=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/clusterctl/cmd/move.go | 40 ++++++++++++++++++++++++++++++++++++- util/apiwarnings/default.go | 13 +++++------- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/cmd/clusterctl/cmd/move.go b/cmd/clusterctl/cmd/move.go index 2a51bf2c0d9b..bff3eca854d0 100644 --- a/cmd/clusterctl/cmd/move.go +++ b/cmd/clusterctl/cmd/move.go @@ -23,6 +23,10 @@ import ( "github.com/spf13/cobra" "sigs.k8s.io/cluster-api/cmd/clusterctl/client" + "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster" + "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" + logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log" + "sigs.k8s.io/cluster-api/util/apiwarnings" ) type moveOptions struct { @@ -34,6 +38,7 @@ type moveOptions struct { fromDirectory string toDirectory string dryRun bool + hideAPIWarnings bool } var mo = &moveOptions{} @@ -80,6 +85,8 @@ func init() { "Write Cluster API objects and all dependencies from a management cluster to directory.") moveCmd.Flags().StringVar(&mo.fromDirectory, "from-directory", "", "Read Cluster API objects and all dependencies from a directory into a management cluster.") + moveCmd.Flags().BoolVar(&mo.hideAPIWarnings, "hide-api-warnings", true, + "Hide warnings returned by the API server.") moveCmd.MarkFlagsMutuallyExclusive("to-directory", "to-kubeconfig") moveCmd.MarkFlagsMutuallyExclusive("from-directory", "to-directory") @@ -98,7 +105,38 @@ func runMove() error { return errors.New("please specify a target cluster using the --to-kubeconfig flag when not using --dry-run, --to-directory or --from-directory") } - c, err := client.New(ctx, cfgFile) + configClient, err := config.New(ctx, cfgFile) + if err != nil { + return err + } + + clientOptions := []client.Option{} + if mo.hideAPIWarnings { + clientOptions = append(clientOptions, + client.InjectClusterClientFactory( + func(input client.ClusterClientFactoryInput) (cluster.Client, error) { + return cluster.New( + cluster.Kubeconfig(input.Kubeconfig), + configClient, + cluster.InjectYamlProcessor(input.Processor), + cluster.InjectProxy( + cluster.NewProxy( + cluster.Kubeconfig(input.Kubeconfig), + cluster.InjectWarningHandler( + apiwarnings.DefaultHandler( + logf.Log.WithName("API Server Warning"), + ), + ), + )), + ), nil + }, + ), + // Ensure that the same configClient used by both the client constructor, and the cluster client factory. + client.InjectConfig(configClient), + ) + } + + c, err := client.New(ctx, cfgFile, clientOptions...) if err != nil { return err } diff --git a/util/apiwarnings/default.go b/util/apiwarnings/default.go index 3ef6c8928a9e..1ee82db944ed 100644 --- a/util/apiwarnings/default.go +++ b/util/apiwarnings/default.go @@ -37,13 +37,10 @@ func DomainQualifiedFinalizerWarning(domain string) *regexp.Regexp { // decisions made by the Cluster API project, but cannot be immediately // addressed, and are therefore not helpful to the user. func DefaultHandler(l logr.Logger) *DiscardMatchingHandler { - return NewDiscardMatchingHandler( - l, - DiscardMatchingHandlerOptions{ - Deduplicate: true, - Expressions: []regexp.Regexp{ - *DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), - }, + return &DiscardMatchingHandler{ + Logger: l, + Expressions: []regexp.Regexp{ + *DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), }, - ) + } } From 0f676d18a3490bed25e6be9765ddee5dcc1a97d5 Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Wed, 25 Sep 2024 10:49:27 -0700 Subject: [PATCH 3/6] =?UTF-8?q?fixup!=20=E2=9C=A8=20util:=20Warning=20hand?= =?UTF-8?q?ler=20that=20discards=20a=20default=20set=20of=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all handlers to one file, expressions to another --- util/apiwarnings/default_test.go | 61 ------------------- .../{default.go => expressions.go} | 16 ----- util/apiwarnings/handler.go | 14 +++++ util/apiwarnings/handler_test.go | 37 +++++++++++ 4 files changed, 51 insertions(+), 77 deletions(-) delete mode 100644 util/apiwarnings/default_test.go rename util/apiwarnings/{default.go => expressions.go} (67%) diff --git a/util/apiwarnings/default_test.go b/util/apiwarnings/default_test.go deleted file mode 100644 index 0653ce8c8a30..000000000000 --- a/util/apiwarnings/default_test.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2024 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 apiwarnings - -import ( - "testing" - - "github.com/go-logr/logr/funcr" - . "github.com/onsi/gomega" -) - -func TestDefaultHandler(t *testing.T) { - tests := []struct { - name string - code int - message string - wantLogged bool - }{ - { - name: "log, if warning does not match any expression", - code: 299, - message: `metadata.finalizers: "foo.example.com": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers`, - wantLogged: true, - }, - { - name: "do not log, if warning matches at least one expression", - code: 299, - message: `metadata.finalizers: "dockermachine.infrastructure.cluster.x-k8s.io": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers`, - wantLogged: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - logged := false - h := DefaultHandler( - funcr.New(func(_, _ string) { - logged = true - }, - funcr.Options{}, - ), - ) - h.HandleWarningHeader(tt.code, "", tt.message) - g.Expect(logged).To(Equal(tt.wantLogged)) - }) - } -} diff --git a/util/apiwarnings/default.go b/util/apiwarnings/expressions.go similarity index 67% rename from util/apiwarnings/default.go rename to util/apiwarnings/expressions.go index 1ee82db944ed..28d195aa039e 100644 --- a/util/apiwarnings/default.go +++ b/util/apiwarnings/expressions.go @@ -19,10 +19,6 @@ package apiwarnings import ( "fmt" "regexp" - - "github.com/go-logr/logr" - - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) // DomainQualifiedFinalizerWarning is a regular expression that matches a @@ -32,15 +28,3 @@ func DomainQualifiedFinalizerWarning(domain string) *regexp.Regexp { fmt.Sprintf("^metadata.finalizers:.*%s.*prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers$", domain), ) } - -// DefaultHandler is a handler that discards warnings that are the result of -// decisions made by the Cluster API project, but cannot be immediately -// addressed, and are therefore not helpful to the user. -func DefaultHandler(l logr.Logger) *DiscardMatchingHandler { - return &DiscardMatchingHandler{ - Logger: l, - Expressions: []regexp.Regexp{ - *DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), - }, - } -} diff --git a/util/apiwarnings/handler.go b/util/apiwarnings/handler.go index 3e74ea5c05c5..3e1d2c6e9bf0 100644 --- a/util/apiwarnings/handler.go +++ b/util/apiwarnings/handler.go @@ -20,6 +20,8 @@ import ( "regexp" "github.com/go-logr/logr" + + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) // DiscardMatchingHandler is a handler that discards API server warnings @@ -49,3 +51,15 @@ func (h *DiscardMatchingHandler) HandleWarningHeader(code int, _, message string h.Logger.Info(message) } + +// DefaultHandler is a handler that discards warnings that are the result of +// decisions made by the Cluster API project, but cannot be immediately +// addressed, and are therefore not helpful to the user. +func DefaultHandler(l logr.Logger) *DiscardMatchingHandler { + return &DiscardMatchingHandler{ + Logger: l, + Expressions: []regexp.Regexp{ + *DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), + }, + } +} diff --git a/util/apiwarnings/handler_test.go b/util/apiwarnings/handler_test.go index f2d6abd6ebbc..c78bd3e42100 100644 --- a/util/apiwarnings/handler_test.go +++ b/util/apiwarnings/handler_test.go @@ -95,3 +95,40 @@ func TestDiscardMatchingHandler_uninitialized(t *testing.T) { h.HandleWarningHeader(299, "", "example") }).ToNot(Panic()) } + +func TestDefaultHandler(t *testing.T) { + tests := []struct { + name string + code int + message string + wantLogged bool + }{ + { + name: "log, if warning does not match any expression", + code: 299, + message: `metadata.finalizers: "foo.example.com": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers`, + wantLogged: true, + }, + { + name: "do not log, if warning matches at least one expression", + code: 299, + message: `metadata.finalizers: "dockermachine.infrastructure.cluster.x-k8s.io": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers`, + wantLogged: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + logged := false + h := DefaultHandler( + funcr.New(func(_, _ string) { + logged = true + }, + funcr.Options{}, + ), + ) + h.HandleWarningHeader(tt.code, "", tt.message) + g.Expect(logged).To(Equal(tt.wantLogged)) + }) + } +} From 689a6e228bf550b8a26b7573e1dfd8fabd43668e Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Wed, 25 Sep 2024 13:00:54 -0700 Subject: [PATCH 4/6] =?UTF-8?q?fixup!=20=E2=9C=A8=20clusterctl:=20Suppress?= =?UTF-8?q?=20API=20warnings=20in=20"move"=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow user to choose which warnings to hide --- cmd/clusterctl/cmd/move.go | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/cmd/clusterctl/cmd/move.go b/cmd/clusterctl/cmd/move.go index bff3eca854d0..9e33542063ab 100644 --- a/cmd/clusterctl/cmd/move.go +++ b/cmd/clusterctl/cmd/move.go @@ -18,9 +18,11 @@ package cmd import ( "context" + "fmt" "github.com/pkg/errors" "github.com/spf13/cobra" + "k8s.io/client-go/rest" "sigs.k8s.io/cluster-api/cmd/clusterctl/client" "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster" @@ -38,7 +40,7 @@ type moveOptions struct { fromDirectory string toDirectory string dryRun bool - hideAPIWarnings bool + hideAPIWarnings string } var mo = &moveOptions{} @@ -85,8 +87,8 @@ func init() { "Write Cluster API objects and all dependencies from a management cluster to directory.") moveCmd.Flags().StringVar(&mo.fromDirectory, "from-directory", "", "Read Cluster API objects and all dependencies from a directory into a management cluster.") - moveCmd.Flags().BoolVar(&mo.hideAPIWarnings, "hide-api-warnings", true, - "Hide warnings returned by the API server.") + moveCmd.Flags().StringVar(&mo.hideAPIWarnings, "hide-api-warnings", "default", + "Set of API server warnings to hide. Valid sets are \"default\" (includes metadata.finalizer warnings), \"all\" , and \"none\".") moveCmd.MarkFlagsMutuallyExclusive("to-directory", "to-kubeconfig") moveCmd.MarkFlagsMutuallyExclusive("from-directory", "to-directory") @@ -111,7 +113,23 @@ func runMove() error { } clientOptions := []client.Option{} - if mo.hideAPIWarnings { + + var warningHandler rest.WarningHandler + switch mo.hideAPIWarnings { + case "all": + warningHandler = rest.NoWarnings{} + case "default": + warningHandler = apiwarnings.DefaultHandler(logf.Log.WithName("API Server Warning")) + case "none": + warningHandler = nil + default: + return fmt.Errorf( + "set of API warnings %q is unknown; choose \"default\", \"all\", or \"none\"", + mo.hideAPIWarnings, + ) + } + + if warningHandler != nil { clientOptions = append(clientOptions, client.InjectClusterClientFactory( func(input client.ClusterClientFactoryInput) (cluster.Client, error) { @@ -123,9 +141,7 @@ func runMove() error { cluster.NewProxy( cluster.Kubeconfig(input.Kubeconfig), cluster.InjectWarningHandler( - apiwarnings.DefaultHandler( - logf.Log.WithName("API Server Warning"), - ), + warningHandler, ), )), ), nil From e1e90d40e3a22c1cd677de7de1e59cf95a3edfc7 Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Thu, 26 Sep 2024 08:50:02 -0700 Subject: [PATCH 5/6] =?UTF-8?q?fixup!=20fixup!=20=E2=9C=A8=20util:=20Warni?= =?UTF-8?q?ng=20handler=20that=20discards=20a=20default=20set=20of=20messa?= =?UTF-8?q?ges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make return value easier to consume --- util/apiwarnings/expressions.go | 4 ++-- util/apiwarnings/handler.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/util/apiwarnings/expressions.go b/util/apiwarnings/expressions.go index 28d195aa039e..d97b44f8af30 100644 --- a/util/apiwarnings/expressions.go +++ b/util/apiwarnings/expressions.go @@ -23,8 +23,8 @@ import ( // DomainQualifiedFinalizerWarning is a regular expression that matches a // warning that the API server returns when a finalizer is not domain-qualified. -func DomainQualifiedFinalizerWarning(domain string) *regexp.Regexp { - return regexp.MustCompile( +func DomainQualifiedFinalizerWarning(domain string) regexp.Regexp { + return *regexp.MustCompile( fmt.Sprintf("^metadata.finalizers:.*%s.*prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers$", domain), ) } diff --git a/util/apiwarnings/handler.go b/util/apiwarnings/handler.go index 3e1d2c6e9bf0..048dd28b0553 100644 --- a/util/apiwarnings/handler.go +++ b/util/apiwarnings/handler.go @@ -59,7 +59,7 @@ func DefaultHandler(l logr.Logger) *DiscardMatchingHandler { return &DiscardMatchingHandler{ Logger: l, Expressions: []regexp.Regexp{ - *DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), + DomainQualifiedFinalizerWarning(clusterv1.GroupVersion.Group), }, } } From 6bc6f81ee78a06277296ed060b87321d78e90d0c Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Thu, 26 Sep 2024 13:13:30 -0700 Subject: [PATCH 6/6] =?UTF-8?q?fixup!=20fixup!=20=E2=9C=A8=20clusterctl:?= =?UTF-8?q?=20Suppress=20API=20warnings=20in=20"move"=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add LogAllHandler for consistent prefix, and DiscardAllHandler for convenience --- cmd/clusterctl/cmd/move.go | 7 ++++-- util/apiwarnings/handler.go | 9 +++++++ util/apiwarnings/handler_test.go | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/cmd/clusterctl/cmd/move.go b/cmd/clusterctl/cmd/move.go index 9e33542063ab..83b4abac39dd 100644 --- a/cmd/clusterctl/cmd/move.go +++ b/cmd/clusterctl/cmd/move.go @@ -117,11 +117,14 @@ func runMove() error { var warningHandler rest.WarningHandler switch mo.hideAPIWarnings { case "all": - warningHandler = rest.NoWarnings{} + // Hide all warnings. + warningHandler = apiwarnings.DiscardAllHandler case "default": + // Hide only the default set of warnings. warningHandler = apiwarnings.DefaultHandler(logf.Log.WithName("API Server Warning")) case "none": - warningHandler = nil + // Hide no warnings. + warningHandler = apiwarnings.LogAllHandler(logf.Log.WithName("API Server Warning")) default: return fmt.Errorf( "set of API warnings %q is unknown; choose \"default\", \"all\", or \"none\"", diff --git a/util/apiwarnings/handler.go b/util/apiwarnings/handler.go index 048dd28b0553..f9664bfcd33d 100644 --- a/util/apiwarnings/handler.go +++ b/util/apiwarnings/handler.go @@ -20,6 +20,7 @@ import ( "regexp" "github.com/go-logr/logr" + "k8s.io/client-go/rest" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) @@ -63,3 +64,11 @@ func DefaultHandler(l logr.Logger) *DiscardMatchingHandler { }, } } + +// DiscardAllHandler is a handler that discards all warnings. +var DiscardAllHandler = rest.NoWarnings{} + +// LogAllHandler is a handler that logs all warnings. +func LogAllHandler(l logr.Logger) rest.WarningHandler { + return &DiscardMatchingHandler{Logger: l} +} diff --git a/util/apiwarnings/handler_test.go b/util/apiwarnings/handler_test.go index c78bd3e42100..0dfcf4e482b1 100644 --- a/util/apiwarnings/handler_test.go +++ b/util/apiwarnings/handler_test.go @@ -132,3 +132,46 @@ func TestDefaultHandler(t *testing.T) { }) } } + +func TestLogAllHandler(t *testing.T) { + tests := []struct { + name string + code int + message string + wantLogged bool + }{ + { + name: "log, if code is 299, and message is not empty", + code: 299, + message: "warning", + wantLogged: true, + }, + { + name: "do not log, if code is not 299", + code: 0, + message: "warning", + wantLogged: false, + }, + { + name: "do not log, if message is empty", + code: 299, + message: "", + wantLogged: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + logged := false + h := LogAllHandler( + funcr.New(func(_, _ string) { + logged = true + }, + funcr.Options{}, + ), + ) + h.HandleWarningHeader(tt.code, "", tt.message) + g.Expect(logged).To(Equal(tt.wantLogged)) + }) + } +}