github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/scenarios/kubernetes_e2e_test.py (about) 1 #!/usr/bin/env python 2 3 # Copyright 2017 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # Need to figure out why this only fails on travis 18 # pylint: disable=too-few-public-methods 19 20 """Test for kubernetes_e2e.py""" 21 22 import json 23 import os 24 import re 25 import shutil 26 import string 27 import tempfile 28 import urllib 29 import urllib2 30 import unittest 31 import time 32 33 import kubernetes_e2e 34 35 FAKE_WORKSPACE_STATUS = 'STABLE_BUILD_GIT_COMMIT 599539dc0b99976fda0f326f4ce47e93ec07217c\n' \ 36 'STABLE_BUILD_SCM_STATUS clean\n' \ 37 'STABLE_BUILD_SCM_REVISION v1.7.0-alpha.0.1320+599539dc0b9997\n' \ 38 'STABLE_BUILD_MAJOR_VERSION 1\n' \ 39 'STABLE_BUILD_MINOR_VERSION 7+\n' \ 40 'STABLE_gitCommit 599539dc0b99976fda0f326f4ce47e93ec07217c\n' \ 41 'STABLE_gitTreeState clean\n' \ 42 'STABLE_gitVersion v1.7.0-alpha.0.1320+599539dc0b9997\n' \ 43 'STABLE_gitMajor 1\n' \ 44 'STABLE_gitMinor 7+\n' 45 46 FAKE_WORKSPACE_STATUS_V1_6 = 'STABLE_BUILD_GIT_COMMIT 84febd4537dd190518657405b7bdb921dfbe0387\n' \ 47 'STABLE_BUILD_SCM_STATUS clean\n' \ 48 'STABLE_BUILD_SCM_REVISION v1.6.4-beta.0.18+84febd4537dd19\n' \ 49 'STABLE_BUILD_MAJOR_VERSION 1\n' \ 50 'STABLE_BUILD_MINOR_VERSION 6+\n' \ 51 'STABLE_gitCommit 84febd4537dd190518657405b7bdb921dfbe0387\n' \ 52 'STABLE_gitTreeState clean\n' \ 53 'STABLE_gitVersion v1.6.4-beta.0.18+84febd4537dd19\n' \ 54 'STABLE_gitMajor 1\n' \ 55 'STABLE_gitMinor 6+\n' 56 57 FAKE_DESCRIBE_FROM_FAMILY_RESPONSE = """ 58 archiveSizeBytes: '1581831882' 59 creationTimestamp: '2017-06-16T10:37:57.681-07:00' 60 description: 'Google, Container-Optimized OS, 59-9460.64.0 stable, Kernel: ChromiumOS-4.4.52 61 Kubernetes: 1.6.4 Docker: 1.11.2' 62 diskSizeGb: '10' 63 family: cos-stable 64 id: '2388425242502080922' 65 kind: compute#image 66 labelFingerprint: 42WmSpB8rSM= 67 licenses: 68 - https://www.googleapis.com/compute/v1/projects/cos-cloud/global/licenses/cos 69 name: cos-stable-59-9460-64-0 70 rawDisk: 71 containerType: TAR 72 source: '' 73 selfLink: https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/cos-stable-59-9460-64-0 74 sourceType: RAW 75 status: READY 76 """ 77 78 def fake_pass(*_unused, **_unused2): 79 """Do nothing.""" 80 pass 81 82 def fake_bomb(*a, **kw): 83 """Always raise.""" 84 raise AssertionError('Should not happen', a, kw) 85 86 def raise_urllib2_error(*_unused, **_unused2): 87 """Always raise a urllib2.URLError""" 88 raise urllib2.URLError("test failure") 89 90 def always_kubernetes(*_unused, **_unused2): 91 """Always return 'kubernetes'""" 92 return 'kubernetes' 93 94 class Stub(object): 95 """Replace thing.param with replacement until exiting with.""" 96 def __init__(self, thing, param, replacement): 97 self.thing = thing 98 self.param = param 99 self.replacement = replacement 100 self.old = getattr(thing, param) 101 setattr(thing, param, self.replacement) 102 103 def __enter__(self, *a, **kw): 104 return self.replacement 105 106 def __exit__(self, *a, **kw): 107 setattr(self.thing, self.param, self.old) 108 109 110 class ClusterNameTest(unittest.TestCase): 111 def test_name_filled(self): 112 """Return the cluster name if set.""" 113 name = 'foo' 114 build = '1984' 115 actual = kubernetes_e2e.cluster_name(name, build) 116 self.assertTrue(actual) 117 self.assertIn(name, actual) 118 self.assertNotIn(build, actual) 119 120 def test_name_empty_short_build(self): 121 """Return the build number if name is empty.""" 122 name = '' 123 build = '1984' 124 actual = kubernetes_e2e.cluster_name(name, build) 125 self.assertTrue(actual) 126 self.assertIn(build, actual) 127 128 def test_name_empty_long_build(self): 129 """Return a short hash of a long build number if name is empty.""" 130 name = '' 131 build = '0' * 63 132 actual = kubernetes_e2e.cluster_name(name, build) 133 self.assertTrue(actual) 134 self.assertNotIn(build, actual) 135 if len(actual) > 32: # Some firewall names consume half the quota 136 self.fail('Name should be short: %s' % actual) 137 138 139 class ScenarioTest(unittest.TestCase): # pylint: disable=too-many-public-methods 140 """Test for e2e scenario.""" 141 callstack = [] 142 envs = {} 143 144 def setUp(self): 145 self.boiler = [ 146 Stub(kubernetes_e2e, 'check', self.fake_check), 147 Stub(shutil, 'copy', fake_pass), 148 ] 149 150 def tearDown(self): 151 for stub in self.boiler: 152 with stub: # Leaving with restores things 153 pass 154 self.callstack[:] = [] 155 self.envs.clear() 156 157 def fake_check(self, *cmd): 158 """Log the command.""" 159 self.callstack.append(string.join(cmd)) 160 161 def fake_check_env(self, env, *cmd): 162 """Log the command with a specific env.""" 163 self.envs.update(env) 164 self.callstack.append(string.join(cmd)) 165 166 def fake_output_work_status(self, *cmd): 167 """fake a workstatus blob.""" 168 self.callstack.append(string.join(cmd)) 169 return FAKE_WORKSPACE_STATUS 170 171 def fake_output_work_status_v1_6(self, *cmd): 172 """fake a workstatus blob for v1.6.""" 173 self.callstack.append(string.join(cmd)) 174 return FAKE_WORKSPACE_STATUS_V1_6 175 176 def fake_output_get_latest_image(self, *cmd): 177 """fake a `gcloud compute images describe-from-family` response.""" 178 self.callstack.append(string.join(cmd)) 179 return FAKE_DESCRIBE_FROM_FAMILY_RESPONSE 180 181 def test_local(self): 182 """Make sure local mode is fine overall.""" 183 args = kubernetes_e2e.parse_args() 184 self.assertEqual(args.mode, 'local') 185 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 186 kubernetes_e2e.main(args) 187 188 self.assertNotEqual(self.envs, {}) 189 for call in self.callstack: 190 self.assertFalse(call.startswith('docker')) 191 192 def test_check_leaks(self): 193 """Ensure --check-leaked-resources=true sends flag to kubetest.""" 194 args = kubernetes_e2e.parse_args(['--check-leaked-resources=true']) 195 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 196 kubernetes_e2e.main(args) 197 self.assertIn('--check-leaked-resources=true', self.callstack[-1]) 198 199 def test_check_leaks_false(self): 200 """Ensure --check-leaked-resources=true sends flag to kubetest.""" 201 args = kubernetes_e2e.parse_args(['--check-leaked-resources=false']) 202 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 203 kubernetes_e2e.main(args) 204 self.assertIn('--check-leaked-resources=false', self.callstack[-1]) 205 206 def test_check_leaks_default(self): 207 """Ensure --check-leaked-resources=true sends flag to kubetest.""" 208 args = kubernetes_e2e.parse_args(['--check-leaked-resources']) 209 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 210 kubernetes_e2e.main(args) 211 self.assertIn('--check-leaked-resources', self.callstack[-1]) 212 213 def test_check_leaks_unset(self): 214 """Ensure --check-leaked-resources=true sends flag to kubetest.""" 215 args = kubernetes_e2e.parse_args(['--mode=local']) 216 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 217 kubernetes_e2e.main(args) 218 self.assertNotIn('--check-leaked-resources', self.callstack[-1]) 219 220 def test_migrated_kubetest_args(self): 221 migrated = [ 222 '--stage-suffix=panda', 223 '--random-flag', 'random-value', 224 '--multiple-federations', 225 'arg1', 'arg2', 226 '--federation', 227 '--kubemark', 228 '--extract=this', 229 '--extract=that', 230 '--perf-tests', 231 '--deployment=yay', 232 '--save=somewhere', 233 '--skew', 234 '--publish=location', 235 '--timeout=42m', 236 '--upgrade_args=ginkgo', 237 '--check-leaked-resources=true', 238 '--charts', 239 ] 240 args = kubernetes_e2e.parse_args(['--mode=docker'] + migrated + ['--test=false']) 241 self.assertEquals(migrated, args.kubetest_args) 242 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 243 kubernetes_e2e.main(args) 244 lastcall = self.callstack[-2] 245 for arg in migrated: 246 self.assertIn(arg, lastcall) 247 248 def test_updown_default(self): 249 args = kubernetes_e2e.parse_args(['--mode=local']) 250 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 251 kubernetes_e2e.main(args) 252 lastcall = self.callstack[-1] 253 self.assertIn('--up', lastcall) 254 self.assertIn('--down', lastcall) 255 256 def test_updown_set(self): 257 args = kubernetes_e2e.parse_args(['--mode=local', '--up=false', '--down=true']) 258 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 259 kubernetes_e2e.main(args) 260 lastcall = self.callstack[-1] 261 self.assertNotIn('--up', lastcall) 262 self.assertIn('--down', lastcall) 263 264 265 def test_kubeadm_ci(self): 266 """Make sure kubeadm ci mode is fine overall.""" 267 args = kubernetes_e2e.parse_args(['--mode=local', '--kubeadm=ci']) 268 self.assertEqual(args.mode, 'local') 269 self.assertEqual(args.kubeadm, 'ci') 270 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 271 with Stub(kubernetes_e2e, 'check_output', self.fake_output_work_status): 272 kubernetes_e2e.main(args) 273 274 self.assertNotIn('E2E_OPT', self.envs) 275 version = 'gs://kubernetes-release-dev/bazel/v1.7.0-alpha.0.1320+599539dc0b9997/bin/linux/amd64/' # pylint: disable=line-too-long 276 self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1]) 277 called = False 278 for call in self.callstack: 279 self.assertFalse(call.startswith('docker')) 280 if call == 'hack/print-workspace-status.sh': 281 called = True 282 self.assertTrue(called) 283 284 def test_local_env(self): 285 """ 286 Ensure that host variables (such as GOPATH) are included, 287 and added envs/env files overwrite os environment. 288 """ 289 mode = kubernetes_e2e.LocalMode('/orig-workspace', '/random-artifacts') 290 mode.add_environment(*( 291 'FOO=BAR', 'GOPATH=/go/path', 'WORKSPACE=/new/workspace')) 292 mode.add_os_environment(*('USER=jenkins', 'FOO=BAZ', 'GOOS=linux')) 293 with tempfile.NamedTemporaryFile() as temp: 294 temp.write('USER=prow') 295 temp.flush() 296 mode.add_file(temp.name) 297 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 298 mode.start([]) 299 self.assertIn(('FOO', 'BAR'), self.envs.viewitems()) 300 self.assertIn(('WORKSPACE', '/new/workspace'), self.envs.viewitems()) 301 self.assertIn(('GOPATH', '/go/path'), self.envs.viewitems()) 302 self.assertIn(('USER', 'prow'), self.envs.viewitems()) 303 self.assertIn(('GOOS', 'linux'), self.envs.viewitems()) 304 self.assertNotIn(('USER', 'jenkins'), self.envs.viewitems()) 305 self.assertNotIn(('FOO', 'BAZ'), self.envs.viewitems()) 306 307 def test_kubeadm_periodic(self): 308 """Make sure kubeadm periodic mode is fine overall.""" 309 args = kubernetes_e2e.parse_args(['--mode=local', '--kubeadm=periodic']) 310 self.assertEqual(args.mode, 'local') 311 self.assertEqual(args.kubeadm, 'periodic') 312 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 313 with Stub(kubernetes_e2e, 'check_output', self.fake_output_work_status): 314 kubernetes_e2e.main(args) 315 316 self.assertNotIn('E2E_OPT', self.envs) 317 version = 'gs://kubernetes-release-dev/bazel/v1.7.0-alpha.0.1320+599539dc0b9997/bin/linux/amd64/' # pylint: disable=line-too-long 318 self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1]) 319 called = False 320 for call in self.callstack: 321 self.assertFalse(call.startswith('docker')) 322 if call == 'hack/print-workspace-status.sh': 323 called = True 324 self.assertTrue(called) 325 326 def test_kubeadm_periodic_v1_6(self): 327 """Make sure kubeadm periodic mode has correct version on v1.6""" 328 args = kubernetes_e2e.parse_args(['--mode=local', '--kubeadm=periodic']) 329 self.assertEqual(args.mode, 'local') 330 self.assertEqual(args.kubeadm, 'periodic') 331 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 332 with Stub(kubernetes_e2e, 'check_output', self.fake_output_work_status_v1_6): 333 kubernetes_e2e.main(args) 334 335 self.assertNotIn('E2E_OPT', self.envs) 336 version = 'gs://kubernetes-release-dev/bazel/v1.6.4-beta.0.18+84febd4537dd19/build/debs/' 337 self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1]) 338 called = False 339 for call in self.callstack: 340 self.assertFalse(call.startswith('docker')) 341 if call == 'hack/print-workspace-status.sh': 342 called = True 343 self.assertTrue(called) 344 345 def test_kubeadm_pull(self): 346 """Make sure kubeadm pull mode is fine overall.""" 347 args = kubernetes_e2e.parse_args([ 348 '--mode=local', 349 '--kubeadm=pull', 350 '--use-shared-build=bazel' 351 ]) 352 self.assertEqual(args.mode, 'local') 353 self.assertEqual(args.kubeadm, 'pull') 354 self.assertEqual(args.use_shared_build, 'bazel') 355 356 gcs_bucket = "gs://kubernetes-release-dev/bazel/v1.8.0-beta.1.132+599539dc0b9997" 357 358 def fake_gcs_path(path): 359 bazel_default = os.path.join( 360 'gs://kubernetes-jenkins/shared-results', 'bazel-build-location.txt') 361 self.assertEqual(path, bazel_default) 362 return gcs_bucket 363 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 364 with Stub(kubernetes_e2e, 'read_gcs_path', fake_gcs_path): 365 kubernetes_e2e.main(args) 366 367 self.assertNotIn('E2E_OPT', self.envs) 368 version = '%s/bin/linux/amd64/' % gcs_bucket 369 self.assertIn('--kubernetes-anywhere-kubeadm-version=%s' % version, self.callstack[-1]) 370 371 def test_kubeadm_invalid(self): 372 """Make sure kubeadm invalid mode exits unsuccessfully.""" 373 with self.assertRaises(SystemExit) as sysexit: 374 kubernetes_e2e.parse_args(['--mode=local', '--kubeadm=deploy']) 375 376 self.assertEqual(sysexit.exception.code, 2) 377 378 def test_docker(self): 379 """Make sure docker mode is fine overall.""" 380 args = kubernetes_e2e.parse_args(['--mode=docker']) 381 self.assertEqual(args.mode, 'docker') 382 with Stub(kubernetes_e2e, 'check_env', fake_bomb): 383 kubernetes_e2e.main(args) 384 385 self.assertEqual(self.envs, {}) 386 call = self.callstack[-2] 387 self.assertTrue(call.startswith('docker'), call) 388 389 def test_default_tag(self): 390 """Ensure the default tag exists on gcr.io.""" 391 args = kubernetes_e2e.parse_args() 392 match = re.match('gcr.io/([^:]+):(.+)', kubernetes_e2e.kubekins(args.tag)) 393 self.assertIsNotNone(match) 394 url = 'https://gcr.io/v2/%s/manifests/%s' % (match.group(1), 395 match.group(2)) 396 data = json.loads(urllib.urlopen(url).read()) 397 self.assertNotIn('errors', data) 398 self.assertIn('name', data) 399 400 def test_docker_env(self): 401 """ 402 Ensure that host variables (such as GOPATH) are excluded, 403 and OS envs are included. 404 """ 405 mode = kubernetes_e2e.DockerMode( 406 'fake-container', '/host-workspace', False, 'fake-tag', []) 407 mode.add_environment(*('FOO=BAR', 'GOPATH=/something/else', 408 'WORKSPACE=/new/workspace')) 409 mode.add_os_environment('USER=jenkins') 410 self.assertIn('FOO=BAR', mode.cmd) 411 self.assertIn('WORKSPACE=/workspace', mode.cmd) 412 self.assertNotIn('GOPATH=/something/else', mode.cmd) 413 self.assertIn('USER=jenkins', mode.cmd) 414 415 def test_image_family(self): 416 """Make sure --image-family fetches the latest image correctly.""" 417 args = kubernetes_e2e.parse_args([ 418 '--mode=local', 419 '--image-family=cos-stable', 420 '--image-project=cos-cloud']) 421 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 422 with Stub( 423 kubernetes_e2e, 424 'check_output', 425 self.fake_output_get_latest_image): 426 kubernetes_e2e.main(args) 427 self.assertEqual( 428 self.envs['KUBE_GCE_NODE_IMAGE'], 'cos-stable-59-9460-64-0') 429 self.assertEqual(self.envs['KUBE_GCE_NODE_PROJECT'], 'cos-cloud') 430 431 def test_parse_args_order_agnostic(self): 432 args = kubernetes_e2e.parse_args([ 433 '--mode=local', 434 '--some-kubetest-arg=foo', 435 '--cluster=test']) 436 self.assertEqual(args.kubetest_args, ['--some-kubetest-arg=foo']) 437 self.assertEqual(args.mode, 'local') 438 self.assertEqual(args.cluster, 'test') 439 440 def test_gcp_network(self): 441 args = kubernetes_e2e.parse_args(['--mode=local', '--cluster=test']) 442 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 443 kubernetes_e2e.main(args) 444 lastcall = self.callstack[-1] 445 self.assertIn('--gcp-network=test', lastcall) 446 447 def test_env_local(self): 448 env = 'FOO' 449 value = 'BLAT' 450 args = kubernetes_e2e.parse_args([ 451 '--mode=local', 452 '--env={env}={value}'.format(env=env, value=value), 453 ]) 454 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 455 kubernetes_e2e.main(args) 456 self.assertIn(env, self.envs) 457 self.assertEqual(self.envs[env], value) 458 459 def test_env_docker(self): 460 env = 'FOO=bar blatz' 461 args = kubernetes_e2e.parse_args([ 462 '--mode=docker', 463 '--env=' + env, 464 ]) 465 kubernetes_e2e.main(args) 466 self.assertIn('-e '+env, self.callstack[-2]) 467 468 def test_aws(self): 469 temp = tempfile.NamedTemporaryFile() 470 args = kubernetes_e2e.parse_args([ 471 '--aws', 472 '--cluster=foo', 473 '--aws-cluster-domain=test-aws.k8s.io', 474 '--aws-ssh=%s' % temp.name, 475 '--aws-pub=%s' % temp.name, 476 '--aws-cred=%s' % temp.name, 477 ]) 478 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 479 kubernetes_e2e.main(args) 480 481 lastcall = self.callstack[-1] 482 self.assertIn('kops-e2e-runner.sh', lastcall) 483 self.assertIn('--kops-cluster=foo.test-aws.k8s.io', lastcall) 484 self.assertIn('--kops-zones', lastcall) 485 self.assertIn('--kops-state=s3://k8s-kops-jenkins/', lastcall) 486 self.assertIn('--kops-nodes=4', lastcall) 487 self.assertIn('--kops-ssh-key', lastcall) 488 489 self.assertEqual( 490 self.envs['JENKINS_AWS_SSH_PRIVATE_KEY_FILE'], temp.name) 491 self.assertEqual( 492 self.envs['JENKINS_AWS_SSH_PUBLIC_KEY_FILE'], temp.name) 493 self.assertEqual( 494 self.envs['JENKINS_AWS_CREDENTIALS_FILE'], temp.name) 495 496 def test_use_shared_build(self): 497 # normal path 498 args = kubernetes_e2e.parse_args([ 499 '--use-shared-build=bazel' 500 ]) 501 def expect_bazel_gcs(path): 502 bazel_default = os.path.join( 503 'gs://kubernetes-jenkins/shared-results', 'bazel-build-location.txt') 504 self.assertEqual(path, bazel_default) 505 return always_kubernetes() 506 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 507 with Stub(kubernetes_e2e, 'read_gcs_path', expect_bazel_gcs): 508 with Stub(time, 'sleep', fake_pass): 509 kubernetes_e2e.main(args) 510 lastcall = self.callstack[-1] 511 self.assertIn('--extract=kubernetes', lastcall) 512 # normal path, not bazel 513 args = kubernetes_e2e.parse_args([ 514 '--use-shared-build' 515 ]) 516 def expect_normal_gcs(path): 517 bazel_default = os.path.join( 518 'gs://kubernetes-jenkins/shared-results', 'build-location.txt') 519 self.assertEqual(path, bazel_default) 520 return always_kubernetes() 521 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 522 with Stub(kubernetes_e2e, 'read_gcs_path', expect_normal_gcs): 523 kubernetes_e2e.main(args) 524 lastcall = self.callstack[-1] 525 self.assertIn('--extract=kubernetes', lastcall) 526 # test failure to read shared path from GCS 527 with Stub(kubernetes_e2e, 'check_env', self.fake_check_env): 528 with Stub(kubernetes_e2e, 'read_gcs_path', raise_urllib2_error): 529 with Stub(os, 'getcwd', always_kubernetes): 530 with Stub(time, 'sleep', fake_pass): 531 try: 532 kubernetes_e2e.main(args) 533 except RuntimeError as err: 534 if not err.message.startswith('Failed to get shared build location'): 535 raise err 536 537 if __name__ == '__main__': 538 unittest.main()