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."""
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
49 def handle(self
, environ
, start_response
, url_map
, match
, request_id
,
51 """Serves a request by displaying an error page.
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
64 A sequence of strings containing the body of the HTTP response.
66 start_response('500 Internal Server Error',
67 [('Content-Type', 'text/plain')])
68 yield 'The Go application could not be built.\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(
80 WARMUP_URL_MAP
= appinfo
.URLMap(
84 FILE_CHANGE_INSTANCE_RESTART_POLICY
= instance
.ALWAYS
86 def __init__(self
, request_data
, runtime_config_getter
, server_configuration
):
87 """Initializer for GoRuntimeInstanceFactory.
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
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.
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.
116 go_path
= os
.environ
['GOPATH']
120 if sys
.platform
.startswith('win32'):
121 roots
= go_path
.split(';')
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.
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.
146 instance_id: A string or integer representing the unique (per server) id
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
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
:
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
)
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
,
178 self
.max_concurrent_requests
,
179 self
.max_background_threads
,
180 expect_ready_request
)