github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/kubetest/kubeadmdind/kubeadm_dind_test.go (about) 1 /* 2 Copyright 2018 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 kubeadmdind 18 19 import ( 20 "os/exec" 21 "testing" 22 "time" 23 24 "k8s.io/test-infra/kubetest/process" 25 ) 26 27 // fakeExecCmder implements the execCmder interface for testing how the 28 // deployer processes executed command output. 29 type fakeExecCmder struct { 30 simulatedOutput string // Simulated output 31 generateError bool // Create a command that causes an error 32 } 33 34 func newFakeExecCmder(simOutput string, genError bool) *fakeExecCmder { 35 cmder := new(fakeExecCmder) 36 cmder.simulatedOutput = simOutput 37 cmder.generateError = genError 38 return cmder 39 } 40 41 // execCmd creates an exec.Cmd structure for either: 42 // - Echoing a simulated output string to be processed by the deployer. 43 // - Running a bogus command to cause an execution error to be processed 44 // by the deployer. 45 func (f *fakeExecCmder) execCmd(cmd string) *exec.Cmd { 46 if f.generateError { 47 return exec.Command("Bogus_Command_to_Cause_an_Error") 48 } 49 return exec.Command("echo", f.simulatedOutput) 50 } 51 52 // fakeNodeCmder implements the execCmder interface for testing how the 53 // deployer processes output from commands executed on a node. 54 type fakeNodeCmder struct { 55 node string 56 simulatedOutput string // Simulated output 57 generateError bool // Create a command that causes an error 58 } 59 60 func newFakeNodeCmder(node, simOutput string, genError bool) *fakeNodeCmder { 61 cmder := new(fakeNodeCmder) 62 cmder.node = node 63 cmder.simulatedOutput = simOutput 64 cmder.generateError = genError 65 return cmder 66 } 67 68 // execCmd creates an exec.Cmd structure for either: 69 // - Echoing a simulated output string to be processed by the deployer. 70 // - Running a bogus command to cause an execution error to be processed 71 // by the deployer. 72 func (f *fakeNodeCmder) execCmd(cmd string) *exec.Cmd { 73 if f.generateError { 74 return exec.Command("Bogus_Command_to_Cause_an_Error") 75 } 76 return exec.Command("echo", f.simulatedOutput) 77 } 78 79 // getNode returns the node name for a fakeNodeCmder 80 func (f *fakeNodeCmder) getNode() string { 81 return f.node 82 } 83 84 // createTestDeployer creates a kubeadmdind deployer for unit testing. 85 func createTestDeployer(ipMode string) (*Deployer, error) { 86 *kubeadmDinDIPMode = ipMode 87 timeout := time.Duration(10) * time.Second 88 interrupt := time.NewTimer(time.Duration(10) * time.Second) 89 terminate := time.NewTimer(time.Duration(10) * time.Second) 90 verbose := false 91 control := process.NewControl(timeout, interrupt, terminate, verbose) 92 return NewDeployer(control) 93 } 94 95 // slicesAreEqual tests whether two slices of strings are of equal length 96 // and have the same entries, independent of ordering. It assumes that 97 // entries in the slice being compared against (argument 'sliceA', and by 98 // extension, both slices) form a set. 99 func slicesAreEqual(sliceA, sliceB []string) bool { 100 if len(sliceA) != len(sliceB) { 101 return false 102 } 103 matched := false 104 for _, stringA := range sliceA { 105 matched = false 106 for _, stringB := range sliceB { 107 if stringB == stringA { 108 matched = true 109 break 110 } 111 } 112 if !matched { 113 return false 114 } 115 } 116 return true 117 } 118 119 // TestIPModeValidation tests whether the NewDeployer method correctly 120 // validates configured values for IP mode. 121 func TestIPModeValidation(t *testing.T) { 122 oldEnableCmd := ipv6EnableCmd 123 ipv6EnableCmd = "echo success" 124 defer func() { ipv6EnableCmd = oldEnableCmd }() 125 126 testCases := []struct { 127 ipMode string 128 expError bool 129 }{ 130 { 131 ipMode: "ipv4", 132 expError: false, 133 }, 134 { 135 ipMode: "ipv6", 136 expError: false, 137 }, 138 { 139 ipMode: "dual-stack", 140 expError: false, 141 }, 142 { 143 ipMode: "twas-bryllyg", 144 expError: true, 145 }, 146 } 147 for _, tc := range testCases { 148 _, err := createTestDeployer(tc.ipMode) 149 switch { 150 case err != nil && !tc.expError: 151 t.Errorf("ip mode '%s': Unexpected error: %v", tc.ipMode, err) 152 continue 153 case err == nil && tc.expError: 154 t.Errorf("ip mode '%s': Did not get expected error", tc.ipMode) 155 continue 156 } 157 } 158 } 159 160 // TestClusterSize tests whether the clusterSize method: 161 // - Processes a sample 'kubectl get nodes --no-header' output and 162 // calculates the correct number of nodes, or... 163 // - Handles 'kubectl get nodes ...' command errors (reports -1 nodes) 164 func TestClusterSize(t *testing.T) { 165 d, err := createTestDeployer("ipv4") 166 if err != nil { 167 t.Errorf("couldn't create deployer: %v", err) 168 return 169 } 170 171 testCases := []struct { 172 testName string 173 simOutput string 174 genError bool 175 expSize int 176 }{ 177 { 178 testName: "No nodes", 179 simOutput: "", 180 expSize: 0, 181 }, 182 { 183 testName: "3-node Cluster", 184 simOutput: ` 185 kube-master Ready master 10m v1.11.0 186 kube-node-1 Ready <none> 10m v1.11.0 187 kube-node-2 Ready <none> 10m v1.11.0 188 `, 189 expSize: 3, 190 }, 191 { 192 testName: "Simulated command error", 193 genError: true, 194 expSize: -1, 195 }, 196 } 197 for _, tc := range testCases { 198 d.hostCmder = newFakeExecCmder(tc.simOutput, tc.genError) 199 size, err := d.clusterSize() 200 switch { 201 case err != nil && !tc.genError: 202 t.Errorf("test case '%s': Unexpected error %v", tc.testName, err) 203 continue 204 case err == nil && tc.genError: 205 t.Errorf("test case '%s': Did not get expected error", tc.testName) 206 continue 207 } 208 if size != tc.expSize { 209 t.Errorf("test case '%s': expected size %d, found size %d", tc.testName, tc.expSize, size) 210 continue 211 } 212 } 213 } 214 215 // TestDetectNodeContainers tests whether detectNodeContainers can 216 // either correctly process a sample command output for 'kubectl get 217 // nodes ...', or gracefully handle a command error. Test cases include: 218 // - Detect master nodes 219 // - Detect worker nodes 220 // - Return an empty list upon command error 221 func TestDetectNodeContainers(t *testing.T) { 222 d, err := createTestDeployer("ipv4") 223 if err != nil { 224 t.Errorf("couldn't create deployer: %v", err) 225 return 226 } 227 228 kubectlNodesOutput := ` 229 kube-master Ready master 1d v1.11.0-alpha.0 230 kube-node-1 Ready <none> 1d v1.11.0-alpha.0 231 kube-node-2 Ready <none> 1d v1.11.0-alpha.0 232 ` 233 testCases := []struct { 234 testName string 235 nodePrefix string 236 genError bool 237 expNodes []string 238 }{ 239 { 240 testName: "Detect master nodes", 241 nodePrefix: kubeMasterPrefix, 242 expNodes: []string{"kube-master"}, 243 }, 244 { 245 testName: "Detect worker nodes", 246 nodePrefix: kubeNodePrefix, 247 expNodes: []string{"kube-node-1", "kube-node-2"}, 248 }, 249 { 250 testName: "Check error handling", 251 nodePrefix: kubeNodePrefix, 252 genError: true, 253 expNodes: []string{}, 254 }, 255 } 256 257 for _, tc := range testCases { 258 d.hostCmder = newFakeExecCmder(kubectlNodesOutput, tc.genError) 259 foundNodes, err := d.detectNodeContainers(tc.nodePrefix) 260 switch { 261 case err != nil && !tc.genError: 262 t.Errorf("test case '%s': Unexpected error: %v", tc.testName, err) 263 continue 264 case err == nil && tc.genError: 265 t.Errorf("test case '%s': Did not get expected error", tc.testName) 266 continue 267 } 268 // Check whether the expected nodes have all been detected 269 if !slicesAreEqual(tc.expNodes, foundNodes) { 270 t.Errorf("test case: '%s', Expected nodes: %v, Detected nodes: %v", tc.testName, tc.expNodes, foundNodes) 271 continue 272 } 273 } 274 } 275 276 // TestDetectKubeContainers tests whether detectKubeContainers can 277 // either correctly process a sample command output for 'docker ps -a', 278 // or gracefully handle a command error. Test cases include: 279 // - Detect Kubernetes system pod containers on a node 280 // - Return an empty list upon command error 281 func TestDetectKubeContainers(t *testing.T) { 282 d, err := createTestDeployer("ipv4") 283 if err != nil { 284 t.Errorf("couldn't create deployer: %v", err) 285 return 286 } 287 288 dockerPsOutput := "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n" + 289 290 "fba3566d4b43 k8s.gcr.io/k8s-dns-sidecar \"/sidecar --v=2 --log\" 10 minutes ago Up 10 minutes k8s_sidecar_kube-dns-69f5bbc4c7\n" + 291 "3b7d8cf5b937 k8s.gcr.io/k8s-dns-dnsmasq-nanny \"/dnsmasq-nanny -v=2 \" 10 minutes ago Up 10 minutes k8s_dnsmasq_kube-dns-69f5bbc4c7\n" + 292 "5aacb0551aa6 k8s.gcr.io/k8s-dns-kube-dns \"/kube-dns --domain=c\" 10 minutes ago Up 10 minutes k8s_kubedns_kube-dns-69f5bbc4c7\n" + 293 "a4abfb755f58 k8s.gcr.io/pause-amd64:3.1 \"/pause\" 10 minutes ago Up 10 minutes k8s_POD_kube-dns-69f5bbc4c7\n" + 294 "03d1bb19d515 60e55008753b \"/usr/local/bin/kube-\" 10 minutes ago Up 10 minutes k8s_kube-proxy_kube-proxy-4tzr8\n" + 295 "1455bc3829d0 k8s.gcr.io/pause-amd64:3.1 \"/pause\" 10 minutes ago Up 10 minutes k8s_POD_kube-proxy-4tzr8\n" 296 297 testCases := []struct { 298 testName string 299 genError bool 300 expContainers []string 301 }{ 302 { 303 testName: "Detect Containers", 304 genError: false, 305 expContainers: []string{ 306 "k8s_sidecar_kube-dns-69f5bbc4c7", 307 "k8s_dnsmasq_kube-dns-69f5bbc4c7", 308 "k8s_kubedns_kube-dns-69f5bbc4c7", 309 "k8s_kube-proxy_kube-proxy-4tzr8", 310 }, 311 }, 312 { 313 testName: "Check error handling", 314 genError: true, 315 expContainers: []string{}, 316 }, 317 } 318 319 for _, tc := range testCases { 320 node := "fakeNodeName" 321 fakeCmder := newFakeNodeCmder(node, dockerPsOutput, tc.genError) 322 containers, err := d.detectKubeContainers(fakeCmder, node, nodeKubePods) 323 switch { 324 case err != nil && !tc.genError: 325 t.Errorf("test case '%s': Unexpected error: %v", tc.testName, err) 326 continue 327 case err == nil && tc.genError: 328 t.Errorf("test case '%s': Did not get expected error", tc.testName) 329 continue 330 } 331 // Check whether the expected containers have been detected 332 if !slicesAreEqual(tc.expContainers, containers) { 333 t.Errorf("test case: '%s', Expected containers: %v, Detected containers: %v", tc.testName, tc.expContainers, containers) 334 continue 335 } 336 } 337 }