1// Copyright 2019 Istio Authors. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package multicluster 16 17import ( 18 "bytes" 19 "io/ioutil" 20 "reflect" 21 "testing" 22 "time" 23 24 "github.com/google/go-cmp/cmp" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/client-go/kubernetes" 27 "k8s.io/client-go/kubernetes/fake" 28 "k8s.io/client-go/tools/clientcmd/api" 29 "k8s.io/client-go/tools/clientcmd/api/latest" 30) 31 32var fakeKubeconfigData = `apiVersion: v1 33kind: Config 34clusters: 35- cluster: 36 certificate-authority-data: UEhPTlkK 37 server: https://192.168.1.1 38 name: prod0 39contexts: 40- context: 41 cluster: prod0 42 user: prod0 43 name: prod0 44current-context: prod0 45users: 46- name: prod0 47 user: 48 auth-provider: 49 name: gcp` // nolint:lll 50 51func createFakeKubeconfigFileOrDie(t *testing.T) (string, *api.Config) { 52 t.Helper() 53 54 f, err := ioutil.TempFile("", "fakeKubeconfigForEnvironment") 55 if err != nil { 56 t.Fatalf("could not create fake kubeconfig file: %v", err) 57 } 58 kubeconfigPath := f.Name() 59 defer func() { 60 if err := f.Close(); err != nil { 61 t.Errorf("couldn't close temp file %v: %v", kubeconfigPath, err) 62 } 63 }() 64 65 if _, err = f.WriteString(fakeKubeconfigData); err != nil { 66 t.Fatalf("could not write fake kubeconfig data to %v: %v", kubeconfigPath, err) 67 } 68 69 // Temporary workaround until https://github.com/kubernetes/kubernetes/pull/86414 merges 70 into := &api.Config{ 71 Clusters: map[string]*api.Cluster{}, 72 AuthInfos: map[string]*api.AuthInfo{}, 73 Contexts: map[string]*api.Context{}, 74 Extensions: map[string]runtime.Object{}, 75 } 76 out, _, err := latest.Codec.Decode([]byte(fakeKubeconfigData), nil, into) 77 if err != nil { 78 t.Fatalf("could not decode fake kubeconfig: %v", err) 79 } 80 config, ok := out.(*api.Config) 81 if !ok { 82 t.Fatalf("decoded kubeconfig is not a valid api.Config (%v)", reflect.TypeOf(out)) 83 } 84 85 // fill in missing defaults 86 config.Contexts[config.CurrentContext].LocationOfOrigin = kubeconfigPath 87 config.Clusters[config.CurrentContext].LocationOfOrigin = kubeconfigPath 88 config.AuthInfos[config.CurrentContext].LocationOfOrigin = kubeconfigPath 89 config.Extensions = make(map[string]runtime.Object) 90 config.Preferences.Extensions = make(map[string]runtime.Object) 91 92 return kubeconfigPath, config 93} 94 95type fakeEnvironment struct { 96 KubeEnvironment 97 98 client *fake.Clientset 99 injectClientCreateError error 100 kubeconfig string 101 wOut bytes.Buffer 102 wErr bytes.Buffer 103} 104 105func newFakeEnvironmentOrDie(t *testing.T, config *api.Config, objs ...runtime.Object) *fakeEnvironment { 106 t.Helper() 107 108 var wOut, wErr bytes.Buffer 109 110 f := &fakeEnvironment{ 111 KubeEnvironment: KubeEnvironment{ 112 config: config, 113 stdout: &wOut, 114 stderr: &wErr, 115 kubeconfig: "unused", 116 }, 117 client: fake.NewSimpleClientset(objs...), 118 kubeconfig: "unused", 119 wOut: wOut, 120 wErr: wErr, 121 } 122 123 return f 124} 125 126func (f *fakeEnvironment) CreateClientSet(context string) (kubernetes.Interface, error) { 127 if f.injectClientCreateError != nil { 128 return nil, f.injectClientCreateError 129 } 130 return f.client, nil 131} 132 133func (f *fakeEnvironment) Poll(interval, timeout time.Duration, condition ConditionFunc) error { 134 // TODO - add hooks to inject fake timeouts 135 condition() 136 return nil 137} 138 139func TestNewEnvironment(t *testing.T) { 140 context := "" // empty, use current-Context 141 kubeconfig, wantConfig := createFakeKubeconfigFileOrDie(t) 142 143 var wOut, wErr bytes.Buffer 144 145 wantEnv := &KubeEnvironment{ 146 config: wantConfig, 147 stdout: &wOut, 148 stderr: &wErr, 149 kubeconfig: kubeconfig, 150 } 151 152 gotEnv, err := NewEnvironment(kubeconfig, context, &wOut, &wErr) 153 if err != nil { 154 t.Fatal(err) 155 } 156 157 if diff := cmp.Diff(*wantEnv.GetConfig(), *gotEnv.GetConfig()); diff != "" { 158 t.Errorf("bad config: \n got %v \nwant %v\ndiff %v", gotEnv, wantEnv, diff) 159 } 160 161 wantEnv.config = nil 162 gotEnv.config = nil 163 if !reflect.DeepEqual(wantEnv, gotEnv) { 164 t.Errorf("bad environment: \n got %v \nwant %v", *gotEnv, *wantEnv) 165 } 166 167 // verify interface 168 if gotEnv.Stderr() != &wErr { 169 t.Errorf("Stderr() returned wrong io.writer") 170 } 171 if gotEnv.Stdout() != &wOut { 172 t.Errorf("Stdout() returned wrong io.writer") 173 } 174 gotEnv.Printf("stdout %v", "test") 175 wantOut := "stdout test" 176 if gotOut := wOut.String(); gotOut != wantOut { 177 t.Errorf("Printf() printed wrong string: got %v want %v", gotOut, wantOut) 178 } 179 gotEnv.Errorf("stderr %v", "test") 180 wantErr := "stderr test" 181 if gotErr := wErr.String(); gotErr != wantErr { 182 t.Errorf("Errorf() printed wrong string: got %v want %v", gotErr, wantErr) 183 } 184} 185