Merge branch 'development' into 'master'

Development

See merge request !3
This commit is contained in:
Joachim Lusiardi 2016-12-30 14:46:20 +01:00
commit 189201b10a
2 changed files with 44 additions and 13 deletions

View File

@ -7,6 +7,10 @@ This variable must be of the following format:
`PROXY_DATA=server_names:test.com;www.test.com,port:80` `PROXY_DATA=server_names:test.com;www.test.com,port:80`
Or written as regex:
PROXY\_DATA=(KEY:VALUE,)\*KEY:VALUE
The following options are possible: The following options are possible:
* **server_names**(required) the names of the virtual hosts separated by ";" * **server_names**(required) the names of the virtual hosts separated by ";"
@ -30,7 +34,7 @@ Run the container like this:
That means that the container exposes all Web Apps on all IPs. Do **not** use the *ip* option from above on the target containers. The *PROXY_DATA* environment variables would be something like That means that the container exposes all Web Apps on all IPs. Do **not** use the *ip* option from above on the target containers. The *PROXY_DATA* environment variables would be something like
`PROXY_DATA=server_names:cooldomain.test.com,port:8080,location=/webApp` `PROXY_DATA=server_names:cooldomain.test.com,port:8080,location:/webApp`
### Multiple IPs ### Multiple IPs
This option is used if your Docker Host has multiple IPs (perhaps a public IP in the internet and a private IP on a VPN). It is possible to expose some Web Apps only to the private network. This option is used if your Docker Host has multiple IPs (perhaps a public IP in the internet and a private IP on a VPN). It is possible to expose some Web Apps only to the private network.
@ -41,6 +45,6 @@ One container must be started for each IP that should host Web Apps. For example
If a target container does **not** have the *ip* option set, it listens on **all** IP adresses and will be handled by both containers. If a target container does **not** have the *ip* option set, it listens on **all** IP adresses and will be handled by both containers.
If a container uses, e.g., If a container uses, e.g.,
`PROXY_DATA=server_names:cooldomain.test.com,port:8080,location=/webApp,ip=10.1.2.3` `PROXY_DATA=server_names:cooldomain.test.com,port:8080,location:/webApp,ip:10.1.2.3`
then it will be only available on the private 10.1.2.3 IP (perhaps using a VPN). then it will be only available on the private 10.1.2.3 IP (perhaps using a VPN).

View File

@ -4,12 +4,13 @@ from docker import Client
from docker.errors import APIError from docker.errors import APIError
from string import Template from string import Template
import json import json
import re
import signal import signal
import os import os
import logging import logging
target_path="/tmp/nginx/" target_path = "/tmp/nginx/"
pid_file="/var/run/nginx.pid" pid_file = "/var/run/nginx.pid"
non_location_template = """# proxy for container '$containername' non_location_template = """# proxy for container '$containername'
server { server {
listen $listen; listen $listen;
@ -17,15 +18,16 @@ server {
location / { location / {
client_max_body_size $body_size; client_max_body_size $body_size;
client_body_timeout 300s; client_body_timeout 300s;
proxy_set_header X-Real-IP $$remote_addr; if ($$http_x_forwarded_for = "") {
proxy_set_header X-Forwarded-For $$remote_addr; add_header X-Forwarded-For $$remote_addr;
}
proxy_set_header Host $$host; proxy_set_header Host $$host;
proxy_pass http://$ip:$port/; proxy_pass http://$ip:$port/;
} }
} }
""" """
location_template="""# proxy for container '$containername' location_template = """# proxy for container '$containername'
server { server {
listen $listen; listen $listen;
server_name $names; server_name $names;
@ -35,29 +37,33 @@ server {
location /$location { location /$location {
client_max_body_size $body_size; client_max_body_size $body_size;
client_body_timeout 300s; client_body_timeout 300s;
proxy_set_header X-Real-IP $$remote_addr; if ($$http_x_forwarded_for = "") {
proxy_set_header X-Forwarded-For $$remote_addr; add_header X-Forwarded-For $$remote_addr;
}
proxy_set_header Host $$host; proxy_set_header Host $$host;
proxy_pass http://$ip:$port/; proxy_pass http://$ip:$port/;
} }
} }
""" """
def print_json(data): def print_json(data):
"""Prints the given value in JSON to stdout. Use this for debugging only""" """Prints the given value in JSON to stdout. Use this for debugging only"""
print(json.dumps(data, sort_keys=True, indent=4)) print(json.dumps(data, sort_keys=True, indent=4))
def analyse_env_vars(inspect_data): def analyse_env_vars(inspect_data):
"""Extracts the environment variables from the given result of an 'inspect """Extracts the environment variables from the given result of an 'inspect
container' call.""" container' call."""
env_data = {} env_data = {}
if not 'Env' in inspect_data['Config'] or inspect_data['Config']['Env'] is None: if 'Env' not in inspect_data['Config'] or inspect_data['Config']['Env'] is None:
return env_data return env_data
for env_var in inspect_data['Config']['Env']: for env_var in inspect_data['Config']['Env']:
t = env_var.split("=") t = env_var.split("=")
env_data[t[0]] = t[1] env_data[t[0]] = t[1]
return env_data return env_data
def analyse_proxy_data(data): def analyse_proxy_data(data):
"""Extracts the data for the proxy configuration (envrionment variable """Extracts the data for the proxy configuration (envrionment variable
'PROXY_DATA' and converts it to a dictionary.""" 'PROXY_DATA' and converts it to a dictionary."""
@ -67,20 +73,33 @@ def analyse_proxy_data(data):
proxy_data[t[0]] = t[1] proxy_data[t[0]] = t[1]
return proxy_data return proxy_data
def check_proxy_data_format(var_content):
"""
Validates the content of the variable.
:param var_content: content of the proxy data variable
:return: True if the content is of valid format, False otherwise
"""
return re.match(r"^(\w+:[^:,]+,)+\w+:[^:,]+$", var_content) is not None
def extract_ip(inspect_data): def extract_ip(inspect_data):
"""extracts the container's ip from the given inspect data""" """extracts the container's ip from the given inspect data"""
return inspect_data['NetworkSettings']['IPAddress'] return inspect_data['NetworkSettings']['IPAddress']
def extract_name(inspect_data): def extract_name(inspect_data):
"""extracts the container's name from the given inspect data""" """extracts the container's name from the given inspect data"""
return inspect_data['Name'] return inspect_data['Name']
def get_if_available(dict, key, defValue):
if key in dict: def get_if_available(dictionary, key, defValue):
return dict[key] if key in dictionary:
return dictionary[key]
else: else:
return defValue return defValue
def handle_container(id): def handle_container(id):
"""This function take a container's id and collects all data required """This function take a container's id and collects all data required
to create a proper proxy configuration. The configuration is then to create a proper proxy configuration. The configuration is then
@ -88,6 +107,9 @@ def handle_container(id):
inspect_data = client.inspect_container(id) inspect_data = client.inspect_container(id)
env_vars = analyse_env_vars(inspect_data) env_vars = analyse_env_vars(inspect_data)
if 'PROXY_DATA' in env_vars: if 'PROXY_DATA' in env_vars:
if not check_proxy_data_format(env_vars['PROXY_DATA']):
logging.info('cannot handle container with id "%s" named "%s": %s', id, extract_name(inspect_data), env_vars['PROXY_DATA'])
return
proxy_data = analyse_proxy_data(env_vars) proxy_data = analyse_proxy_data(env_vars)
container_listen_ip = get_if_available(proxy_data, 'ip', '0.0.0.0') container_listen_ip = get_if_available(proxy_data, 'ip', '0.0.0.0')
if container_listen_ip != '0.0.0.0' and container_listen_ip not in listen_ips: if container_listen_ip != '0.0.0.0' and container_listen_ip not in listen_ips:
@ -111,15 +133,18 @@ def handle_container(id):
else: else:
file.write(Template(location_template).substitute(substitutes)) file.write(Template(location_template).substitute(substitutes))
def reload_nginx_configuration(): def reload_nginx_configuration():
logging.info('HUPing nginx') logging.info('HUPing nginx')
os.kill(pid, signal.SIGHUP) os.kill(pid, signal.SIGHUP)
def get_pid(): def get_pid():
"""This function reads the process id from the given file.""" """This function reads the process id from the given file."""
with open(pid_file, 'r') as file: with open(pid_file, 'r') as file:
return int(file.read()) return int(file.read())
def get_docker_id(): def get_docker_id():
"""This function extracts the container's id from /proc/self/cgroup.""" """This function extracts the container's id from /proc/self/cgroup."""
id = '' id = ''
@ -138,6 +163,7 @@ def get_docker_id():
logging.error('could not determine container\'s id!') logging.error('could not determine container\'s id!')
return id return id
def get_listen_ips(): def get_listen_ips():
inspect_data = client.inspect_container(get_docker_id()) inspect_data = client.inspect_container(get_docker_id())
mappings = inspect_data['NetworkSettings']['Ports']['80/tcp'] mappings = inspect_data['NetworkSettings']['Ports']['80/tcp']
@ -150,6 +176,7 @@ def get_listen_ips():
ips.append(data['HostIp']) ips.append(data['HostIp'])
return ips return ips
def setup_logging(): def setup_logging():
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO) logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)