unbit / uwsgi

uWSGI application server container
http://projects.unbit.it/uwsgi
Other
3.46k stars 692 forks source link

Using the URL to destinguish between Vassal clones of the same app #847

Open shikasta-net opened 9 years ago

shikasta-net commented 9 years ago

This is both a question and an answer as I could not find this question anywhere and eventually figured out the answer. It may help someone else and may belong in the docs under emperor mode and routing.

I am creating a lightweight API using flask (RESTful). I wanted each 'client' (here meaning an entity with multiple users) to be completely isolated from web right down to database level. I also wanted it to be trivial to set up new clients.

The Emperor mode of uwsgi appeared to proved a way to do this: each client would have a config file that loaded the same API code and environmental variables set in that vassal's config would affect how its API behaved; worked. However, when defining the api resources I discovered a problem with using clones of the code as the end points of different vassals would collide (particularly if they had arbitrary variables in them). However, I discovered that I could use the same environment variables in those api resource definitions and thereby isolate each vassal by simply specifying the client name as part of the path "statically".

This is a little like routing but it's done automagically by virtue of how each vassal is isolated.

unbit commented 9 years ago

If you can post your config, it could be an interesting snippet

shikasta-net commented 9 years ago

I am using uWSGI 2.1 and flask 0.10.1. I think this is the minimum config required. app.py

import os
from flask import Flask

app = Flask (_name_)

client_name = os.environ ['CLIENT_NAME']

@app.rout('/'+client_name+'/index.html'
def index():
  return 'client %s says goodbye cruel world'%cname,200

emperor.ini

[uwsgi]
http = 0.0.0.0:80
subscription-mountpoints = true
http-subscription-server = /tmp/subscription-server.sock
master = true

emperor = /path/to/folder/of/vassals/

vassal.skel

[uwsgi]
env = CLIENT_NAME=%n

subscribe2 = server=/tmp/subscription-server.sock,key=127.0.0.1/%n
chdir = /path/to/location/of/app.py
wsgi-file = app.py
callable = app
enable-threads = true
processes = 1
socket = /tmp/worker_%n.sock

Run sudo uwsgi --ini emperor.ini. Create linked vassals in /path/to/folder/of/vassals/ ln -s vassal.skel <name>.ini Browse 127.0.0.1/<name>/index.html

Sorry about any typos, my virtual machine's shared clipboard doesn't want to copy out.

I can try to expand this example if it's not obvious how this is beneficial. I have a more sophisticated version working with RESTful resource points.

shikasta-net commented 9 years ago

While this has worked very well for me, I wonder if anyone has any thoughts on catching routes that haven't been defined yet? For example, if vassal Foo does not exist then path 127.0.0.1/Foo/index.html is not a known route and should produce an error message, but at the moment it just gets dropped.

unbit commented 9 years ago

There are the fastrouter/httprouter/sslrouter/rawrouter-fallback options, and in 2.1 (current master) you have the --fastrouter-fallback-on-no-key (that can be ported to httprouter too, feel free to make a pull request)