github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/scenarios/kubernetes_janitor.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 """Dig through jobs/FOO.env, and execute a janitor pass for each of the project""" 21 22 import argparse 23 import json 24 import os 25 import re 26 import subprocess 27 import sys 28 29 ORIG_CWD = os.getcwd() # Checkout changes cwd 30 31 def test_infra(*paths): 32 """Return path relative to root of test-infra repo.""" 33 return os.path.join(ORIG_CWD, os.path.dirname(__file__), '..', *paths) 34 35 36 def check(*cmd): 37 """Log and run the command, raising on errors.""" 38 print >>sys.stderr, 'Run:', cmd 39 subprocess.check_call(cmd) 40 41 42 def parse_project(path): 43 """Parse target env file and return GCP project name.""" 44 with open(path, 'r') as fp: 45 env = fp.read() 46 match = re.search(r'PROJECT=([^\n"]+)', env) 47 if match: 48 project = match.group(1) 49 return project 50 return None 51 52 53 def clean_project(project, hours=24, dryrun=False): 54 """Execute janitor for target GCP project """ 55 # Multiple jobs can share the same project, woooo 56 if project in CHECKED: 57 return 58 CHECKED.add(project) 59 60 cmd = ['python', test_infra('boskos/janitor/janitor.py'), '--project=%s' % project] 61 cmd.append('--hour=%d' % hours) 62 if dryrun: 63 cmd.append('--dryrun') 64 65 try: 66 check(*cmd) 67 except subprocess.CalledProcessError: 68 FAILED.append(project) 69 70 71 BLACKLIST = [ 72 '-soak', # We need to keep deployed resources for test uses 73 'kubernetes-scale', # Let it's up/down job handle the resources 74 ] 75 76 PR_PROJECTS = { 77 # k8s-jkns-pr-bldr-e2e-gce-fdrtn 78 # k8s-jkns-pr-cnry-e2e-gce-fdrtn 79 # cleans up resources older than 3h 80 # which is more than enough for presubmit jobs to finish. 81 'k8s-jkns-pr-gce': 3, 82 'k8s-jkns-pr-gce-bazel': 3, 83 'k8s-jkns-pr-gce-etcd3': 3, 84 'k8s-jkns-pr-gci-gce': 3, 85 'k8s-jkns-pr-gci-gke': 3, 86 'k8s-jkns-pr-gci-kubemark': 3, 87 'k8s-jkns-pr-gke': 3, 88 'k8s-jkns-pr-kubeadm': 3, 89 'k8s-jkns-pr-kubemark': 3, 90 'k8s-jkns-pr-node-e2e': 3, 91 'k8s-jkns-pr-gce-gpus': 3, 92 'k8s-gke-gpu-pr': 3, 93 } 94 95 def check_pr_jobs(): 96 """Handle PR jobs""" 97 for project, expire in PR_PROJECTS.iteritems(): 98 clean_project(project, hours=expire) 99 100 101 def check_ci_jobs(): 102 """Handle CI jobs""" 103 with open(test_infra('jobs/config.json')) as fp: 104 config = json.load(fp) 105 106 match_re = re.compile(r'--gcp-project=(.+)') 107 for value in config.values(): 108 for arg in value.get('args', []): 109 mat = match_re.match(arg) 110 if not mat: 111 continue 112 project = mat.group(1) 113 if any(b in project for b in BLACKLIST): 114 print >>sys.stderr, 'Project %r is blacklisted in ci-janitor' % project 115 continue 116 if project in PR_PROJECTS: 117 continue # CI janitor skips all PR jobs 118 clean_project(project) 119 120 # Hard code node-ci project here 121 clean_project('k8s-jkns-ci-node-e2e') 122 # gke internal project 123 clean_project('gke-e2e-createdelete') 124 125 126 def main(mode): 127 """Run janitor for each project.""" 128 if mode == 'pr': 129 check_pr_jobs() 130 else: 131 check_ci_jobs() 132 133 # Summary 134 print 'Janitor checked %d project, %d failed to clean up.' % (len(CHECKED), len(FAILED)) 135 if FAILED: 136 print >>sys.stderr, 'Failed projects: %r' % FAILED 137 exit(1) 138 139 140 if __name__ == '__main__': 141 # keep some metric 142 CHECKED = set() 143 FAILED = [] 144 PARSER = argparse.ArgumentParser() 145 PARSER.add_argument( 146 '--mode', default='ci', choices=['ci', 'pr'], 147 help='Which type of projects to clear') 148 ARGS = PARSER.parse_args() 149 main(ARGS.mode)