sigs.k8s.io/cluster-api@v1.6.3/cmd/clusterctl/client/cluster/proxy_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cluster
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"testing"
    24  	"time"
    25  
    26  	. "github.com/onsi/gomega"
    27  
    28  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
    29  	"sigs.k8s.io/cluster-api/version"
    30  )
    31  
    32  var _ Proxy = &test.FakeProxy{}
    33  
    34  func TestProxyGetConfig(t *testing.T) {
    35  	t.Run("GetConfig", func(t *testing.T) {
    36  		tests := []struct {
    37  			name               string
    38  			context            string
    39  			kubeconfigContents string
    40  			expectedHost       string
    41  			expectErr          bool
    42  		}{
    43  			{
    44  				name:               "defaults to the currentContext if none is specified",
    45  				kubeconfigContents: kubeconfig("management", "default"),
    46  				expectedHost:       "https://management-server:1234",
    47  				expectErr:          false,
    48  			},
    49  			{
    50  				name:               "returns host of cluster associated with the specified context even if currentContext is different",
    51  				kubeconfigContents: kubeconfig("management", "default"),
    52  				context:            "workload",
    53  				expectedHost:       "https://kind-server:38790",
    54  				expectErr:          false,
    55  			},
    56  			{
    57  				name:               "returns error if cannot load the kubeconfig file",
    58  				expectErr:          true,
    59  				kubeconfigContents: "bad contents",
    60  			},
    61  		}
    62  
    63  		for _, tt := range tests {
    64  			t.Run(tt.name, func(t *testing.T) {
    65  				g := NewWithT(t)
    66  				dir, err := os.MkdirTemp("", "clusterctl")
    67  				g.Expect(err).ToNot(HaveOccurred())
    68  				defer os.RemoveAll(dir)
    69  				configFile := filepath.Join(dir, ".test-kubeconfig.yaml")
    70  				g.Expect(os.WriteFile(configFile, []byte(tt.kubeconfigContents), 0600)).To(Succeed())
    71  
    72  				proxy := newProxy(Kubeconfig{Path: configFile, Context: tt.context})
    73  				conf, err := proxy.GetConfig()
    74  				if tt.expectErr {
    75  					g.Expect(err).To(HaveOccurred())
    76  					return
    77  				}
    78  				g.Expect(err).ToNot(HaveOccurred())
    79  				g.Expect(conf).ToNot(BeNil())
    80  				// asserting on the host of the cluster associated with the
    81  				// context
    82  				g.Expect(conf.Host).To(Equal(tt.expectedHost))
    83  				g.Expect(conf.UserAgent).To(Equal(fmt.Sprintf("clusterctl/%s (%s)", version.Get().GitVersion, version.Get().Platform)))
    84  				g.Expect(conf.QPS).To(BeEquivalentTo(20))
    85  				g.Expect(conf.Burst).To(BeEquivalentTo(100))
    86  				g.Expect(conf.Timeout.String()).To(Equal("30s"))
    87  			})
    88  		}
    89  	})
    90  
    91  	t.Run("configure timeout", func(t *testing.T) {
    92  		g := NewWithT(t)
    93  		dir, err := os.MkdirTemp("", "clusterctl")
    94  		g.Expect(err).ToNot(HaveOccurred())
    95  		defer os.RemoveAll(dir)
    96  		configFile := filepath.Join(dir, ".test-kubeconfig.yaml")
    97  		g.Expect(os.WriteFile(configFile, []byte(kubeconfig("management", "default")), 0600)).To(Succeed())
    98  
    99  		proxy := newProxy(Kubeconfig{Path: configFile, Context: "management"}, InjectProxyTimeout(23*time.Second))
   100  		conf, err := proxy.GetConfig()
   101  		g.Expect(err).ToNot(HaveOccurred())
   102  		g.Expect(conf.Timeout.String()).To(Equal("23s"))
   103  	})
   104  }
   105  
   106  // These tests are emulating the files passed in via KUBECONFIG env var by
   107  // injecting the file paths into the ClientConfigLoadingRules.Precedence
   108  // chain.
   109  func TestKUBECONFIGEnvVar(t *testing.T) {
   110  	t.Run("CurrentNamespace", func(t *testing.T) {
   111  		// KUBECONFIG can specify multiple config files. We should be able to
   112  		// get the correct namespace for the context by parsing through all
   113  		// kubeconfig files
   114  		var (
   115  			context            = "workload"
   116  			kubeconfigContents = kubeconfig("does-not-exist", "some-ns")
   117  		)
   118  
   119  		g := NewWithT(t)
   120  		dir, err := os.MkdirTemp("", "clusterctl")
   121  		g.Expect(err).ToNot(HaveOccurred())
   122  		defer os.RemoveAll(dir)
   123  		configFile := filepath.Join(dir, ".test-kubeconfig.yaml")
   124  		g.Expect(os.WriteFile(configFile, []byte(kubeconfigContents), 0600)).To(Succeed())
   125  
   126  		proxy := newProxy(
   127  			// dont't give an explicit path but rather define the file in the
   128  			// configLoadingRules precedence chain.
   129  			Kubeconfig{Path: "", Context: context},
   130  			InjectKubeconfigPaths([]string{configFile}),
   131  		)
   132  		actualNS, err := proxy.CurrentNamespace()
   133  		g.Expect(err).ToNot(HaveOccurred())
   134  		g.Expect(actualNS).To(Equal("some-ns"))
   135  	})
   136  
   137  	t.Run("GetConfig", func(t *testing.T) {
   138  		// KUBECONFIG can specify multiple config files. We should be able to
   139  		// get the valid cluster context by parsing all the kubeconfig files.
   140  		var (
   141  			context = "workload"
   142  			// TODO: If we change current context to "do-not-exist", we get an
   143  			// error. See https://github.com/kubernetes/client-go/issues/797
   144  			kubeconfigContents = kubeconfig("management", "default")
   145  			expectedHost       = "https://kind-server:38790"
   146  		)
   147  		g := NewWithT(t)
   148  		dir, err := os.MkdirTemp("", "clusterctl")
   149  		g.Expect(err).ToNot(HaveOccurred())
   150  		defer os.RemoveAll(dir)
   151  		configFile := filepath.Join(dir, ".test-kubeconfig.yaml")
   152  		g.Expect(os.WriteFile(configFile, []byte(kubeconfigContents), 0600)).To(Succeed())
   153  
   154  		proxy := newProxy(
   155  			// dont't give an explicit path but rather define the file in the
   156  			// configLoadingRules precedence chain.
   157  			Kubeconfig{Path: "", Context: context},
   158  			InjectKubeconfigPaths([]string{configFile}),
   159  		)
   160  		conf, err := proxy.GetConfig()
   161  		g.Expect(err).ToNot(HaveOccurred())
   162  		g.Expect(conf).ToNot(BeNil())
   163  		// asserting on the host of the cluster associated with the
   164  		// context
   165  		g.Expect(conf.Host).To(Equal(expectedHost))
   166  	})
   167  }
   168  
   169  func TestProxyCurrentNamespace(t *testing.T) {
   170  	tests := []struct {
   171  		name               string
   172  		kubeconfigPath     string
   173  		kubeconfigContents string
   174  		kubeconfigContext  string
   175  		expectErr          bool
   176  		expectedNamespace  string
   177  	}{
   178  		{
   179  			name:           "return error for invalid kubeconfig path",
   180  			kubeconfigPath: "do-not-exist",
   181  			expectErr:      true,
   182  		},
   183  		{
   184  			name:               "return error for bad kubeconfig contents",
   185  			kubeconfigContents: "management",
   186  			expectErr:          true,
   187  		},
   188  		{
   189  			name:               "return default namespace if unspecified",
   190  			kubeconfigContents: kubeconfig("workload", ""),
   191  			expectErr:          false,
   192  			expectedNamespace:  "default",
   193  		},
   194  		{
   195  			name:               "return error when current-context is empty",
   196  			kubeconfigContents: kubeconfig("", ""),
   197  			expectErr:          true,
   198  		},
   199  		{
   200  			name:               "return error when current-context is incorrect or does not exist",
   201  			kubeconfigContents: kubeconfig("does-not-exist", ""),
   202  			expectErr:          true,
   203  		},
   204  		{
   205  			name:               "return specified namespace for the current context",
   206  			kubeconfigContents: kubeconfig("workload", "mykindns"),
   207  			expectErr:          false,
   208  			expectedNamespace:  "mykindns",
   209  		},
   210  		{
   211  			name:               "return the namespace of the specified context which is different from current context",
   212  			kubeconfigContents: kubeconfig("management", "workload-ns"),
   213  			expectErr:          false,
   214  			kubeconfigContext:  "workload",
   215  			expectedNamespace:  "workload-ns",
   216  		},
   217  	}
   218  	for _, tt := range tests {
   219  		t.Run(tt.name, func(t *testing.T) {
   220  			g := NewWithT(t)
   221  			var configFile string
   222  			if tt.kubeconfigPath != "" {
   223  				configFile = tt.kubeconfigPath
   224  			} else {
   225  				dir, err := os.MkdirTemp("", "clusterctl")
   226  				g.Expect(err).ToNot(HaveOccurred())
   227  				defer os.RemoveAll(dir)
   228  				configFile = filepath.Join(dir, ".test-kubeconfig.yaml")
   229  				g.Expect(os.WriteFile(configFile, []byte(tt.kubeconfigContents), 0600)).To(Succeed())
   230  			}
   231  
   232  			proxy := newProxy(Kubeconfig{Path: configFile, Context: tt.kubeconfigContext})
   233  			ns, err := proxy.CurrentNamespace()
   234  			if tt.expectErr {
   235  				g.Expect(err).To(HaveOccurred())
   236  				return
   237  			}
   238  			g.Expect(err).ToNot(HaveOccurred())
   239  			g.Expect(ns).To(Equal(tt.expectedNamespace))
   240  		})
   241  	}
   242  }
   243  
   244  func kubeconfig(currentContext, namespace string) string {
   245  	return fmt.Sprintf(`---
   246  apiVersion: v1
   247  clusters:
   248  - cluster:
   249      insecure-skip-tls-verify: true
   250      server: https://management-server:1234
   251    name: management
   252  - cluster:
   253      insecure-skip-tls-verify: true
   254      server: https://kind-server:38790
   255    name: workload
   256  contexts:
   257  - context:
   258      cluster: management
   259      user: management
   260      namespace: management-ns
   261    name: management
   262  - context:
   263      cluster: workload
   264      user: workload
   265      namespace: %s
   266    name: workload
   267  current-context: %s
   268  kind: Config
   269  preferences: {}
   270  users:
   271  - name: management
   272    user:
   273      client-certificate-data: c3R1ZmYK
   274      client-key-data: c3R1ZmYK
   275  - name: workload
   276    user:
   277      client-certificate-data: c3R1ZmYK
   278      client-key-data: c3R1ZmYK
   279  `, namespace, currentContext)
   280  }