Skip to content

Commit

Permalink
Add support for custom SSH port
Browse files Browse the repository at this point in the history
  • Loading branch information
dimikot committed Mar 3, 2024
1 parent 053e59b commit b9a87bd
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- "v*"

jobs:
# Tests ci-storage tool itself.
# Tests ci-storage tool and action itself.
ci-storage-tool-test:
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ ones, so rsync can run efficiently.
# Required.
action: ''

# Storage host in the format [user@]host; it must have password-free
# SSH key access.
# Storage host in the format [user@]host[:port]; it must allow password-free
# SSH key based access.
# Default: the content of ~/ci-storage-host file.
storage-host: ''

Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ inputs:
description: "What to do (store or load)."
required: true
storage-host:
description: "Storage host in the format [user@]host; it must have password-free SSH key access. If not passed, tries to read it from ~/ci-storage-host file."
description: "Storage host in the format [user@]host[:port]; it must allow password-free SSH key based access. If not passed, tries to read it from ~/ci-storage-host file."
required: false
storage-dir:
description: "Storage directory on the remote host. If not set, uses ~/ci-storage/{owner}/{repo}"
Expand Down
59 changes: 43 additions & 16 deletions ci-storage
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def main():
"--storage-host",
type=str,
required=False,
help="storage host in the format [user@]host; it must have password-free SSH key access; if omitted, uses the local filesystem (no SSH)",
help="storage host in the format [user@]host[:port]; it must allow password-free SSH key based access; if omitted, uses the local filesystem (no SSH)",
)
parser.add_argument(
"--storage-dir",
Expand Down Expand Up @@ -168,9 +168,11 @@ def action_store(
slot_ids_and_ages = list_slots(storage_host=storage_host, storage_dir=storage_dir)
slot_id_recent = slot_ids_and_ages[0][0] if len(slot_ids_and_ages) else None
slot_id_tmp = f"{slot_id}.tmp.{int(time.time())}"
host, port = parse_host_port(storage_host)
check_call(
cmd=[
"rsync",
*(["-e", shlex.join(build_ssh_cmd(port=port))] if storage_host else []),
"-a",
"--delete",
"--partial",
Expand All @@ -180,8 +182,7 @@ def action_store(
*[f"--exclude={pattern}" for pattern in exclude],
*(["-v"] if verbose else []),
f"{local_dir}/",
(f"{storage_host}:" if storage_host else "")
+ f"{storage_dir}/{slot_id_tmp}/",
(f"{host}:" if storage_host else "") + f"{storage_dir}/{slot_id_tmp}/",
],
print_elapsed=True,
)
Expand Down Expand Up @@ -223,17 +224,19 @@ def action_load(
slot_id = slot_ids_and_ages[0][0]
else:
slot_id = normalize_slot_id(slot_id)
host, port = parse_host_port(storage_host)
check_call(
cmd=[
"rsync",
*(["-e", shlex.join(build_ssh_cmd(port=port))] if storage_host else []),
"-a",
"--delete",
"--partial",
"--stats",
"--human-readable",
*[f"--exclude={pattern}" for pattern in exclude],
*(["-v"] if verbose else []),
(f"{storage_host}:" if storage_host else "") + f"{storage_dir}/{slot_id}/",
(f"{host}:" if storage_host else "") + f"{storage_dir}/{slot_id}/",
f"{local_dir}/",
],
print_elapsed=True,
Expand Down Expand Up @@ -311,18 +314,11 @@ def check_output(
cmd: list[str],
indent: bool = False,
) -> str:
if host is not None:
cmd_prefix = [
"ssh",
host,
]
print(f"$ {cmd_to_debug_str([*cmd_prefix, *cmd])}")
cmd = [
*cmd_prefix,
"-oStrictHostKeyChecking=no",
"-oUserKnownHostsFile=/dev/null",
shlex.join(cmd),
]
host, port = parse_host_port(host)
if host:
ssh_prefix = [*build_ssh_cmd(port=port), host]
print(f"$ {cmd_to_debug_str([*ssh_prefix, *cmd])}")
cmd = [*ssh_prefix, shlex.join(cmd)]
else:
print(f"$ {cmd_to_debug_str(cmd)}")
output = subprocess.check_output(cmd, text=True, stderr=subprocess.PIPE)
Expand Down Expand Up @@ -362,6 +358,37 @@ def cmd_to_debug_str(
return shlex.join([f"<{inv[arg]}>" if arg in inv else arg for arg in cmd])


#
# Parses host:port pair (with port being optional).
#
def parse_host_port(
host_port: str | None,
) -> tuple[str | None, int | None]:
if not host_port:
return None, None
match = re.match(r"^(.*?)(?::(\d+))?$", host_port)
if match and match.group(2):
return match.group(1), int(match.group(2))
else:
return host_port, None


#
# Builds ssh command line.
#
def build_ssh_cmd(
*,
port: int | None,
) -> list[str]:
return [
"ssh",
*([f"-p{port}"] if port else []),
"-oStrictHostKeyChecking=no",
"-oUserKnownHostsFile=/dev/null",
"-oLogLevel=error",
]


#
# A helper class for ArgumentParser.
#
Expand Down
2 changes: 1 addition & 1 deletion docker/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ services:
- GH_REPOSITORY=${GH_REPOSITORY:-dimikot/ci-storage}
- GH_LABELS=${GH_LABELS:-ci-storage}
- GH_TOKEN
- CI_STORAGE_HOST=${CI_STORAGE_HOST:-host}
- CI_STORAGE_HOST=${CI_STORAGE_HOST:-host:22}
- CI_STORAGE_HOST_PRIVATE_KEY=${CI_STORAGE_HOST_PRIVATE_KEY_TEST_ONLY?}
6 changes: 2 additions & 4 deletions docker/self-hosted-runner/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ if [[ "${GH_TOKEN:=}" == "" ]]; then
echo "GH_TOKEN must be set.";
exit 1;
fi
if [[ "${CI_STORAGE_HOST:=}" != "" && ! "$CI_STORAGE_HOST" =~ ^([-.[:alnum:]]+@)?[-.[:alnum:]]+$ ]]; then
echo "If CI_STORAGE_HOST is passed, it must be in form of {hostname} or {user}@{hostname}.";
if [[ "${CI_STORAGE_HOST:=}" != "" && ! "$CI_STORAGE_HOST" =~ ^([-.[:alnum:]]+@)?[-.[:alnum:]]+(:[0-9]+)?$ ]]; then
echo "If CI_STORAGE_HOST is passed, it must be in form of [user@]host[:port].";
exit 1;
fi
if [[ "${CI_STORAGE_HOST_PRIVATE_KEY:=}" != "" && "$CI_STORAGE_HOST_PRIVATE_KEY" != *OPENSSH\ PRIVATE\ KEY* ]]; then
Expand All @@ -57,8 +57,6 @@ name="ci-storage-$(hostname)"
local_dir=_work/${GH_REPOSITORY##*/}/${GH_REPOSITORY##*/}

if [[ "$CI_STORAGE_HOST" != "" && "$CI_STORAGE_HOST_PRIVATE_KEY" != "" ]]; then
ssh-keyscan -H "$CI_STORAGE_HOST" >> ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
mkdir -p "$local_dir"
ci-storage load \
--storage-host="$CI_STORAGE_HOST" \
Expand Down

0 comments on commit b9a87bd

Please sign in to comment.