App Engine Python SDK version 1.7.7
[gae.git] / python / google / appengine / tools / devappserver2 / go_runtime.py
bloba50ad99ec294c8e41f4008998545bb52f156fd3c
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 """Servers 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')])
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, server_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 server_configuration: An application_configuration.ServerConfiguration
96 instance respresenting the configuration of the server that owns the
97 runtime.
98 """
99 super(GoRuntimeInstanceFactory, self).__init__(request_data, 1)
100 self._runtime_config_getter = runtime_config_getter
101 self._server_configuration = server_configuration
102 self._application_lock = threading.Lock()
103 self._go_application = go_application.GoApplication(
104 self._server_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 return [os.path.join(r, 'src') for r in roots]
126 def files_changed(self):
127 """Called when a file relevant to the factory *might* have changed."""
128 with self._application_lock:
129 self._modified_since_last_build = True
131 def configuration_changed(self, config_changes):
132 """Called when the configuration of the server has changed.
134 Args:
135 config_changes: A set containing the changes that occured. See the
136 *_CHANGED constants in the application_configuration module.
138 if config_changes & _REBUILD_CONFIG_CHANGES:
139 with self._application_lock:
140 self._modified_since_last_build = True
142 def new_instance(self, instance_id, expect_ready_request=False):
143 """Create and return a new Instance.
145 Args:
146 instance_id: A string or integer representing the unique (per server) id
147 of the instance.
148 expect_ready_request: If True then the instance will be sent a special
149 request (i.e. /_ah/warmup or /_ah/start) before it can handle external
150 requests.
152 Returns:
153 The newly created instance.Instance.
156 def instance_config_getter():
157 runtime_config = self._runtime_config_getter()
158 runtime_config.instance_id = str(instance_id)
159 return runtime_config
161 with self._application_lock:
162 try:
163 self._go_application.maybe_build(self._modified_since_last_build)
164 except go_application.BuildError as e:
165 logging.error('Failed to build Go application: %s', e)
166 proxy = _GoBuildFailureRuntimeProxy(e)
167 else:
168 proxy = http_runtime.HttpRuntimeProxy(
169 self._go_application.go_executable,
170 instance_config_getter,
171 self._server_configuration,
172 self._go_application.get_environment())
173 self._modified_since_last_build = False
175 return instance.Instance(self.request_data,
176 instance_id,
177 proxy,
178 self.max_concurrent_requests,
179 self.max_background_threads,
180 expect_ready_request)