github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/ci/ansible/tasks/templates/usr/bin/goswitch.template (about) 1 #!/usr/bin/env python3 2 3 import argparse 4 import json 5 import os 6 import subprocess 7 import sys 8 9 from pathlib import Path 10 11 GOLANG_VERSIONS_FILE = "{{ golang_versions_file }}" 12 GO_DIR_PARENT_PATH = "{{ go_dir_parent_path }}" 13 GO_DIR_NAME = "{{ go_dir_name }}" 14 GO_DIR_PATH = os.path.join(GO_DIR_PARENT_PATH, GO_DIR_NAME) 15 SOURCE_ROOT = "{{ source_root }}" 16 REPO_CLONE_PARENT_DIR = os.path.join( 17 SOURCE_ROOT, "src", "github.com", "swiftstack" 18 ) 19 PROXYFS_SRC_DIR = os.path.join(REPO_CLONE_PARENT_DIR, "ProxyFS") 20 21 22 class GoDirNotLinkError(Exception): 23 pass 24 25 26 def print_error_and_fail(msg): 27 print(msg) 28 sys.exit(1) 29 30 31 def parse_args(golang_versions): 32 parser = argparse.ArgumentParser( 33 description="Switch the enabled version of golang in the system" 34 ) 35 parser.add_argument( 36 "-b", 37 "--build-proxyfs", 38 action="store_true", 39 default=False, 40 help="Build ProxyFS with the new golang version", 41 ) 42 parser.add_argument( 43 "-l", 44 "--list-versions", 45 action="store_true", 46 default=False, 47 help='Show the "well-known" available versions', 48 ) 49 parser.add_argument( 50 "-v", 51 "--version", 52 type=str, 53 help=f"Version of golang to use. It can be either a path to an " 54 f"arbitrary directory containing the version of golang to use or " 55 f"one of the well know versions provided by Runway: " 56 f"{', '.join(get_sorted_version_names(golang_versions))}.", 57 ) 58 59 args = parser.parse_args() 60 return args 61 62 63 def read_golang_versions_file(): 64 try: 65 with open(GOLANG_VERSIONS_FILE, "r") as f: 66 try: 67 golang_versions = json.load(f) 68 except json.decoder.JSONDecodeError: 69 print_error_and_fail( 70 f"Error parsing golang versions file at " 71 f"{GOLANG_VERSIONS_FILE}" 72 ) 73 except IOError: 74 print_error_and_fail( 75 f"Could not read golang versions file at {GOLANG_VERSIONS_FILE}" 76 ) 77 78 return golang_versions 79 80 81 def get_sorted_version_names(golang_versions): 82 return [ 83 name 84 for name, version in sorted( 85 golang_versions.items(), key=lambda kv: kv[1] 86 ) 87 ] 88 89 90 def list_versions(golang_versions, current_version_path): 91 version_name_title = "Name" 92 version_title = "Version" 93 version_path_title = "Path" 94 max_name_len = max( 95 map(len, list(golang_versions.keys()) + [version_name_title]) 96 ) 97 max_version_len = max( 98 map(len, list(golang_versions.values()) + [version_title]) 99 ) 100 sorted_version_names = get_sorted_version_names(golang_versions) 101 print("\nAvailable golang versions:\n") 102 print( 103 f" {version_name_title:<{max_name_len}}" 104 f" {version_title:<{max_version_len}}" 105 f" {version_path_title}\n" 106 ) 107 for name in sorted_version_names: 108 version_path = os.path.join(GO_DIR_PARENT_PATH, golang_versions[name]) 109 row = ( 110 f" {name:<{max_name_len}}" 111 f" {golang_versions[name]:<{max_version_len}}" 112 f" {version_path}" 113 ) 114 if current_version_path == version_path: 115 row = f"{row} (active)" 116 print(row) 117 print("") 118 119 120 def get_desired_version_path(golang_versions, version): 121 if version in golang_versions: 122 return os.path.normpath( 123 os.path.join(GO_DIR_PARENT_PATH, golang_versions[version]) 124 ) 125 126 if os.path.isdir(version): 127 return os.path.normpath(version) 128 else: 129 return None 130 131 132 def get_current_version_path(): 133 if not os.path.exists(GO_DIR_PATH): 134 return None 135 elif not os.path.islink(GO_DIR_PATH): 136 raise GoDirNotLinkError( 137 f"{GO_DIR_PATH} already exists but it's not a link" 138 ) 139 return os.path.normpath(Path(GO_DIR_PATH).resolve()) 140 141 142 def switch_versions(current_version_path, desired_version_path): 143 if current_version_path is not None: 144 os.remove(GO_DIR_PATH) 145 os.symlink(desired_version_path, GO_DIR_PATH, target_is_directory=True) 146 print("Golang version successfully switched.") 147 148 149 def print_remaining_process_output(p): 150 remaining_output = p.stdout.read().decode("utf-8").strip() 151 if remaining_output != "": 152 print(p.stdout.read().decode("utf-8")) 153 154 155 def run_command(cmd, cwd=None): 156 print(cmd) 157 p = None 158 try: 159 p = subprocess.Popen( 160 cmd, 161 stdout=subprocess.PIPE, 162 stderr=subprocess.STDOUT, 163 cwd=cwd, 164 bufsize=1, 165 shell=True, 166 ) 167 while p.poll() is None: 168 # This blocks until it receives a newline: 169 line = p.stdout.readline().decode("utf-8") 170 print(line.rstrip()) 171 print_remaining_process_output(p) 172 exit_code = p.wait() 173 if exit_code != 0: 174 if p: 175 print_remaining_process_output(p) 176 print_error_and_fail( 177 f"Command '{cmd}' responded with a non-zero exit status " 178 f"({exit_code}).\nAn error for this command might have been " 179 f"printed above these lines. Please read the output in order " 180 f"to check what went wrong." 181 ) 182 except subprocess.CalledProcessError as e: 183 if p: 184 print_remaining_process_output(p) 185 print_error_and_fail(f"Error running '{cmd}':\n{e.output}\n{str(e)}") 186 except Exception as e: 187 if p: 188 print_remaining_process_output(p) 189 print_error_and_fail(f"Error running '{cmd}':\n{str(e)}") 190 191 192 def build_proxyfs(): 193 print("Stopping services and unmounting mount points...") 194 run_command("/usr/bin/unmount_and_stop_pfs") 195 196 print("Building ProxyFS...") 197 run_command( 198 "make version fmt pre-generate generate install test", PROXYFS_SRC_DIR 199 ) 200 201 print("Starting services and mounting mount points...") 202 run_command("/usr/bin/start_and_mount_pfs") 203 204 print( 205 "\nCongrats! After re-building ProxyFS and running the go tests, " 206 "everything still looks ok! But don't trust this humble script and " 207 "test thoroughly!\n" 208 ) 209 210 211 if __name__ == "__main__": 212 golang_versions = read_golang_versions_file() 213 214 options = parse_args(golang_versions) 215 if not options.version and not options.list_versions: 216 print_error_and_fail("Version not specified") 217 218 try: 219 current_version_path = get_current_version_path() 220 except GoDirNotLinkError as e: 221 print_error_and_fail(e) 222 223 if options.list_versions: 224 list_versions(golang_versions, current_version_path) 225 sys.exit(0) 226 227 desired_version_path = get_desired_version_path( 228 golang_versions, options.version 229 ) 230 if not desired_version_path: 231 print_error_and_fail( 232 f"'{options.version}' is not a well-known version name nor a " 233 f"valid path" 234 ) 235 236 if current_version_path == desired_version_path: 237 msg = ( 238 f"You are already using version '{options.version}'. " 239 f"No changes needed." 240 ) 241 if options.build_proxyfs: 242 msg = f"{msg} ProxyFS wasn't re-built." 243 print(msg) 244 sys.exit(0) 245 246 switch_versions(current_version_path, desired_version_path) 247 248 if options.build_proxyfs: 249 build_proxyfs()