AppEngine: gaierror when starting a task - python

I ran into an error that was painful to track down, so I thought I'd add the cause + "solution" here.
The setup:
Devbox - Running Google App Engine listening on all ports ("--address=0.0.0.0") serving a URL that launches a task.
Client - Client (Python requests library) which queries the callback URL
App Engine code:
class StartTaskCallback(webapp.RequestHandler):
def post(self):
param = self.request.get('param')
logging.info('STARTTASK: %s' % param)
# launch a task
taskqueue.add(url='/tasks/mytask',
queue_name='myqueue',
params={'param': param})
class MyTask(webapp.RequestHandler):
def post(self):
param = self.request.get('param')
logging.info('MYTASK: param = %s' % param)
When I queried the callback with my browser, everything worked, but the same query from the remote client gave me the following error:
ERROR 2012-03-23 21:18:27,351 taskqueue_stub.py:1858] An error occured while sending the task "task1" (Url: "/tasks/mytask") in queue "myqueue". Treating as a task error.
Traceback (most recent call last):
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/taskqueue/taskqueue_stub.py", line 1846, in ExecuteTask
connection.endheaders()
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 868, in endheaders
self._send_output()
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 740, in _send_output
self.send(msg)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 699, in send
self.connect()
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 683, in connect
self.timeout)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/socket.py", line 498, in create_connection
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
gaierror: [Errno 8] nodename nor servname provided, or not known
This error would just spin in a loop as the task retried. Though oddly, I could go to Admin -> Task Queues and click 'Run' to get the task to complete successfully.
At first I thought this was an error with the binding. I would not get an error if I queried the StartTaskCallback via the browser or if I ran the client locally.
Finally I noticed that App Engine is using the 'host' field of the request in order to build an absolute URL for the task. In /Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/taskqueue/taskqueue_stub.py (1829):
connection_host, = header_dict.get('host', [self._default_host])
if connection_host is None:
logging.error('Could not determine where to send the task "%s" '
'(Url: "%s") in queue "%s". Treating as an error.',
task.task_name(), task.url(), queue.queue_name)
return False
connection = httplib.HTTPConnection(connection_host)
In my case, I was using a special name + hosts file on the remote client to access the server.
192.168.1.208 devbox
So the 'host' for the remote client looked like 'devbox:8085' which the local server could not resolve.

To fix the issue, I simply added devbox to my AppEngine server's hosts file, but it sure would have been nice if the gaierror exception had printed the name it failed to resolve, or if App Engine didn't use the 'host' of the incoming request to build a URL for task creation.

Related

Python-Jenkins tunnel connection failed: 403 Forbidden

I have been using the Python Jenkins APIs to manager my Jenkins jobs. It has worked for a long time, but it stopped suddenly working. This is the code excerpt:
import jenkins
server = jenkins.Jenkins('https://jenkins.company.com', username='xxxx', password='password')
server._session.verify = False
print(server.jobs_count())
The traceback:
File "", line 1, in
server.jobs_count()
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line
1160, in jobs_count
return len(self.get_all_jobs())
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line
1020, in get_all_jobs
jobs = [(0, [], self.get_info(query=jobs_query)['jobs'])]
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 769,
in get_info
requests.Request('GET', self._build_url(url))
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 557,
in jenkins_open
return self.jenkins_request(req, add_crumb, resolve_auth).text
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 573,
in jenkins_request
self.maybe_add_crumb(req)
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 371,
in maybe_add_crumb
'GET', self._build_url(CRUMB_URL)), add_crumb=False)
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 557,
in jenkins_open
return self.jenkins_request(req, add_crumb, resolve_auth).text
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 576,
in jenkins_request
self._request(req))
File "E:\anaconda3\Lib\site-packages\jenkins_init_.py", line 550,
in _request
return self._session.send(r, **_settings)
File "E:\anaconda3\Lib\site-packages\requests\sessions.py", line
622, in send
r = adapter.send(request, **kwargs)
File "E:\anaconda3\Lib\site-packages\requests\adapters.py", line
507, in send
raise ProxyError(e, request=request)
ProxyError: HTTPSConnectionPool(host='jenkins.company.com', port=443): Max
retries exceeded with url:
/job/scp/job/sm/job/9218/job/4198/job/SIT/crumbIssuer/api/json (Caused
by ProxyError('Cannot connect to proxy.', OSError('Tunnel connection
failed: 403 Forbidden')))
Note that there isn't any proxy on the Jenkins server, and I can use the user/password logon to the Jenkins server without any issues.
I have the crum id and API token, but I haven't found anything that is indicating how to add the crum into the Python-Jenkins API.
The final part of the traceback says:
ProxyError: HTTPSConnectionPool(host='ebs.usps.gov', port=443)
Which most likely indicates that you have proxy settings that your Python code inherits from somewhere when it runs. It could be environment variables ((HTTP|HTTPS)_PROXY) on POSIX sort of platforms or something similar... If you need to to use a proxy to reach the Jenkins instance, then the issue is in the proxy itself. It blocks your access for some reason. If you do not need to use a proxy, then you should remove the settings affecting your Python code when you run it.
Also, see what J_H said...
tl;dr: You lack connectivity.
The jenkins library depends on import requests,
which is reporting the connectivity error.
Regrettably, it uses ProxyError in the diagnostic.
The rationale goes like this:
We're making a GET request for the application.
Optionally the "GET from server S" will be turned into "GET from proxy P" if proxying is in use.
Eventually we try to contact some host, S or P. Might as well tell a proxy user that state of S is unknown, but state of P is "down".
Here ends the "why mention proxying?" diagnostic rant.
When you say "I'm not using proxying", I believe you.
The diagnostic can be a bit of a red herring for
folks who are not yet familiar with it.
When I probe ebs.usps.gov (56.207.107.97) on ports 443, 80, or with ICMP, I see zero response packets.
You're in a different part of the net, with different
filters between you and server, so your mileage might vary.
I wouldn't describe that host as a "public server",
since it offers me no responses.
It appears you sent SYN to tcp port 443,
and either some network device discarded that packet,
or the server replied with SYN-ACK and that
reply packet was discarded.
Most likely the server is down or your request was discarded.

MongoDB Server Configuration

I have a MongoDB server running on my localhost. I wrote a simple Python program that reads/writes to the database using "localhost"; however, I want to give other clients access to my MongoDB server. For now, I am not concerned about access security and would like to grant access to anyone. How should I configure the Mongo Server to do this?
Here is the simple program connecting to localhost.
from pymongo import MongoClient
connection = MongoClient("Localhost")
db = connection.hockey.players
results = db.find()
print()
print('+-+-+-+-+-+-+-+-+-+-+-+-+-+-')
for record in results:
print(record['name'] + ',',record['position'])
connection.close()
The error message I'm getting:
File "C:/Users/Peter/PycharmProjects/Test/helloWorld.py", line 8, in
for record in results:
File "C:\Python34\lib\site-packages\pymongo\cursor.py", line 1097, in next
if len(self.__data) or self._refresh():
File "C:\Python34\lib\site-packages\pymongo\cursor.py", line 1019, in _refresh self.__read_concern))
File "C:\Python34\lib\site-packages\pymongo\cursor.py", line 850, in __send_message **kwargs)
File "C:\Python34\lib\site-packages\pymongo\mongo_client.py", line 777, in _send_message_with_response server = topology.select_server(selector)
File "C:\Python34\lib\site-packages\pymongo\topology.py", line 142, in select_server address))
File "C:\Python34\lib\site-packages\pymongo\topology.py", line 118, in select_servers self._error_message(selector))
pymongo.errors.ServerSelectionTimeoutError: x.y.z.w:27017: timed out
Process finished with exit code 1
Below is my current localhost setup that works fine if I run it on my client which hosts the MongoDB. Current setup
Thanks
Give them your ip address and the port number (Default:27017) to connect to your server. Also edit the bindIp in the mongod.conf file as bindIp: 0.0.0.0. Ask them to connect to your database by using something like:
from pymongo import MongoClient
connection = MongoClient("mongodb://your_ip:yourport")
If you're on windows, create the configuration file mongod.cfg. And add the entry as
systemLog:
destination: file
path: c:\data\log\mongod.log
storage:
dbPath: c:\data\db
net:
bindIp: 0.0.0.0
port: 27017
Make sure you've already created log and db folder or change the path where your data and log folder are located.
Start the mongodb by specyfying thr configuration file.
Verify that you local interface is up. It should explain the timeout.
ifup lo

Handling Non-SSL Traffic in Python/Tornado

I have a webservice running in python 2.7.10 / Tornado that uses SSL. This service throws an error when a non-SSL call comes through (http://...).
I don't want my service to be accessible when SSL is not used, but I'd like to handle it in a cleaner fashion.
Here is my main code that works great over SSL:
if __name__ == "__main__":
tornado.options.parse_command_line()
#does not work on 2.7.6
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain("...crt.pem","...key.pem")
ssl_ctx.load_verify_locations("...CA.crt.pem")
http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_ctx, decompress_request=True)
http_server.listen(options.port)
mainloop = tornado.ioloop.IOLoop.instance()
print("Main Server started on port XXXX")
mainloop.start()
and here is the error when I hit that server with http://... instead of https://...:
[E 151027 20:45:57 http1connection:700] Uncaught exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/tornado/http1connection.py", line 691, in _server_request_loop
ret = yield conn.read_response(request_delegate)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 807, in run
value = future.result()
File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 209, in result
raise_exc_info(self._exc_info)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 810, in run
yielded = self.gen.throw(*sys.exc_info())
File "/usr/local/lib/python2.7/dist-packages/tornado/http1connection.py", line 166, in _read_message
quiet_exceptions=iostream.StreamClosedError)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 807, in run
value = future.result()
File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 209, in result
raise_exc_info(self._exc_info)
File "<string>", line 3, in raise_exc_info
SSLError: [SSL: HTTP_REQUEST] http request (_ssl.c:590)
Any ideas how I should handle that exception?
And what the standard-conform return value would be when I catch a non-SSL call to an SSL-only API?
UPDATE
This API runs on a specific port e.g. https://example.com:1234/. I want to inform a user who is trying to connect without SSL, e.g. http://example.com:1234/ that what they are doing is incorrect by returning an error message or status code. As it is the uncaught exception returns a 500, which they could interpret as a programming error on my part. Any ideas?
There's an excelent discussion in this Tornado issue about that, where Tornado maintainer says:
If you have both HTTP and HTTPS in the same tornado process, you must be running two separate HTTPServers (of course such a feature should not be tied to whether SSL is handled at the tornado level, since you could be terminating SSL in a proxy, but since your question stipulated that SSL was enabled in tornado let's focus on this case first). You could simply give the HTTP server a different Application, one that just does this redirect.
So, the best solution it's to HTTPServer that listens on port 80 and doesn't has the ssl_options parameter setted.
UPDATE
A request to https://example.com/some/path will go to port 443, where you must have an HTTPServer configured to handle https traffic; while a request to http://example.com/some/path will go to port 80, where you must have another instance of HTTPServer without ssl options, and this is where you must return the custom response code you want. That shouldn't raise any error.

Flask-Socketio: namespace keyerror., get list of connected clients

I am using Flask-Socketio on my server that is running Flask and my clients run javascript. All my clients connect to the server using the namespace '/test'. Upon a certain action on the client, I want the client to trigger a function on the server that iterates through all the connected clients. I am using the following code segment to iterate through the list of connected clients:
for sessid, socket in request.namespace.socket.server.sockets.items():
print "socket id : %r" % (socket['/test'].session['id'])
where each connection has an 'id' key assigned to it on connection.
However sometimes the server gives the following error indicating that the namespace key was not found:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/gevent/greenlet.py", line 327, in run
result = self._run(*self.args, **self.kwargs)
File "/usr/local/lib/python2.7/dist-packages/socketio/virtsocket.py", line 403, in_receiver_loop
retval = pkt_ns.process_packet(pkt)
File "/usr/local/lib/python2.7/dist-packages/socketio/namespace.py", line 155, in process_packet
return self.process_event(packet)
File "/usr/local/lib/python2.7/dist-packages/flask_socketio/__init__.py", line 64, in process_event
return self.socketio._dispatch_message(app, self, message, args)
File "/usr/local/lib/python2.7/dist-packages/flask_socketio/__init__.py", line 137, in _dispatch_message
ret = self.messages[namespace.ns_name][message](*args)
File "/home/ubuntu/flask-app/current/py/flaskapp/main/sockets.py", line 190, in gameInitialisedByClient
print "id : %r random: %r" % (socket['/test'].session['id'], socket['/test'].session['random'])
File "/usr/local/lib/python2.7/dist-packages/socketio/virtsocket.py", line 190, in __getitem__
return self.active_ns[key]
KeyError: '/test'
<Greenlet at 0x7f483bc6bf50: <bound method Socket._receiver_loop of <socketio.virtsocket.Socket object at 0x7f483a5ac6d0>>> failed with KeyError
I do not understand why i get this error since all my sockets that connect to the server are connecting on the same namespace. Moreover, this error is experienced only during a fraction of the function calls. Why isn't this error consistent?
Is there a better way in which I could iterate through the list of connected clients?
You are iterating over a private data structure of package gevent-socketio, so unexpected things can happen.
I do not know the internals of this package to tell you why this happens, but I think a much safer approach would be for you to build your own list of connected clients. You can add and remove clients to your list in the connect and disconnect handlers. I think something like this will work:
clients = []
#socketio.on('connect', namespace='/test')
def connect():
clients.append(request.namespace)
#socketio.on('disconnect', namespace='/test')
def disconnect():
clients.remove(request.namespace)

Unable to connect to external services from App Engine development server with urlfetch or urllib2

The following code works in the Python interactive shell:
import urllib2
result = urllib2.urlopen("http://www.google.com/")
and gives a 200 result.
If I run the same code in an AppEngine app running locally with the development server, it fails with the following error:
URLError: <urlopen error An error occured while connecting to the server:
Unable to fetch URL: http://www.google.com/
Error: [Errno 11004] getaddrinfo failed>`
I've tried using the urlfetch library directly:
from google.appengine.api import urlfetch
result = urlfetch.fetch("http://www.google.com")
and this also fails (which makes sense, as I believe urllib2 within AppEngine calls URLFetch internally?)
I can clearly access the URL from my local machine - so what's happening?
UPDATE: the relevant stack trace:
File "c:\dev\repos\stackoverflow\main.py", line 40, in get_latest_comments
result = urlfetch.fetch("http://www.google.com")
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\api\urlfetch.py", line 266, in fetch
return rpc.get_result()
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\api\apiproxy_stub_map.py", line 604, in get_result
return self.__get_result_hook(self)
File "C:\Program Files (x86)\Google\google_appengine\google\appengine\api\urlfetch.py", line 397, in _get_fetch_result
raise DownloadError("Unable to fetch URL: " + url + error_detail)
DownloadError: Unable to fetch URL: http://www.google.com Error: [Errno 11004] getaddrinfo failed
DO you have a proxy configured with environment variables? The dev_appserver clears all env vars.

Categories