text: import sys
  from logging import getLogger
  from pathlib import Path
  
  import docker
  import requests
  from easy_infra import constants
  from jinja2 import Environment, FileSystemLoader
  from yaml import YAMLError, dump, safe_load
  
  LOG = getLogger(__name__)
  CLIENT = docker.from_env()
  
  
  # Helper functions
  def render_jinja2(*, template_file: Path, config: dict, output_file: Path) -> None:
      """Render the functions file"""
      folder = str(template_file.parent)
      file = str(template_file.name)
      LOG.info("Rendering %s...", file)
      template = Environment(loader=FileSystemLoader(folder)).get_template(file)
      out = template.render(config)
      output_file.write_text(out)
      output_file.chmod(0o755)
  
  
  def parse_config(*, config_file: Path) -> dict:
      """Parse the easy_infra config file"""
      # Filter
      suffix_whitelist = {".yml", ".yaml"}
  
      if config_file.suffix not in suffix_whitelist:
          LOG.error("Suffix for the config file %s is not allowed", config_file)
          raise RuntimeError
  
      try:
          with open(config_file) as yaml_data:
              config = safe_load(yaml_data)
      except (
          YAMLError,
          FileNotFoundError,
          PermissionError,
          IsADirectoryError,
          OSError,
      ) as err:
          LOG.error(
              "The config file %s was unable to be loaded due to the following exception: %s",
              config_file,
              str(err),
          )
          # Raise if info or debug level logging
          if LOG.getEffectiveLevel() <= 20:
              raise err
          sys.exit(1)
  
      return config
  
  
  def write_config(*, config: dict):
      """Write the easy_infra config file"""
      with open(constants.CONFIG_FILE, "w") as file:
          dump(config, file)
  
  
  def get_latest_release_from_apt(*, package: str) -> str:
      """Get the latest release of a project via apt"""
      # latest-az is used because it has the Microsoft repo added
      image = constants.IMAGE + ":latest-az"
      CLIENT.images.pull(repository=image)
      release = CLIENT.containers.run(
          image=image,
          auto_remove=True,
          detach=False,
          command='/bin/bash -c "apt-get update &>/dev/null && apt-cache policy '
          + package
          + " | grep '^  Candidate:' | awk -F' ' '{print $NF}'\"",
      )
      return release
  
  
  def get_latest_release_from_github(*, repo: str) -> str:
      """Get the latest release of a repo on github"""
      response = requests.get(
          f"https://api.github.com/repos/{repo}/releases/latest"
      ).json()
      return response["tag_name"]
  
  
  def get_latest_tag_from_github(*, repo: str) -> str:
      """Get the latest tag of a repo on github"""
      response = requests.get(f"https://api.github.com/repos/{repo}/tags").json()
      return response[0]["name"]
  
  
  def get_latest_release_from_pypi(*, package: str) -> str:
      """Get the latest release of a package on pypi"""
      response = requests.get(f"https://pypi.org/pypi/{package}/json").json()
      return response["info"]["version"]
  
  
  def get_latest_release_from_hashicorp(*, project: str) -> str:
      """Get the latest release of a project from hashicorp"""
      response = requests.get(
          f"https://checkpoint-api.hashicorp.com/v1/check/{project}"
      ).json()
      return response["current_version"]
  
  
  def update_config_file(*, thing: str, version: str):
      """Update the easy_infra config file"""
      # Normalize
      thing = thing.split("/")[-1].lower()
      if isinstance(version, bytes):
          version = version.decode("utf-8").rstrip()
  
      config = parse_config(config_file=constants.CONFIG_FILE)
      config["commands"][thing]["version"] = version
      write_config(config=config)
  
  
  def opinionated_docker_run(
      *,
      command: str,
      image: str,
      auto_remove: bool = False,
      tty: bool = False,
      detach: bool = True,
      environment: dict = {},
      volumes: dict = {},
      working_dir: str = "/iac/",
      expected_exit: int = 0,
  ):
      """Perform an opinionated docker run"""
      LOG.debug(
          "Invoking CLIENT.containers.run() with the following arguments: "
          + "auto_remove=%s, command=%s, detach=%s, environment=%s, image=%s, tty=%s, volumes=%s, working_dir=%s",
          auto_remove,
          command,
          detach,
          environment,
          image,
          tty,
          volumes,
          working_dir,
      )
      container = CLIENT.containers.run(
          auto_remove=auto_remove,
          command=command,
          detach=detach,
          environment=environment,
          image=image,
          tty=tty,
          volumes=volumes,
          working_dir=working_dir,
      )
  
      if not auto_remove:
          response = container.wait(condition="not-running")
          response["logs"] = container.logs().decode("utf-8").strip().replace("\n", "  ")
          container.remove()
          if not is_status_expected(expected=expected_exit, response=response):
              sys.exit(response["StatusCode"])
  
  
  def is_status_expected(*, expected: int, response: dict) -> bool:
      """Check to see if the status code was expected"""
      actual = response["StatusCode"]
  
      if expected != actual:
          LOG.error(
              "Received an unexpected status code of %s; additional details: %s",
              response["StatusCode"],
              response["logs"],
          )
          return False
  
      return True
  