mirror of https://github.com/apache/kafka.git
149 lines
3.9 KiB
Python
149 lines
3.9 KiB
Python
#
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership.
|
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
|
# (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
"""
|
|
Auxiliary functions to manage the release script runtime
|
|
and launch external utilities.
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
import templates
|
|
|
|
|
|
this_script_dir = os.path.abspath(os.path.dirname(__file__))
|
|
repo_dir = os.environ.get("KAFKA_HOME", os.path.abspath(this_script_dir + "/.."))
|
|
|
|
fail_hooks = []
|
|
failing = False
|
|
|
|
|
|
def append_fail_hook(name, hook_fn):
|
|
"""
|
|
Register a fail hook function, to run in case fail() is called.
|
|
"""
|
|
fail_hooks.append((name, hook_fn))
|
|
|
|
|
|
def fail(msg = ""):
|
|
"""
|
|
Terminate execution with the given message,
|
|
after running any registered hooks.
|
|
"""
|
|
global failing
|
|
if failing:
|
|
raise Exception(f"Recursive fail invocation")
|
|
failing = True
|
|
|
|
for name, func in fail_hooks:
|
|
try:
|
|
func()
|
|
except Exception as e:
|
|
print(f"Exception caught in fail hook {name}: {e}")
|
|
|
|
print(f"FAILURE: {msg}")
|
|
sys.exit(1)
|
|
|
|
|
|
def prompt(msg):
|
|
"""
|
|
Prompt user for input with the given message.
|
|
This removes leading and trailing spaces.
|
|
"""
|
|
text = input(msg)
|
|
return text.strip()
|
|
|
|
|
|
def confirm(msg):
|
|
"""
|
|
Prompt the user to confirm
|
|
"""
|
|
while True:
|
|
text = prompt(msg + " (y/n): ").lower()
|
|
if text in ['y', 'n']:
|
|
return text == 'y'
|
|
|
|
|
|
def confirm_or_fail(msg):
|
|
"""
|
|
Prompt the user to confirm and fail on negative input.
|
|
"""
|
|
if not confirm(msg):
|
|
fail("Ok, giving up")
|
|
|
|
|
|
def execute(cmd, *args, **kwargs):
|
|
"""
|
|
Execute an external command and return its output.
|
|
"""
|
|
if "shell" not in kwargs and isinstance(cmd, str):
|
|
cmd = cmd.split()
|
|
if "input" in kwargs and isinstance(kwargs["input"], str):
|
|
kwargs["input"] = kwargs["input"].encode()
|
|
kwargs["stderr"] = stderr=subprocess.STDOUT
|
|
output = subprocess.check_output(cmd, *args, **kwargs)
|
|
return output.decode("utf-8")
|
|
|
|
|
|
def _prefix(prefix_str, value_str):
|
|
return prefix_str + value_str.replace("\n", "\n" + prefix_str)
|
|
|
|
|
|
def cmd(action, cmd_arg, *args, **kwargs):
|
|
"""
|
|
Execute an external command. This should be preferered over execute()
|
|
when returning the output is not necessary, as the user will be given
|
|
the option of retrying in case of a failure.
|
|
"""
|
|
stdin_log = ""
|
|
if "stdin" in kwargs and isinstance(kwargs["stdin"], str):
|
|
stdin_str = kwargs["stdin"]
|
|
stdin_log = "\n" + _prefix("< ", stdin_str)
|
|
stdin = tempfile.TemporaryFile()
|
|
stdin.write(stdin_str.encode("utf-8"))
|
|
stdin.seek(0)
|
|
kwargs["stdin"] = stdin
|
|
|
|
print(f"{action}\n$ {cmd_arg}{stdin_log}")
|
|
|
|
if isinstance(cmd_arg, str) and not kwargs.get("shell", False):
|
|
cmd_arg = cmd_arg.split()
|
|
|
|
allow_failure = kwargs.pop("allow_failure", False)
|
|
|
|
retry = True
|
|
while retry:
|
|
try:
|
|
output = execute(cmd_arg, *args, stderr=subprocess.STDOUT, **kwargs)
|
|
print(_prefix("> ", output.strip()))
|
|
return True
|
|
except subprocess.CalledProcessError as e:
|
|
print(e.output.decode("utf-8"))
|
|
|
|
if allow_failure:
|
|
return False
|
|
|
|
retry = confirm("Retry?")
|
|
|
|
print(templates.cmd_failed())
|
|
fail("")
|
|
|
|
|