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()