Coverage for datasette/publish/cloudrun.py : 96%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from datasette import hookimpl
2import click
3import json
4import os
5import re
6from subprocess import check_call, check_output
8from .common import (
9 add_common_publish_arguments_and_options,
10 fail_if_publish_binary_not_installed,
11)
12from ..utils import temporary_docker_directory
15@hookimpl
16def publish_subcommand(publish):
17 @publish.command()
18 @add_common_publish_arguments_and_options
19 @click.option(
20 "-n",
21 "--name",
22 default="datasette",
23 help="Application name to use when building",
24 )
25 @click.option(
26 "--service", default="", help="Cloud Run service to deploy (or over-write)"
27 )
28 @click.option("--spatialite", is_flag=True, help="Enable SpatialLite extension")
29 @click.option(
30 "--show-files",
31 is_flag=True,
32 help="Output the generated Dockerfile and metadata.json",
33 )
34 @click.option(
35 "--memory",
36 callback=_validate_memory,
37 help="Memory to allocate in Cloud Run, e.g. 1Gi",
38 )
39 def cloudrun(
40 files,
41 metadata,
42 extra_options,
43 branch,
44 template_dir,
45 plugins_dir,
46 static,
47 install,
48 plugin_secret,
49 version_note,
50 secret,
51 title,
52 license,
53 license_url,
54 source,
55 source_url,
56 about,
57 about_url,
58 name,
59 service,
60 spatialite,
61 show_files,
62 memory,
63 ):
64 fail_if_publish_binary_not_installed(
65 "gcloud", "Google Cloud", "https://cloud.google.com/sdk/"
66 )
67 project = check_output(
68 "gcloud config get-value project", shell=True, universal_newlines=True
69 ).strip()
71 if not service:
72 # Show the user their current services, then prompt for one
73 click.echo("Please provide a service name for this deployment\n")
74 click.echo("Using an existing service name will over-write it")
75 click.echo("")
76 existing_services = get_existing_services()
77 if existing_services:
78 click.echo("Your existing services:\n")
79 for existing_service in existing_services:
80 click.echo(
81 " {name} - created {created} - {url}".format(
82 **existing_service
83 )
84 )
85 click.echo("")
86 service = click.prompt("Service name", type=str)
88 extra_metadata = {
89 "title": title,
90 "license": license,
91 "license_url": license_url,
92 "source": source,
93 "source_url": source_url,
94 "about": about,
95 "about_url": about_url,
96 }
98 environment_variables = {}
99 if plugin_secret:
100 extra_metadata["plugins"] = {}
101 for plugin_name, plugin_setting, setting_value in plugin_secret:
102 environment_variable = (
103 "{}_{}".format(plugin_name, plugin_setting)
104 .upper()
105 .replace("-", "_")
106 )
107 environment_variables[environment_variable] = setting_value
108 extra_metadata["plugins"].setdefault(plugin_name, {})[
109 plugin_setting
110 ] = {"$env": environment_variable}
112 with temporary_docker_directory(
113 files,
114 name,
115 metadata,
116 extra_options,
117 branch,
118 template_dir,
119 plugins_dir,
120 static,
121 install,
122 spatialite,
123 version_note,
124 secret,
125 extra_metadata,
126 environment_variables,
127 ):
128 if show_files:
129 if os.path.exists("metadata.json"):
130 print("=== metadata.json ===\n")
131 print(open("metadata.json").read())
132 print("\n==== Dockerfile ====\n")
133 print(open("Dockerfile").read())
134 print("\n====================\n")
136 image_id = "gcr.io/{project}/{name}".format(project=project, name=name)
137 check_call("gcloud builds submit --tag {}".format(image_id), shell=True)
138 check_call(
139 "gcloud run deploy --allow-unauthenticated --platform=managed --image {} {}{}".format(
140 image_id, service, " --memory {}".format(memory) if memory else ""
141 ),
142 shell=True,
143 )
146def get_existing_services():
147 services = json.loads(
148 check_output(
149 "gcloud run services list --platform=managed --format json",
150 shell=True,
151 universal_newlines=True,
152 )
153 )
154 return [
155 {
156 "name": service["metadata"]["name"],
157 "created": service["metadata"]["creationTimestamp"],
158 "url": service["status"]["address"]["url"],
159 }
160 for service in services
161 ]
164def _validate_memory(ctx, param, value):
165 if value and re.match(r"^\d+(Gi|G|Mi|M)$", value) is None:
166 raise click.BadParameter("--memory should be a number then Gi/G/Mi/M e.g 1Gi")
167 return value