Beispiel #1
0
def _get_binary_url_for(system, release: str = "latest") -> Tuple[str, str]:
    import requests
    from splitgraph.cloud import get_headers

    if release == "latest":
        endpoint = "https://api.github.com/repos/splitgraph/splitgraph/releases/%s" % release
    else:
        endpoint = "https://api.github.com/repos/splitgraph/splitgraph/releases/tags/%s" % release
    headers = get_headers()
    headers.update({"Accept": "application/vnd.github.v3+json"})
    response = requests.get(endpoint, headers=get_headers())
    if response.status_code == 404:
        raise ValueError("No releases found for tag %s, system %s!" %
                         (release, system))
    response.raise_for_status()

    body = response.json()
    actual_version = body["tag_name"].lstrip("v")
    executable = "sgr-%s-x86_64" % system
    if system == "windows":
        executable += ".exe"
    asset = [a for a in body["assets"] if a["name"] == executable]
    if not asset:
        raise ValueError("No releases found for tag %s, system %s!" %
                         (release, system))
    return actual_version, asset[0]["browser_download_url"]
Beispiel #2
0
def curl_c(remote, request_type, image, request_params, curl_args):
    """
    Query a Splitgraph REST API.

    This is a thin wrapper around curl that performs an HTTP request to Splitgraph Cloud to
    interact with a dataset using PostgREST (http://postgrest.org) or the Splitfile execution service.

    The actual invocation is:

    ```
    curl [API endpoint][request] -H [access_token] [extra curl args].
    ```

    The image must be of the form `namespace/repository:[hash_or_tag (default latest)]`.

    The actual request parameters depend on the request type:

      * For PostgREST: `/table?[postgrest request]` or empty to get the OpenAPI spec for this image.
        For a reference on how to perform Postgrest requests, see http://postgrest.org/en/latest/api.html.
      * For the Splitfile executor: a JSON array to be POSTed to the executor, e.g.
        `'{"command": "FROM some/repo IMPORT some_table AS alias", "tag": "new_tag"}'`.

    `--curl-args` allows to pass extra arguments to curl. Note that every argument must be prefixed
    with `--curl-args`, e.g. `--curl-args --cacert --curl-args /path/to/ca.pem`.
    """
    from splitgraph.config import CONFIG
    from splitgraph.cloud import AuthAPIClient, get_headers

    repository, hash_or_tag = image

    # Craft a request
    config = CONFIG["remotes"][remote]
    access_token = AuthAPIClient(remote).access_token
    headers = get_headers()
    headers.update({"Authorization": "Bearer " + access_token})

    if request_type == "postgrest":
        if request_params and not request_params.startswith("/"):
            request_params = "/" + request_params
        full_request = (config["SG_QUERY_API"] + "/%s/%s" %
                        (str(repository), str(hash_or_tag)) + "/-/rest" +
                        request_params)
    else:
        full_request = (config["SG_QUERY_API"] + "/%s/%s" %
                        (str(repository), str(hash_or_tag)) + "/-/splitfile")
        curl_args = ["-X", "POST", "-d", request_params] + list(curl_args)
        headers.update({"Content-Type": "application/json"})

    header_invocation = [
        h for i in headers.items() for h in ("-H", "%s: %s" % i)
    ]
    subprocess_args = ["curl", full_request
                       ] + header_invocation + list(curl_args)

    logging.debug("Calling %s", " ".join(subprocess_args))
    subprocess.call(subprocess_args)
Beispiel #3
0
def upgrade_c(skip_engine_upgrade, path, force, version):
    """Upgrade sgr client and engine.

    This will try to download the most recent stable binary for the current platform
    into the location this binary is running from and then upgrade the default engine.

    This method is only supported for single-binary installs and engines managed
    by `sgr engine`.
    """
    import requests
    from tqdm import tqdm
    from splitgraph.config import CONFIG, SG_CMD_ASCII
    from splitgraph.cloud import get_headers

    # Detect if we're running from a Pyinstaller binary
    if not hasattr(sys, "frozen") and not force and not path:
        raise click.ClickException(
            "Not running from a single binary. Use the tool "
            "you originally used to install Splitgraph (e.g. pip) "
            "to upgrade. To download and run a single binary "
            "for your platform, pass --force and --path."
        )

    system = _get_system_id()

    # Get link to the release
    actual_version, download_url = _get_binary_url_for(
        system, "v" + version if version != "latest" else version
    )

    if version == "latest":
        click.echo("Latest version is %s." % actual_version)

    if Version(actual_version) < Version(__version__) and not force:
        click.echo(
            "Newer or existing version of sgr (%s) is installed, pass --force to reinstall."
            % __version__
        )
        return

    # Download the file

    temp_path, final_path = _get_download_paths(path, download_url)

    # Delete the temporary file at exit if we crash

    def _unlink():
        try:
            temp_path.unlink()
        except FileNotFoundError:
            pass

    atexit.register(_unlink)

    click.echo("Downloading sgr %s from %s to %s" % (actual_version, download_url, temp_path))

    headers = get_headers()
    response = requests.get(download_url, headers=headers, allow_redirects=True, stream=True)
    response.raise_for_status()

    with tqdm(
        total=int(response.headers["Content-Length"]), unit="B", unit_scale=True, ascii=SG_CMD_ASCII
    ) as pbar:
        with (open(str(temp_path), "wb")) as f:
            for chunk in response.iter_content(chunk_size=1024):
                f.write(chunk)
                pbar.update(len(chunk))

    # Test the new binary
    st = os.stat(str(temp_path))
    os.chmod(str(temp_path), st.st_mode | stat.S_IEXEC)
    subprocess.check_call([str(temp_path), "--version"])

    # Upgrade the default engine
    if not skip_engine_upgrade:
        containers = list_engines(include_all=True, prefix=CONFIG["SG_ENGINE_PREFIX"])
        if containers:
            click.echo("Upgrading the default engine...")
            subprocess.check_call([str(temp_path), "engine", "upgrade"])

    def _finalize():
        # On Windows we can't replace a running executable + probably should keep a backup anyway.
        # We hence rename ourselves to a .old file and ask the user to delete it if needed.
        if final_path.exists():
            backup_path = final_path.with_suffix(final_path.suffix + ".old")
            try:
                # shutil.move() breaks on Windows otherwise.
                backup_path.unlink()
            except FileNotFoundError:
                pass
            shutil.move(str(final_path), str(backup_path))
            click.echo("Old version has been backed up to %s." % backup_path)

        shutil.move(str(temp_path), str(final_path))
        click.echo("New sgr has been installed at %s." % final_path)

    # Instead of moving the file right now, do it at exit -- that way
    # Pyinstaller won't break with a scary error when cleaning up because
    # the file it's running from has changed.
    atexit.register(_finalize)