App Engine Python SDK version 1.8.8
[gae.git] / python / google / appengine / tools / devappserver2 / go_runtime.py
blobe174b044331f64c13749ce0c49ba04be75b9d7e6
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Serves content for "script" handlers using the Go runtime."""
20 import logging
21 import os
22 import os.path
23 import sys
24 import threading
26 from google.appengine.api import appinfo
27 from google.appengine.tools.devappserver2 import application_configuration
28 from google.appengine.tools.devappserver2 import go_application
29 from google.appengine.tools.devappserver2 import http_runtime
30 from google.appengine.tools.devappserver2 import instance
32 _REBUILD_CONFIG_CHANGES = frozenset(
33 [application_configuration.SKIP_FILES_CHANGED,
34 application_configuration.NOBUILD_FILES_CHANGED])
37 class _GoBuildFailureRuntimeProxy(instance.RuntimeProxy):
38 """Serves an error page for a Go application build failure."""
40 def __init__(self, failure_exception):
41 self._failure_exception = failure_exception
43 def start(self):
44 pass
46 def quit(self):
47 pass
49 def handle(self, environ, start_response, url_map, match, request_id,
50 request_type):
51 """Serves a request by displaying an error page.
53 Args:
54 environ: An environ dict for the request as defined in PEP-333.
55 start_response: A function with semantics defined in PEP-333.
56 url_map: An appinfo.URLMap instance containing the configuration for the
57 handler matching this request.
58 match: A re.MatchObject containing the result of the matched URL pattern.
59 request_id: A unique string id associated with the request.
60 request_type: The type of the request. See instance.*_REQUEST module
61 constants.
63 Yields:
64 A sequence of strings containing the body of the HTTP response.
65 """
66 start_response('500 Internal Server Error',
67 [('Content-Type', 'text/plain; charset=utf-8')])
68 yield 'The Go application could not be built.\n'
69 yield '\n'
70 yield str(self._failure_exception)
73 class GoRuntimeInstanceFactory(instance.InstanceFactory):
74 """A factory that creates new Go runtime Instances."""
76 START_URL_MAP = appinfo.URLMap(
77 url='/_ah/start',
78 script='_go_app',
79 login='admin')
80 WARMUP_URL_MAP = appinfo.URLMap(
81 url='/_ah/warmup',
82 script='_go_app',
83 login='admin')
84 FILE_CHANGE_INSTANCE_RESTART_POLICY = instance.ALWAYS
86 def __init__(self, request_data, runtime_config_getter, module_configuration):
87 """Initializer for GoRuntimeInstanceFactory.
89 Args:
90 request_data: A wsgi_request_info.WSGIRequestInfo that will be provided
91 with request information for use by API stubs.
92 runtime_config_getter: A function that can be called without arguments
93 and returns the runtime_config_pb2.RuntimeConfig containing the
94 configuration for the runtime.
95 module_configuration: An application_configuration.ModuleConfiguration
96 instance respresenting the configuration of the module that owns the
97 runtime.
98 """
99 super(GoRuntimeInstanceFactory, self).__init__(request_data, 1)
100 self._runtime_config_getter = runtime_config_getter
101 self._module_configuration = module_configuration
102 self._application_lock = threading.Lock()
103 self._go_application = go_application.GoApplication(
104 self._module_configuration)
105 self._modified_since_last_build = False
107 def get_restart_directories(self):
108 """Returns a list of directories changes in which should trigger a restart.
110 Returns:
111 A list of src directory paths in the GOPATH. Changes (i.e. files added,
112 deleted or modified) in these directories will trigger a restart of all
113 instances created with this factory.
115 try:
116 go_path = os.environ['GOPATH']
117 except KeyError:
118 return []
119 else:
120 if sys.platform.startswith('win32'):
121 roots = go_path.split(';')
122 else:
123 roots = go_path.split(':')
124 dirs = [os.path.join(r, 'src') for r in roots]
125 return [d for d in dirs if os.path.isdir(d)]
127 def files_changed(self):
128 """Called when a file relevant to the factory *might* have changed."""
129 with self._application_lock:
130 self._modified_since_last_build = True
132 def configuration_changed(self, config_changes):
133 """Called when the configuration of the module has changed.
135 Args:
136 config_changes: A set containing the changes that occured. See the
137 *_CHANGED constants in the application_configuration module.
139 if config_changes & _REBUILD_CONFIG_CHANGES:
140 with self._application_lock:
141 self._modified_since_last_build = True
143 def new_instance(self, instance_id, expect_ready_request=False):
144 """Create and return a new Instance.
146 Args:
147 instance_id: A string or integer representing the unique (per module) id
148 of the instance.
149 expect_ready_request: If True then the instance will be sent a special
150 request (i.e. /_ah/warmup or /_ah/start) before it can handle external
151 requests.
153 Returns:
154 The newly created instance.Instance.
157 def instance_config_getter():
158 runtime_config = self._runtime_config_getter()
159 runtime_config.instance_id = str(instance_id)
160 return runtime_config
162 with self._application_lock:
163 try:
164 self._go_application.maybe_build(self._modified_since_last_build)
165 except go_application.BuildError as e:
166 logging.error('Failed to build Go application: %s', e)
167 proxy = _GoBuildFailureRuntimeProxy(e)
168 else:
169 proxy = http_runtime.HttpRuntimeProxy(
170 self._go_application.go_executable,
171 instance_config_getter,
172 self._module_configuration,
173 self._go_application.get_environment())
174 self._modified_since_last_build = False
176 return instance.Instance(self.request_data,
177 instance_id,
178 proxy,
179 self.max_concurrent_requests,
180 self.max_background_threads,
181 expect_ready_request)