launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #30072
[Merge] ~cjwatson/launchpad:charm-appserver-http-interface into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:charm-appserver-http-interface into launchpad:master.
Commit message:
charm/launchpad-appserver: Add http interface support
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/444283
This allows relating `launchpad-appserver` to a deployment of the `haproxy` charm to have it load-balance the various workers and expose them on the ports expected by other parts of Launchpad.
The code is based on the `ols-http` layer, but I had to write custom code for it because the appserver exposes two ports with different purposes (the main application, and XML-RPC); and the current production `haproxy` configuration uses slightly different options for those two, with special error page handling for the main application.
While this doesn't result in the exact same `haproxy` configuration that we have on production, I think it's equivalent.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:charm-appserver-http-interface into launchpad:master.
diff --git a/charm/launchpad-appserver/README.md b/charm/launchpad-appserver/README.md
index 2222972..26a3afa 100644
--- a/charm/launchpad-appserver/README.md
+++ b/charm/launchpad-appserver/README.md
@@ -9,5 +9,9 @@ You will need the following relations:
juju relate launchpad-appserver memcached
juju relate launchpad-appserver rabbitmq-server
+You can also relate it to a load balancer:
+
+ juju relate launchpad-appserver:loadbalancer haproxy:reverseproxy
+
By default the main application server runs on port 8085, and the XML-RPC
application server runs on port 8087.
diff --git a/charm/launchpad-appserver/config.yaml b/charm/launchpad-appserver/config.yaml
index f016031..2ff6762 100644
--- a/charm/launchpad-appserver/config.yaml
+++ b/charm/launchpad-appserver/config.yaml
@@ -24,6 +24,30 @@ options:
Secret key for Git access tokens issued to Launchpad users. Any
random string of a reasonable size (64 characters) is ok.
default:
+ haproxy_server_options:
+ type: string
+ description: Options to add to HAProxy "server" lines.
+ default: check inter 5000 rise 2 fall 5 maxconn 16
+ haproxy_service_options_main:
+ type: string
+ description: HAProxy options for the main port.
+ default: |
+ - mode http
+ - option httplog
+ - option httpchk HEAD /_status/ping
+ - option http-server-close
+ - http-check disable-on-404
+ - balance roundrobin
+ haproxy_service_options_xmlrpc:
+ type: string
+ description: HAProxy options for the XML-RPC port.
+ default: |
+ - mode http
+ - option httplog
+ - option httpchk HEAD /_status/ping
+ - option http-server-close
+ - http-check disable-on-404
+ - balance roundrobin
internal_macaroon_secret_key:
type: string
description: >
diff --git a/charm/launchpad-appserver/layer.yaml b/charm/launchpad-appserver/layer.yaml
index 1401723..921b8a8 100644
--- a/charm/launchpad-appserver/layer.yaml
+++ b/charm/launchpad-appserver/layer.yaml
@@ -1,6 +1,7 @@
includes:
- layer:launchpad-db
- layer:coordinator
+ - interface:http
- interface:memcache
repo: https://git.launchpad.net/launchpad
options:
diff --git a/charm/launchpad-appserver/metadata.yaml b/charm/launchpad-appserver/metadata.yaml
index b56f0d6..8fcad01 100644
--- a/charm/launchpad-appserver/metadata.yaml
+++ b/charm/launchpad-appserver/metadata.yaml
@@ -18,3 +18,6 @@ requires:
interface: pgsql
memcache:
interface: memcache
+provides:
+ loadbalancer:
+ interface: http
diff --git a/charm/launchpad-appserver/reactive/launchpad-appserver.py b/charm/launchpad-appserver/reactive/launchpad-appserver.py
index ad4b439..1ceb82c 100644
--- a/charm/launchpad-appserver/reactive/launchpad-appserver.py
+++ b/charm/launchpad-appserver/reactive/launchpad-appserver.py
@@ -5,6 +5,7 @@ import shlex
import subprocess
from multiprocessing import cpu_count
+import yaml
from charmhelpers.core import hookenv, host, templating
from charms.coordinator import acquire
from charms.launchpad.base import configure_email, get_service_config
@@ -206,3 +207,76 @@ def nrpe_available():
@when_not("nrpe-external-master.available")
def nrpe_unavailable():
clear_flag("launchpad.appserver.nrpe-external-master.published")
+
+
+@when("loadbalancer.available", "service.configured")
+@when_not("launchpad.loadbalancer.configured")
+def configure_loadbalancer():
+ config = hookenv.config()
+
+ try:
+ service_options_main = yaml.safe_load(
+ config["haproxy_service_options_main"]
+ )
+ except Exception:
+ hookenv.log("Could not parse haproxy_service_options_main YAML")
+ hookenv.status_set(
+ "blocked", "Bad haproxy_service_options_main YAML configuration"
+ )
+ return
+ try:
+ service_options_xmlrpc = yaml.safe_load(
+ config["haproxy_service_options_xmlrpc"]
+ )
+ except Exception:
+ hookenv.log("Could not parse haproxy_service_options_xmlrpc YAML")
+ hookenv.status_set(
+ "blocked", "Bad haproxy_service_options_xmlrpc YAML configuration"
+ )
+ return
+ server_options = config["haproxy_server_options"]
+
+ unit_name = hookenv.local_unit().replace("/", "-")
+ unit_ip = hookenv.unit_private_ip()
+ services = [
+ {
+ "service_name": "appserver-main",
+ "service_port": config["port_main"],
+ "service_host": "0.0.0.0",
+ "service_options": list(service_options_main),
+ "servers": [
+ [
+ f"main_{unit_name}",
+ unit_ip,
+ config["port_main"],
+ server_options,
+ ]
+ ],
+ },
+ {
+ "service_name": "appserver-xmlrpc",
+ "service_port": config["port_xmlrpc"],
+ "service_host": "0.0.0.0",
+ "service_options": list(service_options_xmlrpc),
+ "servers": [
+ [
+ f"xmlrpc_{unit_name}",
+ unit_ip,
+ config["port_xmlrpc"],
+ server_options,
+ ]
+ ],
+ },
+ ]
+ services_yaml = yaml.dump(services)
+
+ for rel in hookenv.relations_of_type("loadbalancer"):
+ hookenv.relation_set(rel["__relid__"], services=services_yaml)
+
+ set_state("launchpad.loadbalancer.configured")
+
+
+@when("launchpad.loadbalancer.configured")
+@when_not_all("loadbalancer.available", "service.configured")
+def deconfigure_loadbalancer():
+ remove_state("launchpad.loadbalancer.configured")