summaryrefslogtreecommitdiff
path: root/mdbp/ssh.py
blob: 8445dc7a6ea40c66dc71d8552d87bb61c871efc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/python3
# SPDX-License-Identifier: MIT
"""mdbp backend wrapper via ssh"""

import argparse
import contextlib
import io
import json
import pathlib
import subprocess
import sys
import tarfile
import typing

from .common import JsonObject, buildjson, get_dsc_files, tar_add

def produce_request_tar(buildjsonobj: JsonObject,
                        fileobj: typing.IO[bytes]) -> None:
    """Write a tar file suitable for mdbp-streamapi into the given `fileobj`
    based on the given `buildjsonobj`.
     * An .output.directory is discarded.
     * A referenced .dsc file and its components is included.
    """
    sendjsonobj = buildjsonobj.copy()
    sendjsonobj["output"] = sendjsonobj["output"].copy()
    del sendjsonobj["output"]["directory"]
    dscpath: typing.Optional[pathlib.Path]
    try:
        dscpath = pathlib.Path(buildjsonobj["input"]["source_package_path"])
    except KeyError:
        dscpath = None
    else:
        sendjsonobj["input"] = sendjsonobj["input"].copy()
        sendjsonobj["input"]["source_package_path"] = dscpath.name
    with tarfile.open(mode="w|", fileobj=fileobj) as tar:
        info = tarfile.TarInfo("build.json")
        sendjsonfile = io.BytesIO()
        for chunk in json.JSONEncoder().iterencode(sendjsonobj):
            sendjsonfile.write(chunk.encode("utf8"))
        info.size = sendjsonfile.tell()
        sendjsonfile.seek(0)
        tar.addfile(info, sendjsonfile)
        if dscpath:
            for path in [dscpath] + get_dsc_files(dscpath):
                tar_add(tar, path)

def main() -> None:
    """Entry point for mdbp-ssh backend"""
    parser = argparse.ArgumentParser()
    parser.add_argument("host", type=str)
    parser.add_argument("command", nargs=argparse.REMAINDER)
    args = parser.parse_args()
    if len(args.command) < 2:
        parser.error("missing command or json file")
    build = buildjson(args.command.pop())

    cmd = ["ssh", args.host, "mdbp-streamapi", *args.command]
    with contextlib.ExitStack() as stack:
        proc = stack.enter_context(
            subprocess.Popen(
                cmd,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=sys.stdout if build["output"].get("log", True)
                                  else subprocess.DEVNULL
            ),
        )
        assert proc.stdin is not None
        produce_request_tar(build, proc.stdin)
        proc.stdin.close()
        exitcode = 0
        try:
            outtar = stack.enter_context(tarfile.open(fileobj=proc.stdout,
                                                      mode="r|"))
        except tarfile.ReadError as err:
            if str(err) != "empty file":
                raise
            exitcode = 1
        else:
            for member in outtar:
                if "/" in member.name or not member.isfile():
                    raise ValueError("expected flat tar as output")
                outtar.extract(member, build["output"]["directory"],
                               set_attrs=False)
        sys.exit(proc.wait() or exitcode)

if __name__ == "__main__":
    main()