From 37c81ed79ac6836bce4b96f888aa407dc18d747c Mon Sep 17 00:00:00 2001 From: Suresh Kumar Ponnusamy Date: Wed, 28 Aug 2019 10:51:14 +0530 Subject: [PATCH 1/2] Support TLS Server Name overrides in kubeconfig file Signed-off-by: Suresh Kumar Ponnusamy --- .../pkg/genericclioptions/config_flags.go | 9 ++++++++ .../client-go/tools/clientcmd/api/types.go | 3 +++ .../client-go/tools/clientcmd/api/v1/types.go | 3 +++ .../tools/clientcmd/client_config.go | 5 +++++ .../tools/clientcmd/client_config_test.go | 22 +++++++++++++++++++ .../client-go/tools/clientcmd/overrides.go | 4 ++++ .../kubectl/pkg/cmd/config/create_cluster.go | 13 ++++++++--- .../pkg/cmd/config/create_cluster_test.go | 6 ++++- 8 files changed, 61 insertions(+), 4 deletions(-) diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go index 34b1f8e3a0f..9f93b8fd253 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go +++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go @@ -41,6 +41,7 @@ const ( flagContext = "context" flagNamespace = "namespace" flagAPIServer = "server" + flagTLSServerName = "tls-server-name" flagInsecure = "insecure-skip-tls-verify" flagCertFile = "client-certificate" flagKeyFile = "client-key" @@ -84,6 +85,7 @@ type ConfigFlags struct { Context *string Namespace *string APIServer *string + TLSServerName *string Insecure *bool CertFile *string KeyFile *string @@ -160,6 +162,9 @@ func (f *ConfigFlags) toRawKubeConfigLoader() clientcmd.ClientConfig { if f.APIServer != nil { overrides.ClusterInfo.Server = *f.APIServer } + if f.TLSServerName != nil { + overrides.ClusterInfo.TLSServerName = *f.TLSServerName + } if f.CAFile != nil { overrides.ClusterInfo.CertificateAuthority = *f.CAFile } @@ -294,6 +299,9 @@ func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) { if f.APIServer != nil { flags.StringVarP(f.APIServer, flagAPIServer, "s", *f.APIServer, "The address and port of the Kubernetes API server") } + if f.TLSServerName != nil { + flags.StringVar(f.TLSServerName, flagTLSServerName, *f.TLSServerName, "Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used") + } if f.Insecure != nil { flags.BoolVar(f.Insecure, flagInsecure, *f.Insecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure") } @@ -329,6 +337,7 @@ func NewConfigFlags(usePersistentConfig bool) *ConfigFlags { Context: stringptr(""), Namespace: stringptr(""), APIServer: stringptr(""), + TLSServerName: stringptr(""), CertFile: stringptr(""), KeyFile: stringptr(""), CAFile: stringptr(""), diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/api/types.go b/staging/src/k8s.io/client-go/tools/clientcmd/api/types.go index 1f1209f8d47..44317dd019a 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/api/types.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/api/types.go @@ -70,6 +70,9 @@ type Cluster struct { LocationOfOrigin string // Server is the address of the kubernetes cluster (https://hostname:port). Server string `json:"server"` + // TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used. + // +optional + TLSServerName string `json:"tls-server-name,omitempty"` // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. // +optional InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go b/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go index 2159ffc79d5..8ccacd3f879 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go @@ -63,6 +63,9 @@ type Preferences struct { type Cluster struct { // Server is the address of the kubernetes cluster (https://hostname:port). Server string `json:"server"` + // TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used. + // +optional + TLSServerName string `json:"tls-server-name,omitempty"` // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. // +optional InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go index 44115130d99..6b5f3f7375d 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go @@ -210,6 +210,7 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClientConfig.CAFile = configClusterInfo.CertificateAuthority configClientConfig.CAData = configClusterInfo.CertificateAuthorityData configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify + configClientConfig.ServerName = configClusterInfo.TLSServerName mergo.MergeWithOverwrite(mergedConfig, configClientConfig) return mergedConfig, nil @@ -460,6 +461,10 @@ func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) { mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData } + if config.overrides.ClusterInfo.TLSServerName != "" { + mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName + } + return *mergedClusterInfo, nil } diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go index aae65f8582b..e89ce147bd0 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go @@ -180,6 +180,25 @@ func TestCAOverridesCAData(t *testing.T) { matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t) } +func TestTLSServerName(t *testing.T) { + config := createValidTestConfig() + + clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{ + ClusterInfo: clientcmdapi.Cluster{ + TLSServerName: "overridden-server-name", + }, + }, nil) + + actualCfg, err := clientBuilder.ClientConfig() + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + matchStringArg("overridden-server-name", actualCfg.ServerName, t) + matchStringArg("", actualCfg.TLSClientConfig.CAFile, t) + matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t) +} + func TestMergeContext(t *testing.T) { const namespace = "overridden-namespace" @@ -411,6 +430,7 @@ func TestCreateClean(t *testing.T) { matchStringArg("", clientConfig.APIPath, t) matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t) matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t) + matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t) } func TestCreateCleanWithPrefix(t *testing.T) { @@ -461,6 +481,7 @@ func TestCreateCleanDefault(t *testing.T) { } matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t) + matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t) matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t) matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t) } @@ -477,6 +498,7 @@ func TestCreateCleanDefaultCluster(t *testing.T) { } matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t) + matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t) matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t) matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t) } diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go index bfca032847b..95cba0fac27 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go @@ -71,6 +71,7 @@ type ClusterOverrideFlags struct { APIVersion FlagInfo CertificateAuthority FlagInfo InsecureSkipTLSVerify FlagInfo + TLSServerName FlagInfo } // FlagInfo contains information about how to register a flag. This struct is useful if you want to provide a way for an extender to @@ -145,6 +146,7 @@ const ( FlagContext = "context" FlagNamespace = "namespace" FlagAPIServer = "server" + FlagTLSServerName = "tls-server-name" FlagInsecure = "insecure-skip-tls-verify" FlagCertFile = "client-certificate" FlagKeyFile = "client-key" @@ -189,6 +191,7 @@ func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags { APIServer: FlagInfo{prefix + FlagAPIServer, "", "", "The address and port of the Kubernetes API server"}, CertificateAuthority: FlagInfo{prefix + FlagCAFile, "", "", "Path to a cert file for the certificate authority"}, InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"}, + TLSServerName: FlagInfo{prefix + FlagTLSServerName, "", "", "If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used."}, } } @@ -226,6 +229,7 @@ func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, f flagNames.APIServer.BindStringFlag(flags, &clusterInfo.Server) flagNames.CertificateAuthority.BindStringFlag(flags, &clusterInfo.CertificateAuthority) flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify) + flagNames.TLSServerName.BindStringFlag(flags, &clusterInfo.TLSServerName) } // BindFlags is a convenience method to bind the specified flags to their associated variables diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go index 6860b2ce080..f4b4c0ac1b9 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go @@ -24,7 +24,6 @@ import ( "path/filepath" "github.com/spf13/cobra" - "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" cliflag "k8s.io/component-base/cli/flag" @@ -37,6 +36,7 @@ type createClusterOptions struct { configAccess clientcmd.ConfigAccess name string server cliflag.StringFlag + tlsServerName cliflag.StringFlag insecureSkipTLSVerify cliflag.Tristate certificateAuthority cliflag.StringFlag embedCAData cliflag.Tristate @@ -56,7 +56,10 @@ var ( kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/kubernetes.ca.crt # Disable cert checking for the dev cluster entry - kubectl config set-cluster e2e --insecure-skip-tls-verify=true`) + kubectl config set-cluster e2e --insecure-skip-tls-verify=true + + # Set custom TLS server name to use for validation for the e2e cluster entry + kubectl config set-cluster e2e --tls-server-name=my-cluster-name`) ) // NewCmdConfigSetCluster returns a Command instance for 'config set-cluster' sub command @@ -64,7 +67,7 @@ func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess) options := &createClusterOptions{configAccess: configAccess} cmd := &cobra.Command{ - Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure), + Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true] [--%v=example.com]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure, clientcmd.FlagTLSServerName), DisableFlagsInUseLine: true, Short: i18n.T("Sets a cluster entry in kubeconfig"), Long: createClusterLong, @@ -79,6 +82,7 @@ func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess) options.insecureSkipTLSVerify.Default(false) cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in kubeconfig") + cmd.Flags().Var(&options.tlsServerName, clientcmd.FlagTLSServerName, clientcmd.FlagTLSServerName+" for the cluster entry in kubeconfig") f := cmd.Flags().VarPF(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, "", clientcmd.FlagInsecure+" for the cluster entry in kubeconfig") f.NoOptDefVal = "true" cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "Path to "+clientcmd.FlagCAFile+" file for the cluster entry in kubeconfig") @@ -121,6 +125,9 @@ func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluste if o.server.Provided() { modifiedCluster.Server = o.server.Value() } + if o.tlsServerName.Provided() { + modifiedCluster.TLSServerName = o.tlsServerName.Value() + } if o.insecureSkipTLSVerify.Provided() { modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value() // Specifying insecure mode clears any certificate authority diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go index f961468fab6..1a94436c2a2 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go @@ -43,11 +43,12 @@ func TestCreateCluster(t *testing.T) { args: []string{"my-cluster"}, flags: []string{ "--server=http://192.168.0.1", + "--tls-server-name=my-cluster-name", }, expected: `Cluster "my-cluster" set.` + "\n", expectedConfig: clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ - "my-cluster": {Server: "http://192.168.0.1"}, + "my-cluster": {Server: "http://192.168.0.1", TLSServerName: "my-cluster-name"}, }, }, } @@ -115,5 +116,8 @@ func (test createClusterTest) run(t *testing.T) { if cluster.Server != test.expectedConfig.Clusters[test.args[0]].Server { t.Errorf("Fail in %q\n expected cluster server %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].Server, cluster.Server) } + if cluster.TLSServerName != test.expectedConfig.Clusters[test.args[0]].TLSServerName { + t.Errorf("Fail in %q\n expected cluster TLS server name %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].TLSServerName, cluster.TLSServerName) + } } } From 9dcbc0bf909a794cf77a801bfd29e306791b1e24 Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 3 Mar 2020 13:16:50 -0500 Subject: [PATCH 2/2] update override behavior for kubectl --tls-server-name --- .../api/v1/zz_generated.conversion.go | 2 ++ .../tools/clientcmd/client_config.go | 6 +++- .../tools/clientcmd/client_config_test.go | 17 +++++++++++ .../kubectl/pkg/cmd/config/create_cluster.go | 3 ++ .../pkg/cmd/config/create_cluster_test.go | 28 +++++++++++++++++-- 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.conversion.go b/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.conversion.go index 31e00ea6e4f..8f3631e151b 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.conversion.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.conversion.go @@ -233,6 +233,7 @@ func Convert_api_AuthProviderConfig_To_v1_AuthProviderConfig(in *api.AuthProvide func autoConvert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conversion.Scope) error { out.Server = in.Server + out.TLSServerName = in.TLSServerName out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify out.CertificateAuthority = in.CertificateAuthority out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData)) @@ -250,6 +251,7 @@ func Convert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conversi func autoConvert_api_Cluster_To_v1_Cluster(in *api.Cluster, out *Cluster, s conversion.Scope) error { // INFO: in.LocationOfOrigin opted out of conversion generation out.Server = in.Server + out.TLSServerName = in.TLSServerName out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify out.CertificateAuthority = in.CertificateAuthority out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData)) diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go index 6b5f3f7375d..a9806384aab 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go @@ -461,7 +461,11 @@ func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) { mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData } - if config.overrides.ClusterInfo.TLSServerName != "" { + // if the --tls-server-name has been set in overrides, use that value. + // if the --server has been set in overrides, then use the value of --tls-server-name specified on the CLI too. This gives the property + // that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is + // usually correct. + if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" { mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName } diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go index e89ce147bd0..3232d8b04ec 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go @@ -199,6 +199,23 @@ func TestTLSServerName(t *testing.T) { matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t) } +func TestTLSServerNameClearsWhenServerNameSet(t *testing.T) { + config := createValidTestConfig() + + clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{ + ClusterInfo: clientcmdapi.Cluster{ + Server: "http://something", + }, + }, nil) + + actualCfg, err := clientBuilder.ClientConfig() + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + matchStringArg("", actualCfg.ServerName, t) +} + func TestMergeContext(t *testing.T) { const namespace = "overridden-namespace" diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go index f4b4c0ac1b9..2b7f2c9185d 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go @@ -124,6 +124,9 @@ func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluste if o.server.Provided() { modifiedCluster.Server = o.server.Value() + // specifying a --server on the command line, overrides the TLSServerName that was specified in the kubeconfig file. + // if both are specified, then the next if block will write the new TLSServerName. + modifiedCluster.TLSServerName = "" } if o.tlsServerName.Provided() { modifiedCluster.TLSServerName = o.tlsServerName.Value() diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go index 1a94436c2a2..00f00c566fc 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go @@ -58,7 +58,7 @@ func TestCreateCluster(t *testing.T) { func TestModifyCluster(t *testing.T) { conf := clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ - "my-cluster": {Server: "https://192.168.0.1"}, + "my-cluster": {Server: "https://192.168.0.1", TLSServerName: "to-be-cleared"}, }, } test := createClusterTest{ @@ -78,6 +78,30 @@ func TestModifyCluster(t *testing.T) { test.run(t) } +func TestModifyClusterServerAndTLS(t *testing.T) { + conf := clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{ + "my-cluster": {Server: "https://192.168.0.1"}, + }, + } + test := createClusterTest{ + description: "Testing 'kubectl config set-cluster' with an existing cluster", + config: conf, + args: []string{"my-cluster"}, + flags: []string{ + "--server=https://192.168.0.99", + "--tls-server-name=my-cluster-name", + }, + expected: `Cluster "my-cluster" set.` + "\n", + expectedConfig: clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{ + "my-cluster": {Server: "https://192.168.0.99", TLSServerName: "my-cluster-name"}, + }, + }, + } + test.run(t) +} + func (test createClusterTest) run(t *testing.T) { fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "") if err != nil { @@ -117,7 +141,7 @@ func (test createClusterTest) run(t *testing.T) { t.Errorf("Fail in %q\n expected cluster server %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].Server, cluster.Server) } if cluster.TLSServerName != test.expectedConfig.Clusters[test.args[0]].TLSServerName { - t.Errorf("Fail in %q\n expected cluster TLS server name %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].TLSServerName, cluster.TLSServerName) + t.Errorf("Fail in %q\n expected cluster TLS server name %q\n but got %q\n ", test.description, test.expectedConfig.Clusters[test.args[0]].TLSServerName, cluster.TLSServerName) } } }