github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/scenarios/kubernetes_e2e.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=bad-continuation 19 20 """Runs kubernetes e2e test with specified config""" 21 22 import argparse 23 import hashlib 24 import os 25 import random 26 import re 27 import shutil 28 import signal 29 import subprocess 30 import sys 31 import tempfile 32 import traceback 33 import urllib2 34 import time 35 36 ORIG_CWD = os.getcwd() # Checkout changes cwd 37 38 # Note: This variable is managed by experiment/bump_e2e_image.sh. 39 DEFAULT_KUBEKINS_TAG = 'v20171002-aae4252c' 40 41 def test_infra(*paths): 42 """Return path relative to root of test-infra repo.""" 43 return os.path.join(ORIG_CWD, os.path.dirname(__file__), '..', *paths) 44 45 46 def check(*cmd): 47 """Log and run the command, raising on errors.""" 48 print >>sys.stderr, 'Run:', cmd 49 subprocess.check_call(cmd) 50 51 52 def check_output(*cmd): 53 """Log and run the command, raising on errors, return output""" 54 print >>sys.stderr, 'Run:', cmd 55 return subprocess.check_output(cmd) 56 57 58 def check_env(env, *cmd): 59 """Log and run the command with a specific env, raising on errors.""" 60 print >>sys.stderr, 'Environment:' 61 for key, value in env.items(): 62 print >>sys.stderr, '%s=%s' % (key, value) 63 print >>sys.stderr, 'Run:', cmd 64 subprocess.check_call(cmd, env=env) 65 66 67 def kubekins(tag): 68 """Return full path to kubekins-e2e:tag.""" 69 return 'gcr.io/k8s-testimages/kubekins-e2e:%s' % tag 70 71 def parse_env(env): 72 """Returns (FOO, BAR=MORE) for FOO=BAR=MORE.""" 73 return env.split('=', 1) 74 75 def aws_role_config(profile, arn): 76 return (('[profile jenkins-assumed-role]\n' + 77 'role_arn = %s\n' + 78 'source_profile = %s\n') % 79 (arn, profile)) 80 81 def kubeadm_version(mode, shared_build_gcs_path): 82 """Return string to use for kubeadm version, given the job's mode (ci/pull/periodic).""" 83 version = '' 84 if mode in ['ci', 'periodic']: 85 # This job only runs against the kubernetes repo, and bootstrap.py leaves the 86 # current working directory at the repository root. Grab the SCM_REVISION so we 87 # can use the .debs built during the bazel-build job that should have already 88 # succeeded. 89 status = re.search( 90 r'STABLE_BUILD_SCM_REVISION ([^\n]+)', 91 check_output('hack/print-workspace-status.sh') 92 ) 93 if not status: 94 raise ValueError('STABLE_BUILD_SCM_REVISION not found') 95 version = status.group(1) 96 97 # Work-around for release-1.6 jobs, which still upload debs to an older 98 # location (without os/arch prefixes). 99 # TODO(pipejakob): remove this when we no longer support 1.6.x. 100 if version.startswith('v1.6.'): 101 return 'gs://kubernetes-release-dev/bazel/%s/build/debs/' % version 102 103 # The path given here should match jobs/ci-kubernetes-bazel-build.sh 104 return 'gs://kubernetes-release-dev/bazel/%s/bin/linux/amd64/' % version 105 106 elif mode == 'pull': 107 # The format of shared_build_gcs_path looks like: 108 # gs://kubernetes-release-dev/bazel/<git-describe-output> 109 # Add bin/linux/amd64 yet to that path so it points to the dir with the debs 110 return '%s/bin/linux/amd64/' % shared_build_gcs_path 111 112 elif mode == 'stable': 113 # This job need not run against the kubernetes repo and uses the stable version 114 # of kubeadm packages. This mode may be desired when kubeadm itself is not the 115 # SUT (System Under Test). 116 return 'stable' 117 118 else: 119 raise ValueError("Unknown kubeadm mode given: %s" % mode) 120 121 class LocalMode(object): 122 """Runs e2e tests by calling kubetest.""" 123 def __init__(self, workspace, artifacts): 124 self.command = 'kubetest' 125 self.workspace = workspace 126 self.artifacts = artifacts 127 self.env = [] 128 self.os_env = [] 129 self.env_files = [] 130 self.add_environment( 131 'HOME=%s' % workspace, 132 'WORKSPACE=%s' % workspace, 133 'PATH=%s' % os.getenv('PATH'), 134 ) 135 136 def add_environment(self, *envs): 137 """Adds FOO=BAR to the list of environment overrides.""" 138 self.env.extend(parse_env(e) for e in envs) 139 140 def add_os_environment(self, *envs): 141 """Adds FOO=BAR to the list of os environment overrides.""" 142 self.os_env.extend(parse_env(e) for e in envs) 143 144 def add_file(self, env_file): 145 """Reads all FOO=BAR lines from env_file.""" 146 with open(env_file) as fp: 147 for line in fp: 148 line = line.rstrip() 149 if not line or line.startswith('#'): 150 continue 151 self.env_files.append(parse_env(line)) 152 153 def add_env(self, env): 154 self.env_files.append(parse_env(env)) 155 156 def add_aws_cred(self, priv, pub, cred): 157 """Sets aws keys and credentials.""" 158 ssh_dir = os.path.join(self.workspace, '.ssh') 159 if not os.path.isdir(ssh_dir): 160 os.makedirs(ssh_dir) 161 162 cred_dir = os.path.join(self.workspace, '.aws') 163 if not os.path.isdir(cred_dir): 164 os.makedirs(cred_dir) 165 166 aws_ssh = os.path.join(ssh_dir, 'kube_aws_rsa') 167 aws_pub = os.path.join(ssh_dir, 'kube_aws_rsa.pub') 168 aws_cred = os.path.join(cred_dir, 'credentials') 169 shutil.copy(priv, aws_ssh) 170 shutil.copy(pub, aws_pub) 171 shutil.copy(cred, aws_cred) 172 173 self.add_environment( 174 'JENKINS_AWS_SSH_PRIVATE_KEY_FILE=%s' % priv, 175 'JENKINS_AWS_SSH_PUBLIC_KEY_FILE=%s' % pub, 176 'JENKINS_AWS_CREDENTIALS_FILE=%s' % cred, 177 ) 178 179 def add_aws_role(self, profile, arn): 180 with open(os.path.join(self.workspace, '.aws', 'config'), 'w') as cfg: 181 cfg.write(aws_role_config(profile, arn)) 182 self.add_environment('AWS_SDK_LOAD_CONFIG=true') 183 return 'jenkins-assumed-role' 184 185 def add_gce_ssh(self, priv, pub): 186 """Copies priv, pub keys to $WORKSPACE/.ssh.""" 187 ssh_dir = os.path.join(self.workspace, '.ssh') 188 if not os.path.isdir(ssh_dir): 189 os.makedirs(ssh_dir) 190 191 gce_ssh = os.path.join(ssh_dir, 'google_compute_engine') 192 gce_pub = os.path.join(ssh_dir, 'google_compute_engine.pub') 193 shutil.copy(priv, gce_ssh) 194 shutil.copy(pub, gce_pub) 195 self.add_environment( 196 'JENKINS_GCE_SSH_PRIVATE_KEY_FILE=%s' % gce_ssh, 197 'JENKINS_GCE_SSH_PUBLIC_KEY_FILE=%s' % gce_pub, 198 ) 199 200 @staticmethod 201 def add_service_account(path): 202 """Returns path.""" 203 return path 204 205 def add_k8s(self, *a, **kw): 206 """Add specified k8s.io repos (noop).""" 207 pass 208 209 def use_latest_image(self, image_family, image_project): 210 """Gets the latest image from the image_family in the image_project.""" 211 out = check_output( 212 'gcloud', 'compute', 'images', 'describe-from-family', 213 image_family, '--project=%s' % image_project) 214 latest_image = next( 215 (line[6:].strip() for line in out.split('\n') if ( 216 line.startswith('name: '))), 217 None) 218 if not latest_image: 219 raise ValueError( 220 'Failed to get the latest image from family %s in project %s' % ( 221 image_family, image_project)) 222 # TODO(yguo0905): Support this in GKE. 223 self.add_environment( 224 'KUBE_GCE_NODE_IMAGE=%s' % latest_image, 225 'KUBE_GCE_NODE_PROJECT=%s' % image_project) 226 print >>sys.stderr, 'Set KUBE_GCE_NODE_IMAGE=%s' % latest_image 227 print >>sys.stderr, 'Set KUBE_GCE_NODE_PROJECT=%s' % image_project 228 229 def add_aws_runner(self): 230 """Start with kops-e2e-runner.sh""" 231 # TODO(Krzyzacy):retire kops-e2e-runner.sh 232 self.command = os.path.join(self.workspace, 'kops-e2e-runner.sh') 233 234 def start(self, args): 235 """Starts kubetest.""" 236 print >>sys.stderr, 'starts with local mode' 237 env = {} 238 env.update(self.os_env) 239 env.update(self.env_files) 240 env.update(self.env) 241 check_env(env, self.command, *args) 242 243 244 class DockerMode(object): 245 """Runs e2e tests via docker run kubekins-e2e.""" 246 def __init__(self, container, artifacts, sudo, tag, mount_paths): 247 self.tag = tag 248 try: # Pull a newer version if one exists 249 check('docker', 'pull', kubekins(tag)) 250 except subprocess.CalledProcessError: 251 pass 252 253 print 'Starting %s...' % container 254 255 self.workspace = '/workspace' 256 self.container = container 257 self.local_artifacts = artifacts 258 self.artifacts = os.path.join(self.workspace, '_artifacts') 259 self.cmd = [ 260 'docker', 'run', '--rm', 261 '--name=%s' % container, 262 '-v', '%s:%s' % (artifacts, self.artifacts), 263 '-v', '/etc/localtime:/etc/localtime:ro', 264 ] 265 for path in mount_paths or []: 266 self.cmd.extend(['-v', path]) 267 268 if sudo: 269 self.cmd.extend(['-v', '/var/run/docker.sock:/var/run/docker.sock']) 270 self.add_env('HOME=%s' % self.workspace) 271 self.add_env('WORKSPACE=%s' % self.workspace) 272 self.cmd.append( 273 '--entrypoint=/workspace/kubetest' 274 ) 275 276 def add_environment(self, *envs): 277 """Adds FOO=BAR to the -e list for docker. 278 279 Host-specific environment variables are ignored.""" 280 # TODO(krzyzacy) change this to a whitelist? 281 docker_env_ignore = [ 282 'GOOGLE_APPLICATION_CREDENTIALS', 283 'GOPATH', 284 'GOROOT', 285 'HOME', 286 'PATH', 287 'PWD', 288 'WORKSPACE' 289 ] 290 for env in envs: 291 key, _value = parse_env(env) 292 if key in docker_env_ignore: 293 print >>sys.stderr, 'Skipping environment variable %s' % env 294 else: 295 self.add_env(env) 296 297 def add_os_environment(self, *envs): 298 """Adds os envs as FOO=BAR to the -e list for docker.""" 299 self.add_environment(*envs) 300 301 def add_file(self, env_file): 302 """Adds the file to the --env-file list.""" 303 self.cmd.extend(['--env-file', env_file]) 304 305 def add_env(self, env): 306 """Adds a single environment variable to the -e list for docker. 307 308 Does not check against any blacklists.""" 309 self.cmd.extend(['-e', env]) 310 311 def add_k8s(self, k8s, *repos): 312 """Add the specified k8s.io repos into container.""" 313 for repo in repos: 314 self.cmd.extend([ 315 '-v', '%s/%s:/go/src/k8s.io/%s' % (k8s, repo, repo)]) 316 317 def add_aws_cred(self, priv, pub, cred): 318 """Mounts aws keys/creds inside the container.""" 319 aws_ssh = os.path.join(self.workspace, '.ssh', 'kube_aws_rsa') 320 aws_pub = '%s.pub' % aws_ssh 321 aws_cred = os.path.join(self.workspace, '.aws', 'credentials') 322 323 self.cmd.extend([ 324 '-v', '%s:%s:ro' % (priv, aws_ssh), 325 '-v', '%s:%s:ro' % (pub, aws_pub), 326 '-v', '%s:%s:ro' % (cred, aws_cred), 327 ]) 328 329 def add_aws_role(self, profile, arn): 330 with tempfile.NamedTemporaryFile(prefix='aws-config', delete=False) as cfg: 331 cfg.write(aws_role_config(profile, arn)) 332 self.cmd.extend([ 333 '-v', '%s:%s:ro' % (os.path.join(self.workspace, '.aws', 'config'), cfg.name), 334 '-e', 'AWS_SDK_LOAD_CONFIG=true', 335 ]) 336 return 'jenkins-assumed-role' 337 338 def add_aws_runner(self): 339 """Run kops_aws_runner for kops-aws jobs.""" 340 self.cmd.append( 341 '--entrypoint=%s/kops-e2e-runner.sh' % self.workspace 342 ) 343 344 def add_gce_ssh(self, priv, pub): 345 """Mounts priv and pub inside the container.""" 346 gce_ssh = os.path.join(self.workspace, '.ssh', 'google_compute_engine') 347 gce_pub = '%s.pub' % gce_ssh 348 self.cmd.extend([ 349 '-v', '%s:%s:ro' % (priv, gce_ssh), 350 '-v', '%s:%s:ro' % (pub, gce_pub), 351 '-e', 'JENKINS_GCE_SSH_PRIVATE_KEY_FILE=%s' % gce_ssh, 352 '-e', 'JENKINS_GCE_SSH_PUBLIC_KEY_FILE=%s' % gce_pub]) 353 354 def add_service_account(self, path): 355 """Mounts path at /service-account.json inside the container.""" 356 service = '/service-account.json' 357 self.cmd.extend(['-v', '%s:%s:ro' % (path, service)]) 358 return service 359 360 def start(self, args): 361 """Runs kubetest inside a docker container.""" 362 print >>sys.stderr, 'starts with docker mode' 363 cmd = list(self.cmd) 364 cmd.append(kubekins(self.tag)) 365 cmd.extend(args) 366 signal.signal(signal.SIGTERM, self.sig_handler) 367 signal.signal(signal.SIGINT, self.sig_handler) 368 try: 369 check(*cmd) 370 finally: # Ensure docker files are readable by bootstrap 371 if not os.path.isdir(self.local_artifacts): # May not exist 372 pass 373 try: 374 check('sudo', 'chmod', '-R', 'o+r', self.local_artifacts) 375 except subprocess.CalledProcessError: # fails outside CI 376 traceback.print_exc() 377 378 def sig_handler(self, _signo, _frame): 379 """Stops container upon receive signal.SIGTERM and signal.SIGINT.""" 380 print >>sys.stderr, 'docker stop (signo=%s, frame=%s)' % (_signo, _frame) 381 check('docker', 'stop', self.container) 382 383 384 def cluster_name(cluster, build): 385 """Return or select a cluster name.""" 386 if cluster: 387 return cluster 388 if len(build) < 20: 389 return 'e2e-%s' % build 390 return 'e2e-%s' % hashlib.md5(build).hexdigest()[:10] 391 392 393 # TODO(krzyzacy): Move this into kubetest 394 def build_kops(kops, mode): 395 """Build kops, set kops related envs.""" 396 if not os.path.basename(kops) == 'kops': 397 raise ValueError(kops) 398 version = 'pull-' + check_output('git', 'describe', '--always').strip() 399 job = os.getenv('JOB_NAME', 'pull-kops-e2e-kubernetes-aws') 400 gcs = 'gs://kops-ci/pulls/%s' % job 401 gapi = 'https://storage.googleapis.com/kops-ci/pulls/%s' % job 402 mode.add_environment( 403 'KOPS_BASE_URL=%s/%s' % (gapi, version), 404 'GCS_LOCATION=%s' % gcs 405 ) 406 check('make', 'gcs-publish-ci', 'VERSION=%s' % version, 'GCS_LOCATION=%s' % gcs) 407 408 409 def set_up_aws(workspace, args, mode, cluster, runner_args): 410 """Set up aws related envs.""" 411 for path in [args.aws_ssh, args.aws_pub, args.aws_cred]: 412 if not os.path.isfile(os.path.expandvars(path)): 413 raise IOError(path, os.path.expandvars(path)) 414 mode.add_aws_cred(args.aws_ssh, args.aws_pub, args.aws_cred) 415 416 aws_ssh = os.path.join(workspace, '.ssh', 'kube_aws_rsa') 417 profile = args.aws_profile 418 if args.aws_role_arn: 419 profile = mode.add_aws_role(profile, args.aws_role_arn) 420 421 zones = args.kops_zones or random.choice([ 422 'us-west-1a', 423 'us-west-1c', 424 'us-west-2a', 425 'us-west-2b', 426 'us-east-1a', 427 'us-east-1d', 428 'us-east-2a', 429 'us-east-2b', 430 ]) 431 regions = ','.join([zone[:-1] for zone in zones.split(',')]) 432 433 mode.add_environment( 434 'AWS_PROFILE=%s' % profile, 435 'AWS_DEFAULT_PROFILE=%s' % profile, 436 'KOPS_REGIONS=%s' % regions, 437 ) 438 439 if args.aws_cluster_domain: 440 cluster = '%s.%s' % (cluster, args.aws_cluster_domain) 441 442 runner_args.extend([ 443 '--kops-cluster=%s' % cluster, 444 '--kops-zones=%s' % zones, 445 '--kops-state=%s' % args.kops_state, 446 '--kops-nodes=%s' % args.kops_nodes, 447 '--kops-ssh-key=%s' % aws_ssh, 448 ]) 449 # TODO(krzyzacy):Remove after retire kops-e2e-runner.sh 450 mode.add_aws_runner() 451 452 def read_gcs_path(gcs_path): 453 """reads a gcs path (gs://...) by GETing storage.googleapis.com""" 454 link = gcs_path.replace('gs://', 'https://storage.googleapis.com/') 455 loc = urllib2.urlopen(link).read() 456 print >>sys.stderr, "Read GCS Path: %s" % loc 457 return loc 458 459 def get_shared_gcs_path(gcs_shared, use_shared_build): 460 """return the shared path for this set of jobs using args and $PULL_REFS.""" 461 build_file = '' 462 if use_shared_build: 463 build_file += use_shared_build + '-' 464 build_file += 'build-location.txt' 465 return os.path.join(gcs_shared, os.getenv('PULL_REFS', ''), build_file) 466 467 def main(args): 468 """Set up env, start kubekins-e2e, handle termination. """ 469 # pylint: disable=too-many-branches,too-many-statements,too-many-locals 470 471 # Rules for env var priority here in docker: 472 # -e FOO=a -e FOO=b -> FOO=b 473 # --env-file FOO=a --env-file FOO=b -> FOO=b 474 # -e FOO=a --env-file FOO=b -> FOO=a(!!!!) 475 # --env-file FOO=a -e FOO=b -> FOO=b 476 # 477 # So if you overwrite FOO=c for a local run it will take precedence. 478 # 479 480 # Set up workspace/artifacts dir 481 workspace = os.environ.get('WORKSPACE', os.getcwd()) 482 artifacts = os.path.join(workspace, '_artifacts') 483 if not os.path.isdir(artifacts): 484 os.makedirs(artifacts) 485 486 container = '%s-%s' % (os.environ.get('JOB_NAME'), os.environ.get('BUILD_NUMBER')) 487 if args.mode == 'docker': 488 sudo = args.docker_in_docker or args.build is not None 489 mode = DockerMode(container, artifacts, sudo, args.tag, args.mount_paths) 490 elif args.mode == 'local': 491 mode = LocalMode(workspace, artifacts) # pylint: disable=bad-option-value 492 else: 493 raise ValueError(args.mode) 494 495 for env_file in args.env_file: 496 mode.add_file(test_infra(env_file)) 497 for env in args.env: 498 mode.add_env(env) 499 500 # TODO(fejta): remove after next image push 501 mode.add_environment('KUBETEST_MANUAL_DUMP=y') 502 runner_args = [ 503 '-v', 504 '--dump=%s' % mode.artifacts, 505 ] 506 507 if args.service_account: 508 runner_args.append( 509 '--gcp-service-account=%s' % mode.add_service_account(args.service_account)) 510 511 shared_build_gcs_path = "" 512 if args.use_shared_build is not None: 513 # find shared build location from GCS 514 gcs_path = get_shared_gcs_path(args.gcs_shared, args.use_shared_build) 515 print >>sys.stderr, 'Getting shared build location from: '+gcs_path 516 # retry loop for reading the location 517 attempts_remaining = 12 518 while True: 519 attempts_remaining -= 1 520 try: 521 # tell kubetest to extract from this location 522 shared_build_gcs_path = read_gcs_path(gcs_path) 523 args.kubetest_args.append('--extract=' + shared_build_gcs_path) 524 args.build = None 525 break 526 except urllib2.URLError as err: 527 print >>sys.stderr, 'Failed to get shared build location: %s' % err 528 if attempts_remaining > 0: 529 print >>sys.stderr, 'Waiting 5 seconds and retrying...' 530 time.sleep(5) 531 else: 532 raise RuntimeError('Failed to get shared build location too many times!') 533 534 elif args.build is not None: 535 if args.build == '': 536 # Empty string means --build was passed without any arguments; 537 # if --build wasn't passed, args.build would be None 538 runner_args.append('--build') 539 else: 540 runner_args.append('--build=%s' % args.build) 541 k8s = os.getcwd() 542 if not os.path.basename(k8s) == 'kubernetes': 543 raise ValueError(k8s) 544 mode.add_k8s(os.path.dirname(k8s), 'kubernetes', 'release') 545 546 if args.kops_build: 547 build_kops(os.getcwd(), mode) 548 549 if args.stage is not None: 550 runner_args.append('--stage=%s' % args.stage) 551 if args.aws: 552 for line in check_output('hack/print-workspace-status.sh').split('\n'): 553 if 'gitVersion' in line: 554 _, version = line.strip().split(' ') 555 break 556 else: 557 raise ValueError('kubernetes version not found in workspace status') 558 runner_args.append('--kops-kubernetes-version=%s/%s' % ( 559 args.stage.replace('gs://', 'https://storage.googleapis.com/'), 560 version)) 561 562 # TODO(fejta): move these out of this file 563 if args.up == 'true': 564 runner_args.append('--up') 565 if args.down == 'true': 566 runner_args.append('--down') 567 if args.test == 'true': 568 runner_args.append('--test') 569 570 cluster = cluster_name(args.cluster, os.getenv('BUILD_NUMBER', 0)) 571 runner_args.append('--cluster=%s' % cluster) 572 runner_args.append('--gcp-network=%s' % cluster) 573 runner_args.extend(args.kubetest_args) 574 575 if args.use_logexporter: 576 # TODO(fejta): Take the below value through a flag instead of env var. 577 runner_args.append('--logexporter-gcs-path=%s' % os.environ.get('GCS_ARTIFACTS_DIR', '')) 578 579 if args.kubeadm: 580 version = kubeadm_version(args.kubeadm, shared_build_gcs_path) 581 runner_args.extend([ 582 '--kubernetes-anywhere-path=%s' % os.path.join(workspace, 'kubernetes-anywhere'), 583 '--kubernetes-anywhere-phase2-provider=kubeadm', 584 '--kubernetes-anywhere-cluster=%s' % cluster, 585 '--kubernetes-anywhere-kubeadm-version=%s' % version, 586 ]) 587 588 if args.kubeadm == "pull": 589 # If this is a pull job; the kubelet version should equal 590 # the kubeadm version here: we should use debs from the PR build 591 runner_args.extend([ 592 '--kubernetes-anywhere-kubelet-version=%s' % version, 593 ]) 594 595 if args.aws: 596 set_up_aws(mode.workspace, args, mode, cluster, runner_args) 597 elif args.gce_ssh: 598 mode.add_gce_ssh(args.gce_ssh, args.gce_pub) 599 600 # TODO(fejta): delete this? 601 mode.add_os_environment(*( 602 '%s=%s' % (k, v) for (k, v) in os.environ.items())) 603 604 mode.add_environment( 605 # Boilerplate envs 606 # Skip gcloud update checking 607 'CLOUDSDK_COMPONENT_MANAGER_DISABLE_UPDATE_CHECK=true', 608 # Use default component update behavior 609 'CLOUDSDK_EXPERIMENTAL_FAST_COMPONENT_UPDATE=false', 610 # AWS 611 'KUBE_AWS_INSTANCE_PREFIX=%s' % cluster, 612 # GCE 613 'INSTANCE_PREFIX=%s' % cluster, 614 'KUBE_GCE_INSTANCE_PREFIX=%s' % cluster, 615 ) 616 617 if args and args.image_family and args.image_project: 618 mode.use_latest_image(args.image_family, args.image_project) 619 620 mode.start(runner_args) 621 622 def create_parser(): 623 """Create argparser.""" 624 parser = argparse.ArgumentParser() 625 parser.add_argument( 626 '--mode', default='local', choices=['local', 'docker']) 627 parser.add_argument( 628 '--env-file', default=[], action="append", 629 help='Job specific environment file') 630 parser.add_argument( 631 '--env', default=[], action="append", 632 help='Job specific environment setting ' + 633 '(usage: "--env=VAR=SETTING" will set VAR to SETTING).') 634 parser.add_argument( 635 '--image-family', 636 help='The image family from which to fetch the latest image') 637 parser.add_argument( 638 '--image-project', 639 help='The image project from which to fetch the test images') 640 parser.add_argument( 641 '--gce-ssh', 642 default=os.environ.get('JENKINS_GCE_SSH_PRIVATE_KEY_FILE'), 643 help='Path to .ssh/google_compute_engine keys') 644 parser.add_argument( 645 '--gce-pub', 646 default=os.environ.get('JENKINS_GCE_SSH_PUBLIC_KEY_FILE'), 647 help='Path to pub gce ssh key') 648 parser.add_argument( 649 '--service-account', 650 default=os.environ.get('GOOGLE_APPLICATION_CREDENTIALS'), 651 help='Path to service-account.json') 652 parser.add_argument( 653 '--mount-paths', 654 action='append', 655 help='Paths that should be mounted within the docker container in the form local:remote') 656 parser.add_argument( 657 '--build', nargs='?', default=None, const='', 658 help='Build kubernetes binaries if set, optionally specifying strategy') 659 parser.add_argument( 660 '--use-shared-build', nargs='?', default=None, const='', 661 help='Use prebuilt kubernetes binaries if set, optionally specifying strategy') 662 parser.add_argument( 663 '--gcs-shared', 664 default='gs://kubernetes-jenkins/shared-results/', 665 help='Get shared build from this bucket') 666 parser.add_argument( 667 '--cluster', default='bootstrap-e2e', help='Name of the cluster') 668 parser.add_argument( 669 '--docker-in-docker', action='store_true', help='Enable run docker within docker') 670 parser.add_argument( 671 '--kubeadm', choices=['ci', 'periodic', 'pull', 'stable']) 672 parser.add_argument( 673 '--stage', default=None, help='Stage release to GCS path provided') 674 parser.add_argument( 675 '--tag', default=DEFAULT_KUBEKINS_TAG, help='Use a specific kubekins-e2e tag if set') 676 parser.add_argument( 677 '--test', default='true', help='If we need to run any actual test within kubetest') 678 parser.add_argument( 679 '--down', default='true', help='If we need to tear down the e2e cluster') 680 parser.add_argument( 681 '--up', default='true', help='If we need to bring up a e2e cluster') 682 parser.add_argument( 683 '--use-logexporter', 684 action='store_true', 685 help='If we need to use logexporter tool to upload logs from nodes to GCS directly') 686 parser.add_argument( 687 '--kubetest_args', 688 action='append', 689 default=[], 690 help='Send unrecognized args directly to kubetest') 691 692 # aws 693 parser.add_argument( 694 '--aws', action='store_true', help='E2E job runs in aws') 695 parser.add_argument( 696 '--aws-profile', 697 default=( 698 os.environ.get('AWS_PROFILE') or 699 os.environ.get('AWS_DEFAULT_PROFILE') or 700 'default' 701 ), 702 help='Profile within --aws-cred to use') 703 parser.add_argument( 704 '--aws-role-arn', 705 default=os.environ.get('KOPS_E2E_ROLE_ARN'), 706 help='Use --aws-profile to run as --aws-role-arn if set') 707 parser.add_argument( 708 '--aws-ssh', 709 default=os.environ.get('JENKINS_AWS_SSH_PRIVATE_KEY_FILE'), 710 help='Path to private aws ssh keys') 711 parser.add_argument( 712 '--aws-pub', 713 default=os.environ.get('JENKINS_AWS_SSH_PUBLIC_KEY_FILE'), 714 help='Path to pub aws ssh key') 715 parser.add_argument( 716 '--aws-cred', 717 default=os.environ.get('JENKINS_AWS_CREDENTIALS_FILE'), 718 help='Path to aws credential file') 719 parser.add_argument( 720 '--aws-cluster-domain', help='Domain of the aws cluster for aws-pr jobs') 721 parser.add_argument( 722 '--kops-nodes', default=4, type=int, help='Number of nodes to start') 723 parser.add_argument( 724 '--kops-state', default='s3://k8s-kops-jenkins/', 725 help='Name of the aws state storage') 726 parser.add_argument( 727 '--kops-zones', help='Comma-separated list of zones else random choice') 728 parser.add_argument( 729 '--kops-build', action='store_true', help='If we need to build kops locally') 730 731 return parser 732 733 734 def parse_args(args=None): 735 """Return args, adding unrecognized args to kubetest_args.""" 736 parser = create_parser() 737 args, extra = parser.parse_known_args(args) 738 args.kubetest_args += extra 739 740 if (args.image_family or args.image_project) and args.mode == 'docker': 741 raise ValueError( 742 '--image-family / --image-project is not supported in docker mode') 743 if bool(args.image_family) != bool(args.image_project): 744 raise ValueError( 745 '--image-family and --image-project must be both set or unset') 746 747 if args.aws: 748 # If aws keys are missing, try to fetch from HOME dir 749 if not args.aws_ssh or not args.aws_pub or not args.aws_cred: 750 home = os.environ.get('HOME') 751 if not home: 752 raise ValueError('HOME dir not set!') 753 if not args.aws_ssh: 754 args.aws_ssh = '%s/.ssh/kube_aws_rsa' % home 755 print >>sys.stderr, '-aws-ssh key not set. Defaulting to %s' % args.aws_ssh 756 if not args.aws_pub: 757 args.aws_pub = '%s/.ssh/kube_aws_rsa.pub' % home 758 print >>sys.stderr, '--aws-pub key not set. Defaulting to %s' % args.aws_pub 759 if not args.aws_cred: 760 args.aws_cred = '%s/.aws/credentials' % home 761 print >>sys.stderr, '--aws-cred not set. Defaulting to %s' % args.aws_cred 762 return args 763 764 765 if __name__ == '__main__': 766 main(parse_args())