2019-05-03 13:59:01 +00:00
|
|
|
from datasette import hookimpl
|
|
|
|
import click
|
|
|
|
import json
|
2019-07-08 02:06:31 +00:00
|
|
|
import os
|
2020-03-05 23:34:21 +00:00
|
|
|
import re
|
2019-05-03 13:59:01 +00:00
|
|
|
from subprocess import check_call, check_output
|
|
|
|
|
|
|
|
from .common import (
|
|
|
|
add_common_publish_arguments_and_options,
|
|
|
|
fail_if_publish_binary_not_installed,
|
|
|
|
)
|
|
|
|
from ..utils import temporary_docker_directory
|
|
|
|
|
|
|
|
|
|
|
|
@hookimpl
|
|
|
|
def publish_subcommand(publish):
|
|
|
|
@publish.command()
|
|
|
|
@add_common_publish_arguments_and_options
|
|
|
|
@click.option(
|
|
|
|
"-n",
|
|
|
|
"--name",
|
|
|
|
default="datasette",
|
2019-05-09 16:45:01 +00:00
|
|
|
help="Application name to use when building",
|
|
|
|
)
|
|
|
|
@click.option(
|
2019-05-09 16:47:58 +00:00
|
|
|
"--service", default="", help="Cloud Run service to deploy (or over-write)"
|
2019-05-03 13:59:01 +00:00
|
|
|
)
|
|
|
|
@click.option("--spatialite", is_flag=True, help="Enable SpatialLite extension")
|
2019-07-08 02:06:31 +00:00
|
|
|
@click.option(
|
|
|
|
"--show-files",
|
|
|
|
is_flag=True,
|
|
|
|
help="Output the generated Dockerfile and metadata.json",
|
|
|
|
)
|
2020-03-05 23:34:21 +00:00
|
|
|
@click.option(
|
|
|
|
"--memory",
|
|
|
|
callback=_validate_memory,
|
|
|
|
help="Memory to allocate in Cloud Run, e.g. 1Gi",
|
|
|
|
)
|
2021-08-04 05:20:50 +00:00
|
|
|
@click.option(
|
|
|
|
"--cpu",
|
|
|
|
type=click.Choice(["1", "2", "4"]),
|
|
|
|
help="Number of vCPUs to allocate in Cloud Run",
|
|
|
|
)
|
2022-04-24 14:03:08 +00:00
|
|
|
@click.option(
|
|
|
|
"--timeout",
|
2022-04-24 14:09:08 +00:00
|
|
|
type=int,
|
2022-04-24 14:03:08 +00:00
|
|
|
help="Build timeout in seconds",
|
|
|
|
)
|
2020-11-25 03:05:35 +00:00
|
|
|
@click.option(
|
|
|
|
"--apt-get-install",
|
|
|
|
"apt_get_extras",
|
|
|
|
multiple=True,
|
|
|
|
help="Additional packages to apt-get install",
|
|
|
|
)
|
2019-05-03 13:59:01 +00:00
|
|
|
def cloudrun(
|
|
|
|
files,
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
branch,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
|
|
|
install,
|
2019-07-08 02:06:31 +00:00
|
|
|
plugin_secret,
|
2019-05-03 13:59:01 +00:00
|
|
|
version_note,
|
2020-06-11 16:02:03 +00:00
|
|
|
secret,
|
2019-05-03 13:59:01 +00:00
|
|
|
title,
|
|
|
|
license,
|
|
|
|
license_url,
|
|
|
|
source,
|
|
|
|
source_url,
|
|
|
|
about,
|
|
|
|
about_url,
|
|
|
|
name,
|
2019-05-09 16:45:01 +00:00
|
|
|
service,
|
2019-05-03 13:59:01 +00:00
|
|
|
spatialite,
|
2019-07-08 02:06:31 +00:00
|
|
|
show_files,
|
2020-03-05 23:34:21 +00:00
|
|
|
memory,
|
2021-08-04 05:20:50 +00:00
|
|
|
cpu,
|
2022-04-24 14:03:08 +00:00
|
|
|
timeout,
|
2020-11-25 03:05:35 +00:00
|
|
|
apt_get_extras,
|
2019-05-03 13:59:01 +00:00
|
|
|
):
|
2022-01-14 00:12:54 +00:00
|
|
|
"Publish databases to Datasette running on Cloud Run"
|
2019-05-04 02:15:14 +00:00
|
|
|
fail_if_publish_binary_not_installed(
|
|
|
|
"gcloud", "Google Cloud", "https://cloud.google.com/sdk/"
|
|
|
|
)
|
|
|
|
project = check_output(
|
|
|
|
"gcloud config get-value project", shell=True, universal_newlines=True
|
|
|
|
).strip()
|
2019-05-03 13:59:01 +00:00
|
|
|
|
2019-11-08 02:48:39 +00:00
|
|
|
if not service:
|
|
|
|
# Show the user their current services, then prompt for one
|
|
|
|
click.echo("Please provide a service name for this deployment\n")
|
|
|
|
click.echo("Using an existing service name will over-write it")
|
|
|
|
click.echo("")
|
|
|
|
existing_services = get_existing_services()
|
|
|
|
if existing_services:
|
|
|
|
click.echo("Your existing services:\n")
|
|
|
|
for existing_service in existing_services:
|
|
|
|
click.echo(
|
|
|
|
" {name} - created {created} - {url}".format(
|
|
|
|
**existing_service
|
|
|
|
)
|
|
|
|
)
|
|
|
|
click.echo("")
|
|
|
|
service = click.prompt("Service name", type=str)
|
|
|
|
|
2019-07-08 02:06:31 +00:00
|
|
|
extra_metadata = {
|
|
|
|
"title": title,
|
|
|
|
"license": license,
|
|
|
|
"license_url": license_url,
|
|
|
|
"source": source,
|
|
|
|
"source_url": source_url,
|
|
|
|
"about": about,
|
|
|
|
"about_url": about_url,
|
|
|
|
}
|
|
|
|
|
2021-01-06 18:13:34 +00:00
|
|
|
if not extra_options:
|
|
|
|
extra_options = ""
|
|
|
|
if "force_https_urls" not in extra_options:
|
|
|
|
if extra_options:
|
|
|
|
extra_options += " "
|
|
|
|
extra_options += "--setting force_https_urls on"
|
|
|
|
|
2019-07-08 02:06:31 +00:00
|
|
|
environment_variables = {}
|
|
|
|
if plugin_secret:
|
|
|
|
extra_metadata["plugins"] = {}
|
|
|
|
for plugin_name, plugin_setting, setting_value in plugin_secret:
|
|
|
|
environment_variable = (
|
2020-11-15 23:24:22 +00:00
|
|
|
f"{plugin_name}_{plugin_setting}".upper().replace("-", "_")
|
2019-07-08 02:06:31 +00:00
|
|
|
)
|
|
|
|
environment_variables[environment_variable] = setting_value
|
|
|
|
extra_metadata["plugins"].setdefault(plugin_name, {})[
|
|
|
|
plugin_setting
|
|
|
|
] = {"$env": environment_variable}
|
|
|
|
|
2019-05-03 13:59:01 +00:00
|
|
|
with temporary_docker_directory(
|
|
|
|
files,
|
|
|
|
name,
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
branch,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
|
|
|
install,
|
|
|
|
spatialite,
|
|
|
|
version_note,
|
2020-06-11 16:02:03 +00:00
|
|
|
secret,
|
2019-07-08 02:06:31 +00:00
|
|
|
extra_metadata,
|
|
|
|
environment_variables,
|
2020-11-25 03:05:35 +00:00
|
|
|
apt_get_extras=apt_get_extras,
|
2019-05-03 13:59:01 +00:00
|
|
|
):
|
2019-07-08 02:06:31 +00:00
|
|
|
if show_files:
|
|
|
|
if os.path.exists("metadata.json"):
|
|
|
|
print("=== metadata.json ===\n")
|
2021-03-11 16:15:49 +00:00
|
|
|
with open("metadata.json") as fp:
|
|
|
|
print(fp.read())
|
2019-07-08 02:06:31 +00:00
|
|
|
print("\n==== Dockerfile ====\n")
|
2021-03-11 16:15:49 +00:00
|
|
|
with open("Dockerfile") as fp:
|
|
|
|
print(fp.read())
|
2019-07-08 02:06:31 +00:00
|
|
|
print("\n====================\n")
|
|
|
|
|
2020-11-15 23:24:22 +00:00
|
|
|
image_id = f"gcr.io/{project}/{name}"
|
2022-04-24 14:03:08 +00:00
|
|
|
check_call(
|
|
|
|
"gcloud builds submit --tag {}{}".format(
|
|
|
|
image_id, " --timeout {}".format(timeout) if timeout else ""
|
|
|
|
),
|
|
|
|
shell=True,
|
|
|
|
)
|
2019-05-04 02:15:14 +00:00
|
|
|
check_call(
|
2021-08-04 05:20:50 +00:00
|
|
|
"gcloud run deploy --allow-unauthenticated --platform=managed --image {} {}{}{}".format(
|
|
|
|
image_id,
|
|
|
|
service,
|
|
|
|
" --memory {}".format(memory) if memory else "",
|
|
|
|
" --cpu {}".format(cpu) if cpu else "",
|
2019-05-04 02:15:14 +00:00
|
|
|
),
|
|
|
|
shell=True,
|
|
|
|
)
|
2019-11-08 02:48:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_existing_services():
|
|
|
|
services = json.loads(
|
|
|
|
check_output(
|
2020-01-21 23:28:11 +00:00
|
|
|
"gcloud run services list --platform=managed --format json",
|
2019-11-08 02:48:39 +00:00
|
|
|
shell=True,
|
|
|
|
universal_newlines=True,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
"name": service["metadata"]["name"],
|
|
|
|
"created": service["metadata"]["creationTimestamp"],
|
|
|
|
"url": service["status"]["address"]["url"],
|
|
|
|
}
|
|
|
|
for service in services
|
|
|
|
]
|
2020-03-05 23:34:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _validate_memory(ctx, param, value):
|
2020-03-05 23:44:15 +00:00
|
|
|
if value and re.match(r"^\d+(Gi|G|Mi|M)$", value) is None:
|
2020-03-05 23:34:21 +00:00
|
|
|
raise click.BadParameter("--memory should be a number then Gi/G/Mi/M e.g 1Gi")
|
|
|
|
return value
|