Merge lp:~james-page/charms/precise/openstack-dashboard/ha-support into lp:~openstack-charmers/charms/precise/openstack-dashboard/ha-support
- Precise Pangolin (12.04)
- ha-support
- Merge into ha-support
Status: | Merged |
---|---|
Merged at revision: | 28 |
Proposed branch: | lp:~james-page/charms/precise/openstack-dashboard/ha-support |
Merge into: | lp:~openstack-charmers/charms/precise/openstack-dashboard/ha-support |
Diff against target: |
596 lines (+331/-68) 4 files modified
config.yaml (+25/-0) hooks/horizon-common (+45/-29) hooks/horizon-relations (+36/-17) hooks/lib/openstack-common (+225/-22) |
To merge this branch: | bzr merge lp:~james-page/charms/precise/openstack-dashboard/ha-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Gandelman (community) | Needs Fixing | ||
Review via email: mp+152712@code.launchpad.net |
Commit message
Description of the change
James Page (james-page) wrote : | # |
Adam Gandelman (gandelman-a) wrote : | # |
Hey James, some comments:
- Please add a note to config.yaml or docs stating ssl_cert + ssl_key are expected to be input as base64 encoded.
- Once I realized that, I did a juju set openstack-dashboard ssl_key="$(cat /tmp/key | base64)" ssl_cert="$(cat tmp/cert | base64)" but was getting base64 decode errors in config-changed, needed to update the horizon-common cert config function:
key=$2
- echo $cert | base64 -d > /etc/ssl/
- echo $key | base64 -d > /etc/ssl/
+ echo $cert | base64 -di > /etc/ssl/
+ echo $key | base64 -di > /etc/ssl/
chmod 0600 /etc/ssl/
- I don't think its a blocker, but one issue with putting SSL termination behind haproxy is that all service units are sharing the same certificate. When deploying with custom certificates, its the deployers responsibility to provide a certificate that will work for all dashboard hosts. When using hacluster/VIP, a cert signed for a single common name (the VIP) would work. However, if we want to use keystone-provided certificates (I hope to), when there is no VIP/hacluster, the generated certificate will only be signed for the eligible leader at the time the keystone <-> dashboard relation is made. This will work okay initially, but if that initial leader host goes away and requests start hitting another node in the service group, a certificate for an invalid host will be used and a browser warning. This is less of an issue for the dashboard since it ends with a browser warning, but more of an issue if we plan on using this configuration for the internal openstack API traffic.
- Also, can you please include the latest openstack-common helper from lp:~openstack-charmers/openstack-charm-helpers/ha-helpers and rebase your changes to that code against the latest? It'll make syncing these changes easier once this merges, and I expect we'll be using the haproxy changes elsewhere.
- 42. By James Page
-
Fixup base64 encoding features
- 43. By James Page
-
Rebase from target
Preview Diff
1 | === modified file 'config.yaml' | |||
2 | --- config.yaml 2013-02-13 16:00:50 +0000 | |||
3 | +++ config.yaml 2013-03-13 11:10:27 +0000 | |||
4 | @@ -49,3 +49,28 @@ | |||
5 | 49 | description: | | 49 | description: | |
6 | 50 | Default multicast port number that will be used to communicate between | 50 | Default multicast port number that will be used to communicate between |
7 | 51 | HA Cluster nodes. | 51 | HA Cluster nodes. |
8 | 52 | # User provided SSL cert and key | ||
9 | 53 | ssl_cert: | ||
10 | 54 | type: string | ||
11 | 55 | description: | | ||
12 | 56 | Base64 encoded SSL certificate to install and use for API ports. | ||
13 | 57 | . | ||
14 | 58 | juju set swift-proxy ssl_cert="$(cat cert | base64)" \ | ||
15 | 59 | ssl_key="$(cat key | base64)" | ||
16 | 60 | . | ||
17 | 61 | Setting this value (and ssl_key) will enable reverse proxying, point | ||
18 | 62 | Swifts's entry in the Keystone catalog to use https, and override | ||
19 | 63 | any certficiate and key issued by Keystone (if it is configured to | ||
20 | 64 | do so). | ||
21 | 65 | ssl_key: | ||
22 | 66 | type: string | ||
23 | 67 | description: | | ||
24 | 68 | Base64 encoded SSL key to use with certificate specified as ssl_cert. | ||
25 | 69 | offline-compression: | ||
26 | 70 | type: string | ||
27 | 71 | default: "yes" | ||
28 | 72 | description: Use pre-generated Less compiled JS and CSS. | ||
29 | 73 | debug: | ||
30 | 74 | type: string | ||
31 | 75 | default: "no" | ||
32 | 76 | description: Show Django debug messages. | ||
33 | 52 | 77 | ||
34 | === modified file 'hooks/horizon-common' | |||
35 | --- hooks/horizon-common 2013-02-14 14:22:41 +0000 | |||
36 | +++ hooks/horizon-common 2013-03-13 11:10:27 +0000 | |||
37 | @@ -1,4 +1,5 @@ | |||
38 | 1 | #!/bin/bash | 1 | #!/bin/bash |
39 | 2 | # vim: set ts=2:et | ||
40 | 2 | 3 | ||
41 | 3 | CHARM="openstack-dashboard" | 4 | CHARM="openstack-dashboard" |
42 | 4 | 5 | ||
43 | @@ -16,15 +17,28 @@ | |||
44 | 16 | local key=$1 value=$2 | 17 | local key=$1 value=$2 |
45 | 17 | [[ -z "$key" ]] || [[ -z "$value" ]] && | 18 | [[ -z "$key" ]] || [[ -z "$value" ]] && |
46 | 18 | juju-log "$CHARM set_or_update: ERROR - missing parameters" && return 1 | 19 | juju-log "$CHARM set_or_update: ERROR - missing parameters" && return 1 |
49 | 19 | grep -q "^$key = \"$value\"" "$LOCAL_SETTINGS" && | 20 | if [ "$value" == "True" ] || [ "$value" == "False" ]; then |
50 | 20 | juju-log "$CHARM set_or_update: $key = $value already set" && return 0 | 21 | grep -q "^$key = $value" "$LOCAL_SETTINGS" && |
51 | 22 | juju-log "$CHARM set_or_update: $key = $value already set" && return 0 | ||
52 | 23 | else | ||
53 | 24 | grep -q "^$key = \"$value\"" "$LOCAL_SETTINGS" && | ||
54 | 25 | juju-log "$CHARM set_or_update: $key = $value already set" && return 0 | ||
55 | 26 | fi | ||
56 | 21 | if grep -q "^$key = " "$LOCAL_SETTINGS" ; then | 27 | if grep -q "^$key = " "$LOCAL_SETTINGS" ; then |
57 | 22 | juju-log "$CHARM set_or_update: Setting $key = $value" | 28 | juju-log "$CHARM set_or_update: Setting $key = $value" |
58 | 23 | cp "$LOCAL_SETTINGS" /etc/openstack-dashboard/local_settings.last | 29 | cp "$LOCAL_SETTINGS" /etc/openstack-dashboard/local_settings.last |
60 | 24 | sed -i "s|\(^$key = \).*|\1\"$value\"|g" "$LOCAL_SETTINGS" || return 1 | 30 | if [ "$value" == "True" ] || [ "$value" == "False" ]; then |
61 | 31 | sed -i "s|\(^$key = \).*|\1$value|g" "$LOCAL_SETTINGS" || return 1 | ||
62 | 32 | else | ||
63 | 33 | sed -i "s|\(^$key = \).*|\1\"$value\"|g" "$LOCAL_SETTINGS" || return 1 | ||
64 | 34 | fi | ||
65 | 25 | else | 35 | else |
66 | 26 | juju-log "$CHARM set_or_update: Adding $key = $value" | 36 | juju-log "$CHARM set_or_update: Adding $key = $value" |
68 | 27 | echo "$key = \"$value\"" >>$LOCAL_SETTINGS || return 1 | 37 | if [ "$value" == "True" ] || [ "$value" == "False" ]; then |
69 | 38 | echo "$key = $value" >>$LOCAL_SETTINGS || return 1 | ||
70 | 39 | else | ||
71 | 40 | echo "$key = \"$value\"" >>$LOCAL_SETTINGS || return 1 | ||
72 | 41 | fi | ||
73 | 28 | fi | 42 | fi |
74 | 29 | return 0 | 43 | return 0 |
75 | 30 | } | 44 | } |
76 | @@ -46,35 +60,37 @@ | |||
77 | 46 | export JUJU_REMOTE_UNIT=$(relation-list -r $r_id | head -n1) | 60 | export JUJU_REMOTE_UNIT=$(relation-list -r $r_id | head -n1) |
78 | 47 | export JUJU_RELATION="identity-service" | 61 | export JUJU_RELATION="identity-service" |
79 | 48 | export JUJU_RELATION_ID="$r_id" | 62 | export JUJU_RELATION_ID="$r_id" |
83 | 49 | local ks_host=$(relation-get -r $r_id private-address) | 63 | local service_host=$(relation-get -r $r_id service_host) |
84 | 50 | if [[ -n "$ks_host" ]] ; then | 64 | local service_port=$(relation-get -r $r_id service_port) |
85 | 51 | service_url="http://$ks_host:5000/v2.0" | 65 | if [[ -n "$service_host" ]] && [[ -n "$service_port" ]] ; then |
86 | 66 | service_url="http://$service_host:$service_port/v2.0" | ||
87 | 52 | set_or_update OPENSTACK_KEYSTONE_URL "$service_url" | 67 | set_or_update OPENSTACK_KEYSTONE_URL "$service_url" |
88 | 53 | fi | 68 | fi |
89 | 54 | fi | 69 | fi |
90 | 55 | } | 70 | } |
91 | 56 | 71 | ||
114 | 57 | configure_memcached() { | 72 | configure_apache() { |
115 | 58 | tfile=`mktemp /etc/.memcached.conf.XXXXXXX` | 73 | # Reconfigure to listen on provided port |
116 | 59 | cat > $tfile << EOF | 74 | a2ensite default-ssl || : |
117 | 60 | # memcache config file - juju openstack-dashboard | 75 | a2enmod ssl || : |
118 | 61 | -d | 76 | for ports in $@; do |
119 | 62 | logfile /var/log/memcached.log | 77 | from_port=$(echo $ports | cut -d : -f 1) |
120 | 63 | -m 64 | 78 | to_port=$(echo $ports | cut -d : -f 2) |
121 | 64 | -p 11211 | 79 | sed -i -e "s/$from_port/$to_port/g" /etc/apache2/ports.conf |
122 | 65 | -u memcache | 80 | for site in $(ls -1 /etc/apache2/sites-available); do |
123 | 66 | -l 0.0.0.0 | 81 | sed -i -e "s/$from_port/$to_port/g" \ |
124 | 67 | # TODO: Enable SASL auth | 82 | /etc/apache2/sites-available/$site |
125 | 68 | # -S | 83 | done |
126 | 69 | EOF | 84 | done |
105 | 70 | new_hash=`md5sum $tfile|cut -d' ' -f1` | ||
106 | 71 | old_hash=`md5sum /etc/memcached.conf|cut -d' ' -f1` | ||
107 | 72 | if [ "$new_hash" != "$old_hash" ]; then | ||
108 | 73 | juju-log "Reconfiguring memcache for clustering" | ||
109 | 74 | # TODO: setup SASL authentication | ||
110 | 75 | mv -f /etc/memcached.conf /etc/memcached.conf.$old_hash | ||
111 | 76 | mv -f $tfile /etc/memcached.conf | ||
112 | 77 | service memcached restart | ||
113 | 78 | fi | ||
127 | 79 | } | 85 | } |
128 | 80 | 86 | ||
129 | 87 | configure_apache_cert() { | ||
130 | 88 | cert=$1 | ||
131 | 89 | key=$2 | ||
132 | 90 | echo $cert | base64 -di > /etc/ssl/certs/dashboard.cert | ||
133 | 91 | echo $key | base64 -di > /etc/ssl/private/dashboard.key | ||
134 | 92 | chmod 0600 /etc/ssl/private/dashboard.key | ||
135 | 93 | sed -i -e "s|\(.*SSLCertificateFile\).*|\1 /etc/ssl/certs/dashboard.cert|g" \ | ||
136 | 94 | -e "s|\(.*SSLCertificateKeyFile\).*|\1 /etc/ssl/private/dashboard.key|g" \ | ||
137 | 95 | /etc/apache2/sites-available/default-ssl | ||
138 | 96 | } | ||
139 | 81 | 97 | ||
140 | === modified file 'hooks/horizon-relations' | |||
141 | --- hooks/horizon-relations 2013-02-27 05:27:51 +0000 | |||
142 | +++ hooks/horizon-relations 2013-03-13 11:10:27 +0000 | |||
143 | @@ -50,6 +50,12 @@ | |||
144 | 50 | juju-log "Insufficient information to configure keystone url" | 50 | juju-log "Insufficient information to configure keystone url" |
145 | 51 | exit 0 | 51 | exit 0 |
146 | 52 | fi | 52 | fi |
147 | 53 | local ca_cert=$(relation-get ca_cert) | ||
148 | 54 | if [ -n "$ca_cert" ]; then | ||
149 | 55 | juju-log "Installing Keystone supplied CA cert." | ||
150 | 56 | echo $ca_cert | base64 -di > /usr/local/share/ca-certificates/keystone_juju_ca_cert.crt | ||
151 | 57 | update-ca-certificates --fresh | ||
152 | 58 | fi | ||
153 | 53 | service_url="http://${service_host}:${service_port}/v2.0" | 59 | service_url="http://${service_host}:${service_port}/v2.0" |
154 | 54 | juju-log "$CHARM: Configuring Horizon to access keystone @ $service_url." | 60 | juju-log "$CHARM: Configuring Horizon to access keystone @ $service_url." |
155 | 55 | set_or_update OPENSTACK_KEYSTONE_URL "$service_url" | 61 | set_or_update OPENSTACK_KEYSTONE_URL "$service_url" |
156 | @@ -95,16 +101,39 @@ | |||
157 | 95 | keystone_joined "$relid" | 101 | keystone_joined "$relid" |
158 | 96 | done | 102 | done |
159 | 97 | 103 | ||
162 | 98 | service apache2 reload | 104 | if [ "$(config-get offline-compression)" != "yes" ]; then |
163 | 99 | 105 | set_or_update COMPRESS_OFFLINE False | |
164 | 106 | apt-get install -y nodejs node-less | ||
165 | 107 | else | ||
166 | 108 | set_or_update COMPRESS_OFFLINE True | ||
167 | 109 | fi | ||
168 | 110 | |||
169 | 111 | # Configure default HAProxy + Apache config | ||
170 | 112 | if [ -n "$(config-get ssl_cert)" ] && \ | ||
171 | 113 | [ -n "$(config-get ssl_key)" ]; then | ||
172 | 114 | configure_apache_cert "$(config-get ssl_cert)" "$(config-get ssl_key)" | ||
173 | 115 | fi | ||
174 | 116 | |||
175 | 117 | if [ "$(config-get debug)" != "yes" ]; then | ||
176 | 118 | set_or_update DEBUG False | ||
177 | 119 | else | ||
178 | 120 | set_or_update DEBUG True | ||
179 | 121 | fi | ||
180 | 122 | |||
181 | 123 | # Reconfigure Apache Ports | ||
182 | 124 | configure_apache "80:70" "443:433" | ||
183 | 125 | service apache2 restart | ||
184 | 126 | configure_haproxy "dash_insecure:80:70:http dash_secure:443:433:tcp" | ||
185 | 127 | service haproxy restart | ||
186 | 100 | } | 128 | } |
187 | 101 | 129 | ||
188 | 102 | function cluster_changed() { | 130 | function cluster_changed() { |
191 | 103 | configure_haproxy "openstack_dashboard:80" | 131 | configure_haproxy "dash_insecure:80:70:http dash_secure:443:433:tcp" |
192 | 104 | configure_memcached | 132 | service haproxy reload |
193 | 105 | } | 133 | } |
194 | 106 | 134 | ||
195 | 107 | function ha_relation_joined() { | 135 | function ha_relation_joined() { |
196 | 136 | # Configure HA Cluster | ||
197 | 108 | local corosync_bindiface=`config-get ha-bindiface` | 137 | local corosync_bindiface=`config-get ha-bindiface` |
198 | 109 | local corosync_mcastport=`config-get ha-mcastport` | 138 | local corosync_mcastport=`config-get ha-mcastport` |
199 | 110 | local vip=`config-get vip` | 139 | local vip=`config-get vip` |
200 | @@ -126,28 +155,19 @@ | |||
201 | 126 | init_services="{ | 155 | init_services="{ |
202 | 127 | 'res_horizon_haproxy':'haproxy' | 156 | 'res_horizon_haproxy':'haproxy' |
203 | 128 | }" | 157 | }" |
206 | 129 | groups="{ | 158 | clones="{ |
207 | 130 | 'grp_horizon_haproxy':'res_horizon_vip res_horizon_haproxy' | 159 | 'cl_horizon_haproxy':'res_horizon_haproxy' |
208 | 131 | }" | 160 | }" |
209 | 132 | relation-set corosync_bindiface=$corosync_bindiface \ | 161 | relation-set corosync_bindiface=$corosync_bindiface \ |
210 | 133 | corosync_mcastport=$corosync_mcastport \ | 162 | corosync_mcastport=$corosync_mcastport \ |
211 | 134 | resources="$resources" resource_params="$resource_params" \ | 163 | resources="$resources" resource_params="$resource_params" \ |
213 | 135 | init_services="$init_services" groups="$groups" | 164 | init_services="$init_services" clones="$clones" |
214 | 136 | else | 165 | else |
215 | 137 | juju-log "Insufficient configuration data to configure hacluster" | 166 | juju-log "Insufficient configuration data to configure hacluster" |
216 | 138 | exit 1 | 167 | exit 1 |
217 | 139 | fi | 168 | fi |
218 | 140 | } | 169 | } |
219 | 141 | 170 | ||
220 | 142 | |||
221 | 143 | function ha_relation_changed() { | ||
222 | 144 | local clustered=`relation-get clustered` | ||
223 | 145 | if [ -n "$clustered" ]; then | ||
224 | 146 | open-port 10080 | ||
225 | 147 | fi | ||
226 | 148 | } | ||
227 | 149 | |||
228 | 150 | |||
229 | 151 | juju-log "$CHARM: Running hook $ARG0." | 171 | juju-log "$CHARM: Running hook $ARG0." |
230 | 152 | case $ARG0 in | 172 | case $ARG0 in |
231 | 153 | "install") install_hook ;; | 173 | "install") install_hook ;; |
232 | @@ -161,5 +181,4 @@ | |||
233 | 161 | "cluster-relation-changed") cluster_changed ;; | 181 | "cluster-relation-changed") cluster_changed ;; |
234 | 162 | "cluster-relation-departed") cluster_changed ;; | 182 | "cluster-relation-departed") cluster_changed ;; |
235 | 163 | "ha-relation-joined") ha_relation_joined ;; | 183 | "ha-relation-joined") ha_relation_joined ;; |
236 | 164 | "ha-relation-changed") ha_relation_changed ;; | ||
237 | 165 | esac | 184 | esac |
238 | 166 | 185 | ||
239 | === modified file 'hooks/lib/openstack-common' | |||
240 | --- hooks/lib/openstack-common 2013-03-08 21:15:45 +0000 | |||
241 | +++ hooks/lib/openstack-common 2013-03-13 11:10:27 +0000 | |||
242 | @@ -321,18 +321,16 @@ | |||
243 | 321 | 321 | ||
244 | 322 | HAPROXY_CFG=/etc/haproxy/haproxy.cfg | 322 | HAPROXY_CFG=/etc/haproxy/haproxy.cfg |
245 | 323 | HAPROXY_DEFAULT=/etc/default/haproxy | 323 | HAPROXY_DEFAULT=/etc/default/haproxy |
246 | 324 | |||
247 | 325 | ########################################################################## | 324 | ########################################################################## |
248 | 326 | # Description: Configures HAProxy services for Openstack API's | 325 | # Description: Configures HAProxy services for Openstack API's |
249 | 327 | # Parameters: | 326 | # Parameters: |
251 | 328 | # Space delimited list of service:port combinations for which | 327 | # Space delimited list of service:port:mode combinations for which |
252 | 329 | # haproxy service configuration should be generated for. The function | 328 | # haproxy service configuration should be generated for. The function |
253 | 330 | # assumes the name of the peer relation is 'cluster' and that every | 329 | # assumes the name of the peer relation is 'cluster' and that every |
254 | 331 | # service unit in the peer relation is running the same services. | 330 | # service unit in the peer relation is running the same services. |
255 | 332 | # | 331 | # |
259 | 333 | # The HAProxy service will listen on port + 10000. | 332 | # Example |
260 | 334 | # Example: | 333 | # configure_haproxy cinder_api:8776:8756:tcp nova_api:8774:8764:http |
258 | 335 | # configure_haproxy cinder_api:12345 nova_api:9999 | ||
261 | 336 | ########################################################################## | 334 | ########################################################################## |
262 | 337 | configure_haproxy() { | 335 | configure_haproxy() { |
263 | 338 | local address=`unit-get private-address` | 336 | local address=`unit-get private-address` |
264 | @@ -354,8 +352,8 @@ | |||
265 | 354 | retries 3 | 352 | retries 3 |
266 | 355 | timeout queue 1000 | 353 | timeout queue 1000 |
267 | 356 | timeout connect 1000 | 354 | timeout connect 1000 |
270 | 357 | timeout client 10000 | 355 | timeout client 30000 |
271 | 358 | timeout server 10000 | 356 | timeout server 30000 |
272 | 359 | 357 | ||
273 | 360 | listen stats :8888 | 358 | listen stats :8888 |
274 | 361 | mode http | 359 | mode http |
275 | @@ -368,14 +366,20 @@ | |||
276 | 368 | EOF | 366 | EOF |
277 | 369 | for service in $@; do | 367 | for service in $@; do |
278 | 370 | local service_name=$(echo $service | cut -d : -f 1) | 368 | local service_name=$(echo $service | cut -d : -f 1) |
281 | 371 | local api_listen_port=$(echo $service | cut -d : -f 2) | 369 | local haproxy_listen_port=$(echo $service | cut -d : -f 2) |
282 | 372 | local haproxy_listen_port=$(($api_listen_port + 10000)) | 370 | local api_listen_port=$(echo $service | cut -d : -f 3) |
283 | 371 | local mode=$(echo $service | cut -d : -f 4) | ||
284 | 372 | juju-log "Adding haproxy configuration entry for $service "\ | ||
285 | 373 | "($haproxy_listen_port -> $api_listen_port)" | ||
286 | 373 | cat >> $HAPROXY_CFG << EOF | 374 | cat >> $HAPROXY_CFG << EOF |
287 | 374 | listen $service_name 0.0.0.0:$haproxy_listen_port | 375 | listen $service_name 0.0.0.0:$haproxy_listen_port |
288 | 375 | balance roundrobin | 376 | balance roundrobin |
290 | 376 | option tcplog | 377 | mode $mode |
291 | 378 | option ${mode}log | ||
292 | 377 | server $name $address:$api_listen_port check | 379 | server $name $address:$api_listen_port check |
293 | 378 | EOF | 380 | EOF |
294 | 381 | local r_id="" | ||
295 | 382 | local unit="" | ||
296 | 379 | for r_id in `relation-ids cluster`; do | 383 | for r_id in `relation-ids cluster`; do |
297 | 380 | for unit in `relation-list -r $r_id`; do | 384 | for unit in `relation-list -r $r_id`; do |
298 | 381 | local unit_name=${unit////-} | 385 | local unit_name=${unit////-} |
299 | @@ -388,6 +392,7 @@ | |||
300 | 388 | done | 392 | done |
301 | 389 | done | 393 | done |
302 | 390 | echo "ENABLED=1" > $HAPROXY_DEFAULT | 394 | echo "ENABLED=1" > $HAPROXY_DEFAULT |
303 | 395 | service haproxy restart | ||
304 | 391 | } | 396 | } |
305 | 392 | 397 | ||
306 | 393 | ########################################################################## | 398 | ########################################################################## |
307 | @@ -395,18 +400,20 @@ | |||
308 | 395 | # Returns: 0 if configured, 1 if not configured | 400 | # Returns: 0 if configured, 1 if not configured |
309 | 396 | ########################################################################## | 401 | ########################################################################## |
310 | 397 | is_clustered() { | 402 | is_clustered() { |
311 | 403 | local r_id="" | ||
312 | 404 | local unit="" | ||
313 | 398 | for r_id in $(relation-ids ha); do | 405 | for r_id in $(relation-ids ha); do |
314 | 399 | if [ -n "$r_id" ]; then | 406 | if [ -n "$r_id" ]; then |
315 | 400 | for unit in $(relation-list -r $r_id); do | 407 | for unit in $(relation-list -r $r_id); do |
316 | 401 | clustered=$(relation-get -r $r_id clustered $unit) | 408 | clustered=$(relation-get -r $r_id clustered $unit) |
317 | 402 | if [ -n "$clustered" ]; then | 409 | if [ -n "$clustered" ]; then |
319 | 403 | echo "Unit is clustered" | 410 | juju-log "Unit is haclustered" |
320 | 404 | return 0 | 411 | return 0 |
321 | 405 | fi | 412 | fi |
322 | 406 | done | 413 | done |
323 | 407 | fi | 414 | fi |
324 | 408 | done | 415 | done |
326 | 409 | echo "Unit is not clustered" | 416 | juju-log "Unit is not haclustered" |
327 | 410 | return 1 | 417 | return 1 |
328 | 411 | } | 418 | } |
329 | 412 | 419 | ||
330 | @@ -415,6 +422,7 @@ | |||
331 | 415 | ########################################################################## | 422 | ########################################################################## |
332 | 416 | peer_units() { | 423 | peer_units() { |
333 | 417 | local peers="" | 424 | local peers="" |
334 | 425 | local r_id="" | ||
335 | 418 | for r_id in $(relation-ids cluster); do | 426 | for r_id in $(relation-ids cluster); do |
336 | 419 | peers="$peers $(relation-list -r $r_id)" | 427 | peers="$peers $(relation-list -r $r_id)" |
337 | 420 | done | 428 | done |
338 | @@ -433,11 +441,11 @@ | |||
339 | 433 | echo "Comparing $JUJU_UNIT_NAME with peers: $peers" | 441 | echo "Comparing $JUJU_UNIT_NAME with peers: $peers" |
340 | 434 | local r_unit_no=$(echo $peer | cut -d / -f 2) | 442 | local r_unit_no=$(echo $peer | cut -d / -f 2) |
341 | 435 | if (($r_unit_no<$l_unit_no)); then | 443 | if (($r_unit_no<$l_unit_no)); then |
343 | 436 | echo "Not oldest peer; deferring" | 444 | juju-log "Not oldest peer; deferring" |
344 | 437 | return 1 | 445 | return 1 |
345 | 438 | fi | 446 | fi |
346 | 439 | done | 447 | done |
348 | 440 | echo "Oldest peer; might take charge?" | 448 | juju-log "Oldest peer; might take charge?" |
349 | 441 | return 0 | 449 | return 0 |
350 | 442 | } | 450 | } |
351 | 443 | 451 | ||
352 | @@ -451,13 +459,13 @@ | |||
353 | 451 | eligible_leader() { | 459 | eligible_leader() { |
354 | 452 | if is_clustered; then | 460 | if is_clustered; then |
355 | 453 | if ! is_leader $1; then | 461 | if ! is_leader $1; then |
357 | 454 | echo 'Deferring action to CRM leader' | 462 | juju-log 'Deferring action to CRM leader' |
358 | 455 | return 1 | 463 | return 1 |
359 | 456 | fi | 464 | fi |
360 | 457 | else | 465 | else |
361 | 458 | peers=$(peer_units) | 466 | peers=$(peer_units) |
362 | 459 | if [ -n "$peers" ] && ! oldest_peer "$peers"; then | 467 | if [ -n "$peers" ] && ! oldest_peer "$peers"; then |
364 | 460 | echo 'Deferring action to oldest service unit.' | 468 | juju-log 'Deferring action to oldest service unit.' |
365 | 461 | return 1 | 469 | return 1 |
366 | 462 | fi | 470 | fi |
367 | 463 | fi | 471 | fi |
368 | @@ -469,14 +477,14 @@ | |||
369 | 469 | # Returns: 0 if peered, 1 if not peered | 477 | # Returns: 0 if peered, 1 if not peered |
370 | 470 | ########################################################################## | 478 | ########################################################################## |
371 | 471 | is_peered() { | 479 | is_peered() { |
373 | 472 | r_id=$(relation-ids cluster) | 480 | local r_id=$(relation-ids cluster) |
374 | 473 | if [ -n "$r_id" ]; then | 481 | if [ -n "$r_id" ]; then |
375 | 474 | if [ -n "$(relation-list -r $r_id)" ]; then | 482 | if [ -n "$(relation-list -r $r_id)" ]; then |
377 | 475 | echo "Unit peered" | 483 | juju-log "Unit peered" |
378 | 476 | return 0 | 484 | return 0 |
379 | 477 | fi | 485 | fi |
380 | 478 | fi | 486 | fi |
382 | 479 | echo "Unit not peered" | 487 | juju-log "Unit not peered" |
383 | 480 | return 1 | 488 | return 1 |
384 | 481 | } | 489 | } |
385 | 482 | 490 | ||
386 | @@ -489,12 +497,207 @@ | |||
387 | 489 | hostname=`hostname` | 497 | hostname=`hostname` |
388 | 490 | if [ -x /usr/sbin/crm ]; then | 498 | if [ -x /usr/sbin/crm ]; then |
389 | 491 | if crm resource show $1 | grep -q $hostname; then | 499 | if crm resource show $1 | grep -q $hostname; then |
391 | 492 | echo "$hostname is cluster leader" | 500 | juju-log "$hostname is cluster leader." |
392 | 493 | return 0 | 501 | return 0 |
393 | 494 | fi | 502 | fi |
394 | 495 | fi | 503 | fi |
397 | 496 | echo "$hostname is not cluster leader" | 504 | juju-log "$hostname is not cluster leader." |
398 | 497 | return 1 | 505 | return 1 |
399 | 506 | } | ||
400 | 507 | |||
401 | 508 | ########################################################################## | ||
402 | 509 | # Description: Determines whether enough data has been provided in | ||
403 | 510 | # configuration or relation data to configure HTTPS. | ||
404 | 511 | # Parameters: None | ||
405 | 512 | # Returns: 0 if HTTPS can be configured, 1 if not. | ||
406 | 513 | ########################################################################## | ||
407 | 514 | https() { | ||
408 | 515 | local r_id="" | ||
409 | 516 | if [[ -n "$(config-get ssl_cert)" ]] && | ||
410 | 517 | [[ -n "$(config-get ssl_key)" ]] ; then | ||
411 | 518 | return 0 | ||
412 | 519 | fi | ||
413 | 520 | for r_id in $(relation-ids identity-service) ; do | ||
414 | 521 | for unit in $(relation-list -r $r_id) ; do | ||
415 | 522 | if [[ "$(relation-get -r $r_id https_keystone $unit)" == "True" ]] && | ||
416 | 523 | [[ -n "$(relation-get -r $r_id ssl_cert $unit)" ]] && | ||
417 | 524 | [[ -n "$(relation-get -r $r_id ssl_key $unit)" ]] && | ||
418 | 525 | [[ -n "$(relation-get -r $r_id ca_cert $unit)" ]] ; then | ||
419 | 526 | return 0 | ||
420 | 527 | fi | ||
421 | 528 | done | ||
422 | 529 | done | ||
423 | 530 | return 1 | ||
424 | 531 | } | ||
425 | 532 | |||
426 | 533 | ########################################################################## | ||
427 | 534 | # Description: For a given number of port mappings, configures apache2 | ||
428 | 535 | # HTTPs local reverse proxying using certficates and keys provided in | ||
429 | 536 | # either configuration data (preferred) or relation data. Assumes ports | ||
430 | 537 | # are not in use (calling charm should ensure that). | ||
431 | 538 | # Parameters: Variable number of proxy port mappings as | ||
432 | 539 | # $internal:$external. | ||
433 | 540 | # Returns: 0 if reverse proxy(s) have been configured, 0 if not. | ||
434 | 541 | ########################################################################## | ||
435 | 542 | enable_https() { | ||
436 | 543 | local port_maps="$@" | ||
437 | 544 | local http_restart="" | ||
438 | 545 | juju-log "Enabling HTTPS for port mappings: $port_maps." | ||
439 | 546 | |||
440 | 547 | # allow overriding of keystone provided certs with those set manually | ||
441 | 548 | # in config. | ||
442 | 549 | local cert=$(config-get ssl_cert) | ||
443 | 550 | local key=$(config-get ssl_key) | ||
444 | 551 | local ca_cert="" | ||
445 | 552 | if [[ -z "$cert" ]] || [[ -z "$key" ]] ; then | ||
446 | 553 | juju-log "Inspecting identity-service relations for SSL certificate." | ||
447 | 554 | local r_id="" | ||
448 | 555 | cert="" | ||
449 | 556 | key="" | ||
450 | 557 | ca_cert="" | ||
451 | 558 | for r_id in $(relation-ids identity-service) ; do | ||
452 | 559 | for unit in $(relation-list -r $r_id) ; do | ||
453 | 560 | [[ -z "$cert" ]] && cert="$(relation-get -r $r_id ssl_cert $unit)" | ||
454 | 561 | [[ -z "$key" ]] && key="$(relation-get -r $r_id ssl_key $unit)" | ||
455 | 562 | [[ -z "$ca_cert" ]] && ca_cert="$(relation-get -r $r_id ca_cert $unit)" | ||
456 | 563 | done | ||
457 | 564 | done | ||
458 | 565 | [[ -n "$cert" ]] && cert=$(echo $cert | base64 -di) | ||
459 | 566 | [[ -n "$key" ]] && key=$(echo $key | base64 -di) | ||
460 | 567 | [[ -n "$ca_cert" ]] && ca_cert=$(echo $ca_cert | base64 -di) | ||
461 | 568 | else | ||
462 | 569 | juju-log "Using SSL certificate provided in service config." | ||
463 | 570 | fi | ||
464 | 571 | |||
465 | 572 | [[ -z "$cert" ]] || [[ -z "$key" ]] && | ||
466 | 573 | juju-log "Expected but could not find SSL certificate data, not "\ | ||
467 | 574 | "configuring HTTPS!" && return 1 | ||
468 | 575 | |||
469 | 576 | apt-get -y install apache2 | ||
470 | 577 | a2enmod ssl proxy proxy_http | grep -v "To activate the new configuration" && | ||
471 | 578 | http_restart=1 | ||
472 | 579 | |||
473 | 580 | mkdir -p /etc/apache2/ssl/$CHARM | ||
474 | 581 | echo "$cert" >/etc/apache2/ssl/$CHARM/cert | ||
475 | 582 | echo "$key" >/etc/apache2/ssl/$CHARM/key | ||
476 | 583 | if [[ -n "$ca_cert" ]] ; then | ||
477 | 584 | juju-log "Installing Keystone supplied CA cert." | ||
478 | 585 | echo "$ca_cert" >/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt | ||
479 | 586 | update-ca-certificates --fresh | ||
480 | 587 | |||
481 | 588 | # XXX TODO: Find a better way of exporting this? | ||
482 | 589 | if [[ "$CHARM" == "nova-cloud-controller" ]] ; then | ||
483 | 590 | [[ -e /var/www/keystone_juju_ca_cert.crt ]] && | ||
484 | 591 | rm -rf /var/www/keystone_juju_ca_cert.crt | ||
485 | 592 | ln -s /usr/local/share/ca-certificates/keystone_juju_ca_cert.crt \ | ||
486 | 593 | /var/www/keystone_juju_ca_cert.crt | ||
487 | 594 | fi | ||
488 | 595 | |||
489 | 596 | fi | ||
490 | 597 | for port_map in $port_maps ; do | ||
491 | 598 | local ext_port=$(echo $port_map | cut -d: -f1) | ||
492 | 599 | local int_port=$(echo $port_map | cut -d: -f2) | ||
493 | 600 | juju-log "Creating apache2 reverse proxy vhost for $port_map." | ||
494 | 601 | cat >/etc/apache2/sites-available/${CHARM}_${ext_port} <<END | ||
495 | 602 | Listen $ext_port | ||
496 | 603 | NameVirtualHost *:$ext_port | ||
497 | 604 | <VirtualHost *:$ext_port> | ||
498 | 605 | ServerName $(unit-get private-address) | ||
499 | 606 | SSLEngine on | ||
500 | 607 | SSLCertificateFile /etc/apache2/ssl/$CHARM/cert | ||
501 | 608 | SSLCertificateKeyFile /etc/apache2/ssl/$CHARM/key | ||
502 | 609 | ProxyPass / http://localhost:$int_port/ | ||
503 | 610 | ProxyPassReverse / http://localhost:$int_port/ | ||
504 | 611 | ProxyPreserveHost on | ||
505 | 612 | </VirtualHost> | ||
506 | 613 | <Proxy *> | ||
507 | 614 | Order deny,allow | ||
508 | 615 | Allow from all | ||
509 | 616 | </Proxy> | ||
510 | 617 | <Location /> | ||
511 | 618 | Order allow,deny | ||
512 | 619 | Allow from all | ||
513 | 620 | </Location> | ||
514 | 621 | END | ||
515 | 622 | a2ensite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" && | ||
516 | 623 | http_restart=1 | ||
517 | 624 | done | ||
518 | 625 | if [[ -n "$http_restart" ]] ; then | ||
519 | 626 | service apache2 restart | ||
520 | 627 | fi | ||
521 | 628 | } | ||
522 | 629 | |||
523 | 630 | ########################################################################## | ||
524 | 631 | # Description: Ensure HTTPS reverse proxying is disabled for given port | ||
525 | 632 | # mappings. | ||
526 | 633 | # Parameters: Variable number of proxy port mappings as | ||
527 | 634 | # $internal:$external. | ||
528 | 635 | # Returns: 0 if reverse proxy is not active for all portmaps, 1 on error. | ||
529 | 636 | ########################################################################## | ||
530 | 637 | disable_https() { | ||
531 | 638 | local port_maps="$@" | ||
532 | 639 | local http_restart="" | ||
533 | 640 | juju-log "Ensuring HTTPS disabled for $port_maps." | ||
534 | 641 | ( [[ ! -d /etc/apache2 ]] || [[ ! -d /etc/apache2/ssl/$CHARM ]] ) && return 0 | ||
535 | 642 | for port_map in $port_maps ; do | ||
536 | 643 | local ext_port=$(echo $port_map | cut -d: -f1) | ||
537 | 644 | local int_port=$(echo $port_map | cut -d: -f2) | ||
538 | 645 | if [[ -e /etc/apache2/sites-available/${CHARM}_${ext_port} ]] ; then | ||
539 | 646 | juju-log "Disabling HTTPS reverse proxy for $CHARM $port_map." | ||
540 | 647 | a2dissite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" && | ||
541 | 648 | http_restart=1 | ||
542 | 649 | fi | ||
543 | 650 | done | ||
544 | 651 | if [[ -n "$http_restart" ]] ; then | ||
545 | 652 | service apache2 restart | ||
546 | 653 | fi | ||
547 | 654 | } | ||
548 | 655 | |||
549 | 656 | |||
550 | 657 | ########################################################################## | ||
551 | 658 | # Description: Ensures HTTPS is either enabled or disabled for given port | ||
552 | 659 | # mapping. | ||
553 | 660 | # Parameters: Variable number of proxy port mappings as | ||
554 | 661 | # $internal:$external. | ||
555 | 662 | # Returns: 0 if HTTPS reverse proxy is in place, 1 if it is not. | ||
556 | 663 | ########################################################################## | ||
557 | 664 | setup_https() { | ||
558 | 665 | # configure https via apache reverse proxying either | ||
559 | 666 | # using certs provided by config or keystone. | ||
560 | 667 | [[ -z "$CHARM" ]] && | ||
561 | 668 | error_out "setup_https(): CHARM not set." | ||
562 | 669 | if ! https ; then | ||
563 | 670 | disable_https $@ | ||
564 | 671 | else | ||
565 | 672 | enable_https $@ | ||
566 | 673 | fi | ||
567 | 674 | } | ||
568 | 675 | |||
569 | 676 | ########################################################################## | ||
570 | 677 | # Description: Determine correct API server listening port based on | ||
571 | 678 | # existence of HTTPS reverse proxy and/or haproxy. | ||
572 | 679 | # Paremeters: The standard public port for given service. | ||
573 | 680 | # Returns: The correct listening port for API service. | ||
574 | 681 | ########################################################################## | ||
575 | 682 | determine_api_port() { | ||
576 | 683 | local public_port="$1" | ||
577 | 684 | local i=0 | ||
578 | 685 | ( [[ -n "$(peer_units)" ]] || is_clustered >/dev/null 2>&1 ) && i=$[$i + 1] | ||
579 | 686 | https >/dev/null 2>&1 && i=$[$i + 1] | ||
580 | 687 | echo $[$public_port - $[$i * 10]] | ||
581 | 688 | } | ||
582 | 689 | |||
583 | 690 | ########################################################################## | ||
584 | 691 | # Description: Determine correct proxy listening port based on public IP + | ||
585 | 692 | # existence of HTTPS reverse proxy. | ||
586 | 693 | # Paremeters: The standard public port for given service. | ||
587 | 694 | # Returns: The correct listening port for haproxy service public address. | ||
588 | 695 | ########################################################################## | ||
589 | 696 | determine_haproxy_port() { | ||
590 | 697 | local public_port="$1" | ||
591 | 698 | local i=0 | ||
592 | 699 | https >/dev/null 2>&1 && i=$[$i + 1] | ||
593 | 700 | echo $[$public_port - $[$i * 10]] | ||
594 | 498 | } | 701 | } |
595 | 499 | 702 | ||
596 | 500 | ########################################################################## | 703 | ########################################################################## |
This update adds a few features:
1) HTTPS configuration options ssl_cert and ssl_key
HTTPS is on by default using snakeoil certs; this allows a user provided cert to be used.
2) Config options to
a) enable online compression (useful for use with trunk)
b) enable debugging
3) HA + HTTPS support
A little different in that Apache listens on 70/433 and haproxy fronts apache in full, using the tcp option for the SSL connections. This provides end-to-end SSL security.
General - I made this charm a little less bumpy by configuring the haproxy instance up-front; the cluster hook simply adds new nodes to the haproxy config using reload.