def _wait_for_pg( timeout_secs: int, query: str, dbname: str, port: int, host: str, user: str, password: str, print_result: bool, expected: Union[Iterable[Any], Literal["any"]], ) -> None: """Wait for a pg-compatible database (includes materialized)""" args = f"dbname={dbname} host={host} port={port} user={user} password={password}" ui.progress(f"waiting for {args} to handle {query!r}", "C") error = None for remaining in ui.timeout_loop(timeout_secs): try: conn = pg8000.connect( database=dbname, host=host, port=port, user=user, password=password, timeout=1, ) # The default (autocommit = false) wraps everything in a transaction. conn.autocommit = True cur = conn.cursor() cur.execute(query) if expected == "any" and cur.rowcount == -1: ui.progress("success!", finish=True) return result = list(cur.fetchall()) if expected == "any" or result == expected: if print_result: say(f"query result: {result}") else: ui.progress("success!", finish=True) return else: say( f"host={host} port={port} did not return rows matching {expected} got: {result}" ) except Exception as e: ui.progress(" " + str(int(remaining))) error = e ui.progress(finish=True) raise UIError(f"never got correct result for {args}: {error}")
def run(self, comp: Composition, workflow: Workflow) -> None: pattern = f"{comp.name}_{self._container}" ui.progress(f"Ensuring {self._container} stays up ", "C") for i in range(self._uptime_secs, 0, -1): time.sleep(1) try: stdout = spawn.capture(["docker", "ps", "--format={{.Names}}"], unicode=True) except subprocess.CalledProcessError as e: raise Failed(f"{e.stdout}") found = False for line in stdout.splitlines(): if line.startswith(pattern): found = True break if not found: print(f"failed! {pattern} logs follow:") print_docker_logs(pattern, 10) raise Failed(f"container {self._container} stopped running!") ui.progress(f" {i}") print()
def wait_for_mysql(timeout_secs: int, user: str, passwd: str, host: str, port: int) -> None: args = f"mysql user={user} host={host} port={port}" ui.progress(f"waitng for {args}", "C") error = None for _ in ui.timeout_loop(timeout_secs): try: conn = pymysql.connect(user=user, passwd=passwd, host=host, port=port) with conn.cursor() as cur: cur.execute("SELECT 1") result = cur.fetchone() if result == (1, ): print(f"success!") return else: print(f"weird, {args} did not return 1: {result}") except Exception as e: ui.progress(".") error = e ui.progress(finish=True) raise Failed(f"Never got correct result for {args}: {error}")
def run(self, comp: Composition, workflow: Workflow) -> None: ui.progress( f"waiting for {self._host}:{self._port}", "C", ) for remaining in ui.timeout_loop(self._timeout_secs): cmd = f"docker run --rm -t --network {comp.name}_default ubuntu:bionic-20200403".split( ) cmd.extend([ "timeout", str(self._timeout_secs), "bash", "-c", f"cat < /dev/null > /dev/tcp/{self._host}/{self._port}", ]) try: spawn.capture(cmd, unicode=True, stderr_too=True) except subprocess.CalledProcessError as e: ui.log_in_automation( "wait-for-tcp ({}:{}): error running {}: {}, stdout:\n{}\nstderr:\n{}" .format( self._host, self._port, ui.shell_quote(cmd), e, e.stdout, e.stderr, )) ui.progress(" {}".format(int(remaining))) else: ui.progress(" success!", finish=True) return raise Failed(f"Unable to connect to {self._host}:{self._port}")
def run(self, workflow: Workflow) -> None: ui.progress(f"waiting for {self._host}:{self._port}", "C") for remaining in ui.timeout_loop(self._timeout_secs): cmd = f"docker run --rm -t --network {workflow.composition.name}_default ubuntu:bionic-20200403".split() try: executed = _check_tcp( cmd[:], self._host, self._port, self._timeout_secs ) except subprocess.CalledProcessError as e: ui.progress(" {}".format(int(remaining))) else: ui.progress(" success!", finish=True) return for dep in self._dependencies: host, port = dep["host"], dep["port"] try: _check_tcp( cmd[:], host, port, self._timeout_secs, kind="dependency " ) except subprocess.CalledProcessError as e: message = f"Dependency is down {host}:{port}" if "hint" in dep: message += f"\n hint: {dep['hint']}" raise errors.Failed(message) raise errors.Failed(f"Unable to connect to {self._host}:{self._port}")
def wait( condition: str, resource: str, timeout_secs: int = 300, context: str = "kind-kind" ) -> None: cmd = [ "kubectl", "wait", "--for", condition, resource, "--timeout", f"{timeout_secs}s", "--context", context, ] ui.progress(f'waiting for {" ".join(cmd)} ... ') error = None for remaining in ui.timeout_loop(timeout_secs, tick=0.1): try: output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode( "ascii" ) # output is: # - an empty string when a 'delete' condition is satisfied # - 'condition met' for all other conditions if len(output) == 0 or "condition met" in output: ui.progress("success!", finish=True) return except subprocess.CalledProcessError as e: print(e, e.output) error = e ui.progress(finish=True) raise UIError(f"kubectl wait never returned 'condition met': {error}")
def wait_for_pg( timeout_secs: int, query: str, dbname: str, port: int, host: str, print_result: bool, expected: Union[Iterable[Any], Literal["any"]], ) -> None: """Wait for a pg-compatible database (includes materialized) """ args = f"dbname={dbname} host={host} port={port} user=ignored" ui.progress(f"waiting for {args} to handle {query!r}", "C") error = None if isinstance(expected, tuple): expected = list(expected) for remaining in ui.timeout_loop(timeout_secs): try: conn = pg8000.connect(database=dbname, host=host, port=port, user="******", timeout=1) cur = conn.cursor() cur.execute(query) result = cur.fetchall() found_result = False for row in result: if expected == "any" or list(row) == expected: if not found_result: found_result = True ui.progress(" up and responding!", finish=True) if print_result: say("query result:") if print_result: print(" ".join([str(r) for r in row])) if found_result: return else: say(f"host={host} port={port} did not return any row matching {expected} got: {result}" ) except Exception as e: ui.progress(" " + str(int(remaining))) error = e ui.progress(finish=True) raise Failed(f"never got correct result for {args}: {error}")
def wait_for_tcp( self, *, host: str = "localhost", port: int, timeout_secs: int = 240, ) -> None: ui.progress(f"waiting for {host}:{port}", "C") for remaining in ui.timeout_loop(timeout_secs): cmd = f"docker run --rm -t --network {self.name}_default ubuntu:focal-20210723".split() try: _check_tcp(cmd[:], host, port, timeout_secs) except subprocess.CalledProcessError: ui.progress(" {}".format(int(remaining))) else: ui.progress(" success!", finish=True) return ui.progress(" error!", finish=True) raise UIError(f"unable to connect to {host}:{port}")
def wait_for_tcp( self, *, host: str = "localhost", port: Union[int, str], timeout_secs: int = 240, ) -> None: if isinstance(port, str): port = int(port.split(":")[0]) ui.progress(f"waiting for {host}:{port}", "C") cmd = f"docker run --rm -t --network {self.name}_default ubuntu:focal-20210723".split() try: _check_tcp(cmd[:], host, port, timeout_secs) except subprocess.CalledProcessError: ui.progress(" error!", finish=True) raise UIError(f"unable to connect to {host}:{port}") else: ui.progress(" success!", finish=True)
def run(self, comp: Composition) -> None: ui.progress( f"waiting for {self._host}:{self._port}", "C", ) for remaining in ui.timeout_loop(self._timeout_secs): cmd = f"docker run --rm -it --network {comp.name}_default ubuntu:bionic-20200403".split( ) cmd.extend([ "timeout", str(self._timeout_secs), "bash", "-c", f"cat < /dev/null > /dev/tcp/{self._host}/{self._port}", ]) try: spawn.capture(cmd, unicode=True, stderr_too=True) except subprocess.CalledProcessError: ui.progress(" {}".format(int(remaining))) else: ui.progress(" success!", finish=True) return raise Failed(f"Unable to connect to {self._host}:{self._port}")
def run_for_duration(self, container_id: str) -> None: ui.progress(f"running for {self._duration} ", "C") for _ in ui.timeout_loop(self._duration): self.stop_and_start(container_id) ui.progress(finish=True)