Merge lp:~therve/pyjuju/rapi-uuid into lp:~hazmat/pyjuju/rapi-rollup

Proposed by Kapil Thangavelu
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 624
Merged at revision: 626
Proposed branch: lp:~therve/pyjuju/rapi-uuid
Merge into: lp:~hazmat/pyjuju/rapi-rollup
Diff against target: 2304 lines (+511/-509)
65 files modified
examples/README (+0/-24)
examples/precise/mysql/config.yaml (+0/-1)
examples/precise/mysql/hooks/db-relation-joined (+0/-40)
examples/precise/mysql/hooks/install (+0/-32)
examples/precise/mysql/hooks/start (+0/-3)
examples/precise/mysql/hooks/stop (+0/-3)
examples/precise/mysql/metadata.yaml (+0/-17)
examples/precise/mysql/revision (+0/-1)
examples/precise/php/config.yaml (+0/-5)
examples/precise/php/hooks/config-changed (+0/-14)
examples/precise/php/hooks/install (+0/-49)
examples/precise/php/hooks/start (+0/-1)
examples/precise/php/hooks/stop (+0/-3)
examples/precise/php/metadata.yaml (+0/-4)
examples/precise/php/revision (+0/-1)
examples/precise/recorder/hooks/install (+0/-3)
examples/precise/recorder/hooks/juju-info-relation-changed (+0/-5)
examples/precise/recorder/hooks/juju-info-relation-departed (+0/-3)
examples/precise/recorder/hooks/juju-info-relation-joined (+0/-6)
examples/precise/recorder/metadata.yaml (+0/-9)
examples/precise/recorder/revision (+0/-1)
examples/precise/wordpress/config.yaml (+0/-7)
examples/precise/wordpress/hooks/config-changed (+0/-19)
examples/precise/wordpress/hooks/db-relation-changed (+0/-100)
examples/precise/wordpress/hooks/install (+0/-6)
examples/precise/wordpress/hooks/start (+0/-1)
examples/precise/wordpress/hooks/stop (+0/-3)
examples/precise/wordpress/metadata.yaml (+0/-10)
examples/precise/wordpress/revision (+0/-1)
juju/agents/api.py (+3/-2)
juju/agents/tests/test_unit.py (+1/-0)
juju/charm/tests/test_directory.py (+7/-6)
juju/environment/config.py (+3/-2)
juju/environment/tests/test_config.py (+0/-1)
juju/hooks/invoker.py (+5/-4)
juju/hooks/protocol.py (+5/-5)
juju/hooks/tests/test_executor.py (+3/-1)
juju/hooks/tests/test_invoker.py (+24/-20)
juju/machine/tests/data/test_get_container (+2/-0)
juju/machine/tests/test_unit_deployment.py (+13/-8)
juju/machine/unit.py (+12/-6)
juju/providers/common/cloudinit.py (+13/-3)
juju/providers/maas/maas.py (+13/-2)
juju/providers/maas/tests/test_maas.py (+22/-1)
juju/providers/openstack/client.py (+13/-5)
juju/providers/openstack/launch.py (+6/-2)
juju/providers/openstack/ports.py (+27/-10)
juju/providers/openstack/provider.py (+17/-0)
juju/providers/openstack/tests/__init__.py (+2/-2)
juju/providers/openstack/tests/test_client.py (+30/-0)
juju/providers/openstack/tests/test_launch.py (+48/-1)
juju/providers/openstack/tests/test_ports.py (+29/-4)
juju/providers/openstack/tests/test_provider.py (+29/-0)
juju/rapi/transport/tests/test_ws.py (+2/-2)
juju/rapi/transport/ws.py (+7/-3)
juju/state/environment.py (+10/-2)
juju/state/hook.py (+18/-7)
juju/state/initialize.py (+2/-0)
juju/state/tests/test_environment.py (+13/-0)
juju/state/tests/test_hook.py (+80/-15)
juju/state/tests/test_initialize.py (+2/-0)
juju/unit/deploy.py (+5/-2)
juju/unit/tests/test_address.py (+2/-7)
juju/unit/tests/test_deploy.py (+25/-10)
juju/unit/tests/test_lifecycle.py (+18/-4)
To merge this branch: bzr merge lp:~therve/pyjuju/rapi-uuid
Reviewer Review Type Date Requested Status
Kapil Thangavelu Approve
Review via email: mp+143663@code.launchpad.net

This proposal supersedes a proposal from 2013-01-17.

Description of the change

The branch merges the changes from trunk including the new environment UUID, and expose that UUID in the first API message.

[edit to point api rollup branch]

To post a comment you must log in.
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

looks good thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed directory 'examples'
2=== removed file 'examples/README'
3--- examples/README 2011-09-29 21:11:51 +0000
4+++ examples/README 1970-01-01 00:00:00 +0000
5@@ -1,24 +0,0 @@
6-Examples
7-========
8-
9-
10-These are some example charms that can be deployed together to create some connected services.
11-
12-Many more charms of greater functionality and utility exist in the Principia project, which
13-currently serves as a clearinghouse for juju charms.
14-
15-http://launchpad.net/principia
16-
17-
18-Deploying
19-=========
20-
21-As an example of deploying these sample charms as services and connectinng them.
22-
23- $ juju bootstrap
24- $ juju deploy --repository=examples local:mysql
25- $ juju deploy --repository=examples local:wordpress
26- $ juju add-relation wordpress mysql
27- $ juju status
28-
29-The status command will show the address of the newly deployed wordpress service.
30
31=== removed directory 'examples/precise'
32=== removed directory 'examples/precise/mysql'
33=== removed file 'examples/precise/mysql/config.yaml'
34--- examples/precise/mysql/config.yaml 2011-06-13 20:33:35 +0000
35+++ examples/precise/mysql/config.yaml 1970-01-01 00:00:00 +0000
36@@ -1,1 +0,0 @@
37-options: {}
38
39=== removed directory 'examples/precise/mysql/hooks'
40=== removed file 'examples/precise/mysql/hooks/db-relation-joined'
41--- examples/precise/mysql/hooks/db-relation-joined 2011-09-30 18:52:52 +0000
42+++ examples/precise/mysql/hooks/db-relation-joined 1970-01-01 00:00:00 +0000
43@@ -1,40 +0,0 @@
44-#!/bin/bash
45-
46-set -eu # -x for verbose logging to juju debug-log
47-
48-hostname=`unit-get private-address`
49-
50-# Get the mysql password that was generated by the install hook
51-password=`cat /var/lib/juju/mysql.passwd`
52-
53-# Get the database name; this takes a service unit (ex: wordpress/0)
54-# and extracts just 'wordpress'
55-service=`echo $JUJU_REMOTE_UNIT | cut -d/ -f1` # split on /
56-
57-# Determine if a database needs to be created for this service
58-existing_databases=`mysql --password="$password" --silent --execute 'show databases'`
59-for db in $existing_databases; do
60- if [ "$db" = "$service" ] ; then
61- juju-log "Database already exists, publishing details and exiting"
62- service_password=`cat /var/lib/juju/$service.passwd`
63- # Save these settings on the relation; this will trigger the remote
64- # service unit
65- relation-set database="$service" user="$service" password="$service_password"
66- exit 0 # database already exists
67- fi
68-done
69-
70-# Generate a strong password for the database, using /dev/urandom
71-service_password=`pwgen 10 1`
72-# Store service password, new service units of same service would need it
73-echo $service_password >> /var/lib/juju/$service.passwd
74-
75-# Create new database and corresponding security settings
76-juju-log "Creating new database and corresponding security settings"
77-mysqladmin --password="$password" create "$service"
78-echo "grant all on $service.* to $service identified by '$service_password'" | mysql --password="$password" --database="$service"
79-mysqladmin --password="$password" flush-privileges
80-
81-# Save these settings on the relation; this will trigger the remote
82-# service unit
83-relation-set database="$service" user="$service" password="$service_password"
84
85=== removed file 'examples/precise/mysql/hooks/install'
86--- examples/precise/mysql/hooks/install 2012-08-22 23:20:44 +0000
87+++ examples/precise/mysql/hooks/install 1970-01-01 00:00:00 +0000
88@@ -1,32 +0,0 @@
89-#!/bin/bash
90-
91-set -eu # -x for verbose logging to juju debug-log
92-
93-apt-get install -qqy debconf-utils python-mysqldb pwgen
94-
95-# Generate a strong password for the mysql service, using /dev/urandom
96-PASSWORD=`pwgen 10 1`
97-
98-# Store the password for later use by the db-relation-changed hook for
99-# this service unit. As a general note, for data that service units do
100-# not need to share, simply use the machine's local file store.
101-PASSFILE=/var/lib/juju/mysql.passwd
102-if ! [ -f $PASSFILE ] ; then
103- touch $PASSFILE
104-fi
105-chmod 0600 $PASSFILE
106-if ! [ -s $PASSFILE ] ; then
107- echo $PASSWORD >> /var/lib/juju/mysql.passwd
108-fi
109-
110-echo mysql-server-5.1 mysql-server/root_password password $PASSWORD | debconf-set-selections
111-echo mysql-server-5.1 mysql-server/root_password_again password $PASSWORD | debconf-set-selections
112-
113-juju-log "mysql-server settings preseeded, now installing via apt-get"
114-DEBIAN_FRONTEND=noninteractive apt-get -y install -qq mysql-server
115-
116-juju-log "Editing my.cnf to allow listening on all interfaces"
117-sed --in-place=old 's/127\.0\.0\.1/0.0.0.0/' /etc/mysql/my.cnf
118-
119-juju-log "Stopping mysql service"
120-service mysql stop
121
122=== removed file 'examples/precise/mysql/hooks/start'
123--- examples/precise/mysql/hooks/start 2011-11-28 16:29:37 +0000
124+++ examples/precise/mysql/hooks/start 1970-01-01 00:00:00 +0000
125@@ -1,3 +0,0 @@
126-#!/bin/bash
127-juju-log "Starting mysql service"
128-service mysql start || service mysql restart
129
130=== removed file 'examples/precise/mysql/hooks/stop'
131--- examples/precise/mysql/hooks/stop 2011-11-28 16:29:37 +0000
132+++ examples/precise/mysql/hooks/stop 1970-01-01 00:00:00 +0000
133@@ -1,3 +0,0 @@
134-#!/bin/bash
135-juju-log "Stopping mysql service"
136-service mysql stop || true
137
138=== removed file 'examples/precise/mysql/metadata.yaml'
139--- examples/precise/mysql/metadata.yaml 2011-10-04 19:35:07 +0000
140+++ examples/precise/mysql/metadata.yaml 1970-01-01 00:00:00 +0000
141@@ -1,17 +0,0 @@
142-name: mysql
143-summary: "MySQL relational database provider"
144-description: |
145- Installs and configures the MySQL package (mysqldb), then runs it.
146-
147- Upon a consuming service establishing a relation, creates a new
148- database for that service, if the database does not yet
149- exist. Publishes the following relation settings for consuming
150- services:
151-
152- database: database name
153- user: user name to access database
154- password: password to access the database
155- host: local hostname
156-provides:
157- db:
158- interface: mysql
159
160=== removed file 'examples/precise/mysql/revision'
161--- examples/precise/mysql/revision 2011-10-04 19:35:07 +0000
162+++ examples/precise/mysql/revision 1970-01-01 00:00:00 +0000
163@@ -1,1 +0,0 @@
164-11
165
166=== removed directory 'examples/precise/php'
167=== removed file 'examples/precise/php/config.yaml'
168--- examples/precise/php/config.yaml 2011-08-30 21:38:19 +0000
169+++ examples/precise/php/config.yaml 1970-01-01 00:00:00 +0000
170@@ -1,5 +0,0 @@
171-options:
172- application_file:
173- description: An application file to push.
174- type: string
175-
176
177=== removed directory 'examples/precise/php/hooks'
178=== removed file 'examples/precise/php/hooks/config-changed'
179--- examples/precise/php/hooks/config-changed 2011-09-30 18:54:44 +0000
180+++ examples/precise/php/hooks/config-changed 1970-01-01 00:00:00 +0000
181@@ -1,14 +0,0 @@
182-#!/bin/bash
183-
184-hostname=`unit-get public-address`
185-
186-app_dir="/var/www/$hostname/"
187-app_file=`config-get application_file`
188-
189-if [ -z $app_file ]; then
190- exit 0
191-fi
192-
193-echo "$app_file" > $app_dir/index.php
194-chmod a+r $app_dir/index.php
195-
196
197=== removed file 'examples/precise/php/hooks/install'
198--- examples/precise/php/hooks/install 2011-09-30 18:54:44 +0000
199+++ examples/precise/php/hooks/install 1970-01-01 00:00:00 +0000
200@@ -1,49 +0,0 @@
201-#!/bin/bash
202-
203-juju-log "Installing php5 via apt-get"
204-set -eu # -x for verbose logging to juju debug-log
205-
206-apt-get -y install php5
207-
208-# Create an internal secret key for wordpress; this is unrelated to
209-# the password generated for the admin user of wordpress
210-hostname=`unit-get public-address`
211-
212-SITE_PATH="/var/www/$hostname"
213-
214-
215-juju-log "Creating appropriate upload paths and directories"
216-# Setup appropriate upload paths and directories
217-mkdir -p $SITE_PATH
218-chown -R root:www-data $SITE_PATH
219-chmod -R a+rw $SITE_PATH
220-chmod a+x $SITE_PATH
221-
222-# Write the apache config
223-# XXX a future branch will change this to use augtool
224-apache_config_file_path="/etc/apache2/sites-available/$hostname"
225-juju-log "Writing apache config file $apache_config_file_path"
226-cat > $apache_config_file_path <<EOF
227-<VirtualHost *:80>
228- ServerName $hostname
229- DocumentRoot /var/www/$hostname
230- Options All
231- ErrorLog /var/log/apache2/$hostname-error.log
232- TransferLog /var/log/apache2/$hostname-access.log
233- <FilesMatch \.php$>
234- SetHandler application/x-httpd-php
235- </FilesMatch>
236-</VirtualHost>
237-EOF
238-chmod 0644 $apache_config_file_path
239-
240-# Configure apache
241-juju-log "Enabling apache modules: rewrite, vhost_alias"
242-a2enmod rewrite
243-a2enmod vhost_alias
244-juju-log "Enabling apache site: $hostname"
245-a2ensite $hostname
246-
247-# Restart apache
248-juju-log "Restarting apache2 service"
249-/etc/init.d/apache2 restart
250
251=== removed file 'examples/precise/php/hooks/start'
252--- examples/precise/php/hooks/start 2011-07-01 11:40:20 +0000
253+++ examples/precise/php/hooks/start 1970-01-01 00:00:00 +0000
254@@ -1,1 +0,0 @@
255-#!/bin/bash
256
257=== removed file 'examples/precise/php/hooks/stop'
258--- examples/precise/php/hooks/stop 2011-09-15 18:56:08 +0000
259+++ examples/precise/php/hooks/stop 1970-01-01 00:00:00 +0000
260@@ -1,3 +0,0 @@
261-#!/bin/bash
262-juju-log "Stopping apache"
263-/etc/init.d/apache2 stop
264
265=== removed file 'examples/precise/php/metadata.yaml'
266--- examples/precise/php/metadata.yaml 2011-10-04 19:35:07 +0000
267+++ examples/precise/php/metadata.yaml 1970-01-01 00:00:00 +0000
268@@ -1,4 +0,0 @@
269-name: php
270-summary: "php container"
271-description: |
272- PHP environment for your code.
273
274=== removed file 'examples/precise/php/revision'
275--- examples/precise/php/revision 2011-10-04 19:35:07 +0000
276+++ examples/precise/php/revision 1970-01-01 00:00:00 +0000
277@@ -1,1 +0,0 @@
278-5
279
280=== removed directory 'examples/precise/recorder'
281=== removed directory 'examples/precise/recorder/hooks'
282=== removed file 'examples/precise/recorder/hooks/install'
283--- examples/precise/recorder/hooks/install 2012-06-22 04:24:34 +0000
284+++ examples/precise/recorder/hooks/install 1970-01-01 00:00:00 +0000
285@@ -1,3 +0,0 @@
286-#!/bin/bash
287-# for testing
288-open-port 8080
289\ No newline at end of file
290
291=== removed file 'examples/precise/recorder/hooks/juju-info-relation-changed'
292--- examples/precise/recorder/hooks/juju-info-relation-changed 2012-03-21 12:54:28 +0000
293+++ examples/precise/recorder/hooks/juju-info-relation-changed 1970-01-01 00:00:00 +0000
294@@ -1,5 +0,0 @@
295-#!/bin/bash
296-set -x
297-
298-juju-log "Changed relation with $JUJU_REMOTE_UNIT"
299-juju-log `relation-list`
300
301=== removed file 'examples/precise/recorder/hooks/juju-info-relation-departed'
302--- examples/precise/recorder/hooks/juju-info-relation-departed 2012-03-21 12:54:28 +0000
303+++ examples/precise/recorder/hooks/juju-info-relation-departed 1970-01-01 00:00:00 +0000
304@@ -1,3 +0,0 @@
305-#!/bin/bash
306-
307-juju-log "Departed from $JUJU_REMOTE_UNIT"
308
309=== removed file 'examples/precise/recorder/hooks/juju-info-relation-joined'
310--- examples/precise/recorder/hooks/juju-info-relation-joined 2012-03-21 12:54:28 +0000
311+++ examples/precise/recorder/hooks/juju-info-relation-joined 1970-01-01 00:00:00 +0000
312@@ -1,6 +0,0 @@
313-#!/bin/bash
314-
315-set -x
316-
317-juju-log "Joined with $JUJU_REMOTE_UNIT"
318-juju-log `relation-list`
319
320=== removed file 'examples/precise/recorder/metadata.yaml'
321--- examples/precise/recorder/metadata.yaml 2012-03-14 22:07:28 +0000
322+++ examples/precise/recorder/metadata.yaml 1970-01-01 00:00:00 +0000
323@@ -1,9 +0,0 @@
324-name: recorder
325-summary: "Subordinate test charm"
326-description: |
327- Records which units can see each other to show how subordinates work.
328-subordinate: true
329-requires:
330- juju-info:
331- interface: juju-info
332- scope: container
333
334=== removed file 'examples/precise/recorder/revision'
335--- examples/precise/recorder/revision 2012-03-14 22:07:28 +0000
336+++ examples/precise/recorder/revision 1970-01-01 00:00:00 +0000
337@@ -1,1 +0,0 @@
338-1
339\ No newline at end of file
340
341=== removed directory 'examples/precise/wordpress'
342=== removed file 'examples/precise/wordpress/config.yaml'
343--- examples/precise/wordpress/config.yaml 2011-08-30 21:38:19 +0000
344+++ examples/precise/wordpress/config.yaml 1970-01-01 00:00:00 +0000
345@@ -1,7 +0,0 @@
346-options:
347- plugin:
348- description: |
349- The URL of a wordpress plugin which will be installed and made available in the Admin UI.
350- http://downloads.wordpress.org/plugin/buddypress.1.2.8.zip is a valid example.
351- type: string
352-
353
354=== removed directory 'examples/precise/wordpress/hooks'
355=== removed file 'examples/precise/wordpress/hooks/config-changed'
356--- examples/precise/wordpress/hooks/config-changed 2011-09-30 18:52:52 +0000
357+++ examples/precise/wordpress/hooks/config-changed 1970-01-01 00:00:00 +0000
358@@ -1,19 +0,0 @@
359-#!/bin/bash
360-
361-hostname=`unit-get public-address`
362-plugin_dir="/var/www/$hostname/wp-content/plugins"
363-plugin_url=`config-get plugin`
364-
365-base_plugin=`basename ${plugin_url}`
366-
367-cd /tmp
368-
369-curl -O ${plugin_url}
370-
371-if [ $? = 0 ]; then
372- cd ${plugin_dir}
373- unzip /tmp/${base_plugin}
374-else
375- juju-log -l WARNING "Unable to fetch ${plugin_url}"
376-
377-fi
378
379=== removed file 'examples/precise/wordpress/hooks/db-relation-changed'
380--- examples/precise/wordpress/hooks/db-relation-changed 2011-09-30 18:52:52 +0000
381+++ examples/precise/wordpress/hooks/db-relation-changed 1970-01-01 00:00:00 +0000
382@@ -1,100 +0,0 @@
383-#!/bin/bash
384-
385-set -eu # -x for verbose logging to juju debug-log
386-
387-UPLOAD_PATH="/var/www/wp-uploads"
388-
389-hostname=`unit-get public-address`
390-juju-log "Retrieved hostname: $hostname"
391-
392-# Check we haven't already been setup.
393-config_file_path="/etc/wordpress/config-$hostname.php"
394-if [ -f "$config_file_path" ] ; then
395- juju-log "Wordpress for site $config_file_path already Configured, exiting"
396- echo "Already Configured, Exiting"
397- exit 0 # already setup
398-fi
399-
400-# Get the database settings; if not set, wait for this hook to be
401-# invoked again
402-database=`relation-get database`
403-if [ -z "$database" ] ; then
404- exit 0 # wait for future handshake from database service unit
405-fi
406-
407-# Our protocol on this interface ensures that all or none of the
408-# settings are set. But we can verify the setting or the values if
409-# more error checking if desired.
410-user=`relation-get user`
411-password=`relation-get password`
412-host=`relation-get private-address`
413-
414-# Create an internal secret key for wordpress; this is unrelated to
415-# the password generated for the admin user of wordpress
416-secret_key=`pwgen 10 1`
417-
418-juju-log "Creating appropriate upload paths and directories"
419-# Setup appropriate upload paths and directories
420-ln -s /usr/share/wordpress "/var/www/$hostname"
421-mkdir -p $UPLOAD_PATH
422-mkdir -p "$UPLOAD_PATH/$hostname"
423-chown -R root:www-data $UPLOAD_PATH
424-chmod 0744 $UPLOAD_PATH
425-chmod 0770 "$UPLOAD_PATH/$hostname"
426-chown -R root:www-data "/var/www/$hostname/wp-content"
427-
428-juju-log "Writing wordpress config file $config_file_path"
429-# Write the wordpress config
430-cat > $config_file_path <<EOF
431-<?php
432-define('DB_NAME', '$database');
433-define('DB_USER', '$user');
434-define('DB_PASSWORD', '$password');
435-define('DB_HOST', '$host');
436-define('SECRET_KEY', '$secret_key');
437-
438-#This will disable the update notification.
439-define('WP_CORE_UPDATE', false);
440-
441-\$table_prefix = 'wp_';
442-\$server = '$host';
443-\$loginsql = '$user';
444-\$passsql = '$password';
445-\$base = '$database';
446-\$upload_path = '/var/www/wp-uploads/$hostname';
447-\$upload_url_path = 'http://$hostname/wp-uploads';
448-?>
449-EOF
450-chmod 0644 $config_file_path
451-
452-# Write the apache config
453-# XXX a future branch will change this to use augtool
454-apache_config_file_path="/etc/apache2/sites-available/$hostname"
455-juju-log "Writing apache config file $apache_config_file_path"
456-cat > $apache_config_file_path <<EOF
457-<VirtualHost *:80>
458- ServerName $hostname
459- DocumentRoot /var/www/$hostname
460- Options All
461- ErrorLog /var/log/apache2/wp-error.log
462- TransferLog /var/log/apache2/wp-access.log
463- # Store uploads in /var/www/wp-uploads/$0
464- RewriteEngine On
465- RewriteRule ^/wp-uploads/(.*)$ /var/www/wp-uploads/%%{HTTP_HOST}/\$1
466-</VirtualHost>
467-EOF
468-chmod 0644 $apache_config_file_path
469-
470-# Configure apache
471-juju-log "Enabling apache modules: rewrite, vhost_alias"
472-a2enmod rewrite
473-a2enmod vhost_alias
474-juju-log "Enabling apache site: $hostname"
475-a2ensite $hostname
476-
477-# Restart apache
478-juju-log "Restarting apache2 service"
479-/etc/init.d/apache2 restart
480-
481-# Make it publicly visible, once the wordpress service is exposed
482-open-port 80/tcp
483
484=== removed file 'examples/precise/wordpress/hooks/install'
485--- examples/precise/wordpress/hooks/install 2011-09-30 01:46:14 +0000
486+++ examples/precise/wordpress/hooks/install 1970-01-01 00:00:00 +0000
487@@ -1,6 +0,0 @@
488-#!/bin/bash
489-
490-juju-log "Installing wordpress, pwgen via apt-get"
491-set -eu # -x for verbose logging to juju debug-log
492-
493-apt-get -y install wordpress pwgen unzip
494
495=== removed file 'examples/precise/wordpress/hooks/start'
496--- examples/precise/wordpress/hooks/start 2011-02-03 01:23:43 +0000
497+++ examples/precise/wordpress/hooks/start 1970-01-01 00:00:00 +0000
498@@ -1,1 +0,0 @@
499-#!/bin/bash
500
501=== removed file 'examples/precise/wordpress/hooks/stop'
502--- examples/precise/wordpress/hooks/stop 2011-09-15 18:56:08 +0000
503+++ examples/precise/wordpress/hooks/stop 1970-01-01 00:00:00 +0000
504@@ -1,3 +0,0 @@
505-#!/bin/bash
506-juju-log "Stopping apache"
507-/etc/init.d/apache2 stop
508
509=== removed file 'examples/precise/wordpress/metadata.yaml'
510--- examples/precise/wordpress/metadata.yaml 2011-10-04 19:35:07 +0000
511+++ examples/precise/wordpress/metadata.yaml 1970-01-01 00:00:00 +0000
512@@ -1,10 +0,0 @@
513-name: wordpress
514-summary: "WordPress blog"
515-description: |
516- Installs WordPress package (wordpress). Upon the database provider
517- providing the required database, and the relation settings
518- necessary to access it, completes the configuration of WordPress
519- and makes it available on the web.
520-requires:
521- db:
522- interface: mysql
523
524=== removed file 'examples/precise/wordpress/revision'
525--- examples/precise/wordpress/revision 2011-10-04 19:35:07 +0000
526+++ examples/precise/wordpress/revision 1970-01-01 00:00:00 +0000
527@@ -1,1 +0,0 @@
528-31
529
530=== modified file 'juju/agents/api.py'
531--- juju/agents/api.py 2012-12-18 01:15:02 +0000
532+++ juju/agents/api.py 2013-01-17 11:10:30 +0000
533@@ -50,10 +50,11 @@
534
535 self.environment = yield self.configure_environment()
536 self.provider = yield self._get_provider()
537+ env_uuid = yield self.global_settings_state.get_environment_id()
538 log.debug("Received environment data.")
539 root = Data("Juju API Server\n", "text/plain")
540 self.ws_factory = WebSocketAPIFactory(
541- self.config['zookeeper_servers'], self.provider)
542+ self.config['zookeeper_servers'], self.provider, env_uuid)
543 yield self.ws_factory.startFactory()
544
545 root.putChild('ws', WebSocketsResource(self.ws_factory))
546@@ -151,7 +152,7 @@
547 "--port", default=default_port, type=int)
548 parser.add_argument("--secure", action='store_true')
549 parser.add_argument("--keys", default=os.getenv('JUJU_API_KEYS'),
550- type=keys_directory, metavar='PATH')
551+ type=keys_directory, metavar='PATH')
552 return parser
553
554 if __name__ == '__main__':
555
556=== modified file 'juju/agents/tests/test_unit.py'
557--- juju/agents/tests/test_unit.py 2012-09-10 03:20:20 +0000
558+++ juju/agents/tests/test_unit.py 2013-01-17 11:10:30 +0000
559@@ -35,6 +35,7 @@
560 yield settings.set_provider_type("dummy")
561 self.change_environment(
562 PATH=get_cli_environ_path(),
563+ JUJU_ENV_UUID="snowflake",
564 JUJU_UNIT_NAME="mysql/0")
565
566 @inlineCallbacks
567
568=== modified file 'juju/charm/tests/test_directory.py'
569--- juju/charm/tests/test_directory.py 2012-09-14 15:33:33 +0000
570+++ juju/charm/tests/test_directory.py 2013-01-17 11:10:30 +0000
571@@ -3,7 +3,6 @@
572 import hashlib
573 import inspect
574 import shutil
575-import tempfile
576 import zipfile
577
578 from juju.errors import CharmError, FileNotFound
579@@ -111,12 +110,12 @@
580 def test_make_archive(self):
581 # make archive from sample directory
582 directory = CharmDirectory(sample_directory)
583- f = tempfile.NamedTemporaryFile(suffix=".charm")
584- directory.make_archive(f.name)
585+ f = self.makeFile(suffix=".charm")
586+ directory.make_archive(f)
587
588 # open archive in .zip-format and assert integrity
589 from zipfile import ZipFile
590- zf = ZipFile(f.name)
591+ zf = ZipFile(f)
592 self.assertEqual(zf.testzip(), None)
593
594 # assert included
595@@ -212,7 +211,8 @@
596
597 def test_internal_symlink(self):
598 charm_path = self.copy_charm()
599- os.symlink("/etc/lsb-release", os.path.join(charm_path, "foobar"))
600+ external_file = self.makeFile(content='baz')
601+ os.symlink(external_file, os.path.join(charm_path, "foobar"))
602
603 directory = CharmDirectory(charm_path)
604 e = self.assertRaises(InvalidCharmFile, directory.as_bundle)
605@@ -220,7 +220,8 @@
606
607 def test_extract_symlink(self):
608 charm_path = self.copy_charm()
609- os.symlink("/etc/lsb-release", os.path.join(charm_path, "foobar"))
610+ external_file = self.makeFile(content='lorem ipsum')
611+ os.symlink(external_file, os.path.join(charm_path, "foobar"))
612
613 directory = CharmDirectory(charm_path)
614 e = self.assertRaises(InvalidCharmFile, directory.as_bundle)
615
616=== modified file 'juju/environment/config.py'
617--- juju/environment/config.py 2012-09-20 15:11:08 +0000
618+++ juju/environment/config.py 2013-01-17 11:10:30 +0000
619@@ -76,7 +76,7 @@
620 optional=[
621 "access-key", "secret-key", "auth-url", "project-name",
622 "auth-mode", "region", "use-floating-ip",
623- "ssl-hostname-verification"]),
624+ "ssl-hostname-verification", "default-instance-type"]),
625 "openstack_s3": KeyDict({
626 "control-bucket": String(),
627 "admin-secret": String(),
628@@ -96,7 +96,8 @@
629 optional=[
630 "access-key", "secret-key", "combined-key", "auth-url",
631 "s3-uri", "project-name", "auth-mode", "region",
632- "use-floating-ip", "ssl-hostname-verification"]),
633+ "use-floating-ip", "ssl-hostname-verification",
634+ "default-instance-type"]),
635 "orchestra": KeyDict({
636 "orchestra-server": String(),
637 "orchestra-user": String(),
638
639=== modified file 'juju/environment/tests/test_config.py'
640--- juju/environment/tests/test_config.py 2012-09-27 04:36:51 +0000
641+++ juju/environment/tests/test_config.py 2013-01-17 11:10:30 +0000
642@@ -67,7 +67,6 @@
643 admin-secret: sekret
644 control-bucket: container
645 default-image-id: 42
646- default-instance-type: m1-sample
647 default-series: precise
648 """
649
650
651=== modified file 'juju/hooks/invoker.py'
652--- juju/hooks/invoker.py 2012-09-28 16:22:27 +0000
653+++ juju/hooks/invoker.py 2013-01-17 11:10:30 +0000
654@@ -50,7 +50,7 @@
655 exit_code = reason.value.exitCode
656 if exit_code == 0:
657 return deferred.callback(exit_code)
658- elif exit_code == None and reason.value.signal:
659+ elif exit_code is None and reason.value.signal:
660 error = errors.CharmInvocationError(
661 self._hook_name, exit_code, signal=reason.value.signal)
662 else:
663@@ -215,6 +215,7 @@
664 JUJU_CLIENT_ID=self._client_id,
665 CHARM_DIR=os.path.join(self._unit_path, "charm"),
666 _JUJU_CHARM_FORMAT=str(self.charm_format),
667+ JUJU_ENV_UUID=os.environ["JUJU_ENV_UUID"],
668 JUJU_UNIT_NAME=os.environ["JUJU_UNIT_NAME"],
669 DEBIAN_FRONTEND="noninteractive",
670 APT_LISTCHANGES_FRONTEND="none",
671@@ -234,8 +235,8 @@
672 """Returns the hook context for the invocation."""
673 return self._context
674
675- def get_relation_hook_context(self, relation_ident):
676- """Returns a hook context corresponding to `relation_ident`"""
677+ def get_cached_relation_hook_context(self, relation_ident):
678+ """Returns cached hook context corresponding to `relation_ident`"""
679 try:
680 return self._relation_contexts[relation_ident]
681 except KeyError:
682@@ -255,7 +256,7 @@
683
684 if not os.access(hook_filename, os.X_OK):
685 raise errors.CharmError(hook_filename,
686- "hook is not executable")
687+ "hook is not executable")
688
689 def send_signal(self, signal_id):
690 """Send a signal of the given signal_id.
691
692=== modified file 'juju/hooks/protocol.py'
693--- juju/hooks/protocol.py 2012-09-28 16:22:27 +0000
694+++ juju/hooks/protocol.py 2013-01-17 11:10:30 +0000
695@@ -206,7 +206,7 @@
696 if relation_id:
697 yield self.factory.log(
698 logging.DEBUG, "Getting relation %s" % relation_id)
699- context = yield invoker.get_relation_hook_context(relation_id)
700+ context = invoker.get_cached_relation_hook_context(relation_id)
701 require_relation_context(context)
702
703 try:
704@@ -238,7 +238,7 @@
705 if relation_id:
706 yield self.factory.log(
707 logging.DEBUG, "Setting relation %s" % relation_id)
708- context = yield invoker.get_relation_hook_context(relation_id)
709+ context = invoker.get_cached_relation_hook_context(relation_id)
710 require_relation_context(context)
711
712 for k, v in data.items():
713@@ -256,8 +256,8 @@
714 if relation_id:
715 yield self.factory.log(
716 logging.DEBUG, "Listing relation members for %s" % relation_id)
717- context = yield self.factory.get_invoker(client_id).\
718- get_relation_hook_context(relation_id)
719+ invoker = yield self.factory.get_invoker(client_id)
720+ context = invoker.get_cached_relation_hook_context(relation_id)
721 require_relation_context(context)
722 members = yield context.get_members()
723 defer.returnValue(dict(members=" ".join(members)))
724@@ -265,7 +265,7 @@
725 @RelationIdsCommand.responder
726 @defer.inlineCallbacks
727 def relation_ids(self, client_id, relation_name):
728- """Set values into state.hook.RelationHookContext.
729+ """Get relation idents for this hook context.
730
731 :client_id: hooks client id that is used to define a context
732 for a consistent view of state.
733
734=== modified file 'juju/hooks/tests/test_executor.py'
735--- juju/hooks/tests/test_executor.py 2011-09-15 18:50:23 +0000
736+++ juju/hooks/tests/test_executor.py 2013-01-17 11:10:30 +0000
737@@ -207,7 +207,9 @@
738 log = logging.getLogger("invoker")
739 # Populate environment variables for default invoker.
740 self.change_environment(
741- JUJU_UNIT_NAME="dummy/1", PATH="/bin/:/usr/bin")
742+ JUJU_UNIT_NAME="dummy/1",
743+ JUJU_ENV_UUID="snowflake",
744+ PATH="/bin/:/usr/bin")
745 output = self.capture_logging("invoker", level=logging.DEBUG)
746 invoker = Invoker(
747 None, None, "constant", self.makeFile(), unit_dir, log)
748
749=== modified file 'juju/hooks/tests/test_invoker.py'
750--- juju/hooks/tests/test_invoker.py 2012-10-01 03:44:42 +0000
751+++ juju/hooks/tests/test_invoker.py 2013-01-17 11:10:30 +0000
752@@ -178,6 +178,7 @@
753 "hooks")
754 self.change_environment(
755 PATH=get_cli_environ_path(test_hook_path, "/usr/bin", "/bin"),
756+ JUJU_ENV_UUID="snowflake",
757 JUJU_UNIT_NAME=local_unit,
758 JUJU_REMOTE_UNIT=remote_unit)
759
760@@ -268,8 +269,8 @@
761 state["relations"]["mysql"][0])
762 wpr = yield self.relation_state_manager.get_relations_for_service(
763 state["services"]["wordpress"])
764- wpr = [r for r in wpr if r.internal_relation_id == \
765- self.mysql_relation.internal_relation_id][0]
766+ wpr = [r for r in wpr if r.internal_relation_id ==
767+ self.mysql_relation.internal_relation_id][0]
768 self.wordpress_relation = yield wpr.add_unit_state(
769 state["relations"]["wordpress"][0])
770
771@@ -289,9 +290,10 @@
772 yield self.wordpress_relation.set_data({"hello": "world"})
773
774 hook_log = self.capture_logging("hook")
775- exe = yield self.ua.get_invoker("db:42", "add", "mysql/0",
776- self.mysql_relation,
777- client_id="client_id")
778+ exe = yield self.ua.get_invoker(
779+ "db:42", "add", "mysql/0",
780+ self.mysql_relation,
781+ client_id="client_id")
782
783 yield exe(self.create_hook(
784 "relation-get", "--format=json - wordpress/0"))
785@@ -542,6 +544,7 @@
786 # dummy charm (this is the default charm used when the
787 # add_service method is use)
788 self.assertEqual(env["_JUJU_CHARM_FORMAT"], "1")
789+ self.assertEqual(env["JUJU_ENV_UUID"], "snowflake")
790
791 @defer.inlineCallbacks
792 def test_missing_hook(self):
793@@ -1016,7 +1019,7 @@
794 self.assertEqual(
795 implied,
796 self.ua.server_factory.get_invoker("client_id").\
797- get_relation_hook_context("db:0"))
798+ get_cached_relation_hook_context("db:0"))
799 self.assertEqual(
800 set((yield implied.get_relation_idents("db"))),
801 set(["db:0", "db:1"]))
802@@ -1031,32 +1034,32 @@
803 # Add another relation, verify it's not yet visible
804 yield self.add_a_blog("wordpress3")
805
806- db0 = yield exe.get_relation_hook_context("db:0")
807- db1 = yield exe.get_relation_hook_context("db:1")
808+ db0 = exe.get_cached_relation_hook_context("db:0")
809+ db1 = exe.get_cached_relation_hook_context("db:1")
810 self.assertEqual(db1.relation_ident, "db:1")
811 self.assertEqual(
812 set((yield db1.get_relation_idents("db"))),
813 set(["db:0", "db:1"]))
814 self.assertEqual(
815 db1,
816- exe.get_relation_hook_context("db:1"))
817+ exe.get_cached_relation_hook_context("db:1"))
818
819 # Not yet visible relation
820 self.assertRaises(
821 RelationStateNotFound,
822- exe.get_relation_hook_context, "db:2")
823+ exe.get_cached_relation_hook_context, "db:2")
824
825 # Nonexistent relation idents
826 self.assertRaises(
827 RelationStateNotFound,
828- exe.get_relation_hook_context, "db:12345")
829+ exe.get_cached_relation_hook_context, "db:12345")
830
831 # Reread parent and child contexts
832 exe = yield self.ua.get_invoker(
833 "db:0", "add", "mysql/0", self.relation, client_id="client_id")
834 db0 = yield exe.get_context()
835- db1 = yield exe.get_relation_hook_context("db:1")
836- db2 = yield exe.get_relation_hook_context("db:2")
837+ db1 = exe.get_cached_relation_hook_context("db:1")
838+ db2 = exe.get_cached_relation_hook_context("db:2")
839
840 # Verify that any changes are written out; write directly here
841 # using the relation contexts
842@@ -1107,9 +1110,9 @@
843 set((yield exe.get_context().get_relation_idents("db"))),
844 set(["db:0", "db:1", "db:2"]))
845
846- db0 = yield exe.get_relation_hook_context("db:0")
847- db1 = yield exe.get_relation_hook_context("db:1")
848- db2 = yield exe.get_relation_hook_context("db:2")
849+ db0 = exe.get_cached_relation_hook_context("db:0")
850+ db1 = exe.get_cached_relation_hook_context("db:1")
851+ db2 = exe.get_cached_relation_hook_context("db:2")
852
853 # Verify that any changes are written out; write directly here
854 # using the relation contexts
855@@ -1152,8 +1155,9 @@
856 self.assertEqual(
857 set((yield exe.get_context().get_relation_idents("db"))),
858 set(["db:0", "db:2"]))
859- yield self.assertFailure((yield exe.get_relation_hook_context("db:1")),
860- RelationStateNotFound)
861+ yield self.assertRaises(
862+ RelationStateNotFound,
863+ exe.get_cached_relation_hook_context, "db:1")
864
865 @defer.inlineCallbacks
866 def test_relation_ids(self):
867@@ -1288,7 +1292,7 @@
868 self.assertEqual(result, 0)
869 yield exe.ended
870
871- db1 = exe.get_relation_hook_context("db:1")
872+ db1 = exe.get_cached_relation_hook_context("db:1")
873 yield self.assert_zk_data(db1, {
874 "a": "42",
875 "b": "xyz",
876@@ -1363,7 +1367,7 @@
877 client_id="client_id")
878
879 # First set through the context
880- db1 = exe.get_relation_hook_context("db:1")
881+ db1 = exe.get_cached_relation_hook_context("db:1")
882 yield db1.set({"name": "whiterabbit"})
883
884 # Then get from db:1
885
886=== modified file 'juju/machine/tests/data/test_get_container'
887--- juju/machine/tests/data/test_get_container 2012-10-09 18:55:55 +0000
888+++ juju/machine/tests/data/test_get_container 2013-01-17 11:10:30 +0000
889@@ -25,6 +25,8 @@
890 respawn
891
892
893+ env JUJU_ENV_UUID="snowflake"
894+
895 env JUJU_HOME="/var/lib/juju"
896
897 env JUJU_MACHINE_ID="None"
898
899=== modified file 'juju/machine/tests/test_unit_deployment.py'
900--- juju/machine/tests/test_unit_deployment.py 2012-10-05 13:55:09 +0000
901+++ juju/machine/tests/test_unit_deployment.py 2013-01-17 11:10:30 +0000
902@@ -23,6 +23,7 @@
903
904 DATA_DIR = os.path.join(os.path.dirname(inspect.getabsfile(tests)), "data")
905
906+
907 class UnitMachineDeploymentTest(RepositoryTestBase):
908
909 def setUp(self):
910@@ -110,7 +111,7 @@
911 self.mocker.replay()
912
913 d = self.deployment.start(
914- "123", get_test_zookeeper_address(), self.bundle)
915+ "snowflake", "123", get_test_zookeeper_address(), self.bundle)
916
917 def verify_upstart(_):
918 conf_path = os.path.join(self.init_dir, "juju-wordpress-0.conf")
919@@ -130,6 +131,7 @@
920 "JUJU_HOME": self.juju_directory,
921 "JUJU_UNIT_NAME": self.unit_name,
922 "JUJU_ZOOKEEPER": get_test_zookeeper_address(),
923+ "JUJU_ENV_UUID": "snowflake",
924 "JUJU_MACHINE_ID": "123"})
925
926 log_file = os.path.join(
927@@ -158,7 +160,7 @@
928 self.mocker.replay()
929
930 yield self.deployment.start(
931- "0", get_test_zookeeper_address(), self.bundle)
932+ "snowflake", "0", get_test_zookeeper_address(), self.bundle)
933
934 yield self.deployment.destroy()
935 self.assertFalse(os.path.exists(self.deployment.directory))
936@@ -190,7 +192,7 @@
937 self.mocker.replay()
938
939 yield self.deployment.start(
940- "0", get_test_zookeeper_address(), self.bundle)
941+ "snowflake", "0", get_test_zookeeper_address(), self.bundle)
942 yield self.deployment.destroy()
943 self.assertFalse(os.path.exists(self.deployment.directory))
944
945@@ -263,7 +265,7 @@
946 # "unpatch" to use real /etc/init
947 self.patch(UpstartService, "init_dir", self.real_init_dir)
948 yield self.deployment.start(
949- "0", get_test_zookeeper_address(), self.bundle)
950+ "snowflake", "0", get_test_zookeeper_address(), self.bundle)
951 old_pid = yield self.deployment.get_pid()
952 self.assert_pid_running(old_pid, True)
953
954@@ -292,7 +294,7 @@
955 self.patch(UpstartService, "init_dir", self.real_init_dir)
956
957 d = self.deployment.start(
958- "0", get_test_zookeeper_address(), self.bundle)
959+ "snowflake", "0", get_test_zookeeper_address(), self.bundle)
960 e = yield self.assertFailure(d, UnitDeploymentError)
961 self.assertTrue(str(e).startswith(
962 "Failed to start job juju-wordpress-0; got output:\n"))
963@@ -366,8 +368,10 @@
964 mock_deploy = self.mocker.patch(self.unit_deploy)
965 # this minimally validates that we are also called with the
966 # expect public key
967+
968 def is_cloudinit(obj):
969 return isinstance(obj, CloudInit)
970+
971 mock_deploy._get_container("0", MATCH(is_cloudinit))
972 self.mocker.result((container, rootfs))
973
974@@ -379,7 +383,8 @@
975 self.unit_deploy.directory = rootfs
976 os.makedirs(os.path.join(rootfs, "etc", "init"))
977
978- yield self.unit_deploy.start("0", "127.0.1.1:2181", self.bundle)
979+ yield self.unit_deploy.start(
980+ "snowflake", "0", "127.0.1.1:2181", self.bundle)
981
982 # Verify the symlinks exist
983 self.assertTrue(os.path.lexists(os.path.join(
984@@ -410,9 +415,9 @@
985 def test_get_container(self):
986 rootfs = self.makeDir()
987 cloud_init = self.unit_deploy._get_cloud_init(zookeepers='localhost')
988-
989+ cloud_init.set_environment_id('snowflake')
990 expected = serializer.load(
991- open(os.path.join(DATA_DIR, 'test_get_container')))
992+ open(os.path.join(DATA_DIR, 'test_get_container')))
993 rendered = serializer.load(cloud_init.render())
994 self.assertEquals(rendered, expected)
995
996
997=== modified file 'juju/machine/unit.py'
998--- juju/machine/unit.py 2012-09-27 00:42:32 +0000
999+++ juju/machine/unit.py 2013-01-17 11:10:30 +0000
1000@@ -8,7 +8,7 @@
1001
1002 from juju.charm.bundle import CharmBundle
1003 from juju.errors import ServiceError
1004-from juju.lib.lxc import LXCContainer, get_containers, LXCError
1005+from juju.lib.lxc import LXCContainer, get_containers
1006 from juju.lib.twistutils import get_module_directory
1007 from juju.lib.upstart import UpstartService
1008 from juju.providers.common.cloudinit import CloudInit
1009@@ -27,12 +27,17 @@
1010 return UnitMachineDeployment
1011
1012
1013-def _get_environment(unit_name, juju_home, machine_id, zookeeper_hosts):
1014+def _get_environment(unit_name,
1015+ juju_home, machine_id, zookeeper_hosts, env_id):
1016+ """
1017+ Return environment dictionary for unit.
1018+ """
1019 environ = dict()
1020 environ["JUJU_MACHINE_ID"] = str(machine_id)
1021 environ["JUJU_UNIT_NAME"] = unit_name
1022 environ["JUJU_HOME"] = juju_home
1023 environ["JUJU_ZOOKEEPER"] = zookeeper_hosts
1024+ environ["JUJU_ENV_UUID"] = env_id
1025 environ["PYTHONPATH"] = ":".join(
1026 filter(None, [
1027 os.path.dirname(get_module_directory(juju)),
1028@@ -66,13 +71,14 @@
1029 "juju-%s" % self.unit_path_name, use_sudo=True)
1030
1031 @inlineCallbacks
1032- def start(self, machine_id, zookeeper_hosts, bundle):
1033+ def start(self, env_id, machine_id, zookeeper_hosts, bundle):
1034 """Start a service unit agent."""
1035 self.unpack_charm(bundle)
1036 self.service.set_description(
1037 "Juju unit agent for %s" % self.unit_name)
1038 self.service.set_environ(_get_environment(
1039- self.unit_name, self.juju_home, machine_id, zookeeper_hosts))
1040+ self.unit_name, self.juju_home, machine_id,
1041+ zookeeper_hosts, env_id))
1042 self.service.set_command(" ".join((
1043 "/usr/bin/python", "-m", self.unit_agent_module,
1044 "--nodaemon",
1045@@ -189,7 +195,7 @@
1046 (zk, port) = zk.split(':')
1047 else:
1048 port = 2181
1049- zks.append((zk,port))
1050+ zks.append((zk, port))
1051
1052 cloud_init.set_zookeeper_hosts(zks)
1053 # XXX Very hard to access the provider's notion of network
1054@@ -228,7 +234,7 @@
1055 returnValue((container, directory))
1056
1057 @inlineCallbacks
1058- def start(self, machine_id, zookeeper_hosts, bundle):
1059+ def start(self, env_id, machine_id, zookeeper_hosts, bundle):
1060 """Start the unit.
1061
1062 Creates and starts an lxc container for the unit.
1063
1064=== modified file 'juju/providers/common/cloudinit.py'
1065--- juju/providers/common/cloudinit.py 2012-10-20 19:23:19 +0000
1066+++ juju/providers/common/cloudinit.py 2013-01-17 11:10:30 +0000
1067@@ -56,7 +56,8 @@
1068 "--session-file /var/run/juju/machine-agent.zksession")
1069 return service.get_cloud_init_commands()
1070
1071-def _unit_scripts(machine_id, unit_name, zookeeper_hosts, juju_home):
1072+
1073+def _unit_scripts(machine_id, unit_name, zookeeper_hosts, juju_home, env_id):
1074 unit_path_name = unit_name.replace('/', '-')
1075 service_name = "juju-%s" % unit_path_name
1076 service = UpstartService(service_name)
1077@@ -66,6 +67,7 @@
1078 {"JUJU_MACHINE_ID": str(machine_id),
1079 "JUJU_UNIT_NAME": unit_name,
1080 "JUJU_HOME": juju_home,
1081+ "JUJU_ENV_UUID": env_id,
1082 "JUJU_ZOOKEEPER": zookeeper_hosts})
1083 service.set_output_path(
1084 "/var/log/juju/unit-%s-output.log" % unit_path_name)
1085@@ -78,6 +80,7 @@
1086 "/var/run/juju/unit-%s-agent.zksession" % unit_path_name]))
1087 return service.get_cloud_init_commands()
1088
1089+
1090 def _apiserver_scripts(zookeeper_hosts):
1091 service = UpstartService("juju-api-agent")
1092 service.set_description("Juju api agent")
1093@@ -186,6 +189,7 @@
1094 self._unit_name = None
1095 self._juju_home = None
1096 self._apt_proxy = None
1097+ self._env_id = None
1098
1099 def add_ssh_key(self, key):
1100 """Add an SSH public key.
1101@@ -239,6 +243,11 @@
1102 self._origin = PROPOSED
1103 self._origin_url = None
1104
1105+ def set_environment_id(self, id):
1106+ """Specify the environment id.
1107+ """
1108+ self._env_id = id
1109+
1110 def set_machine_id(self, id):
1111 """Specify the juju machine ID.
1112
1113@@ -314,7 +323,7 @@
1114
1115 def set_apt_proxy(self, proxy):
1116 """Specify an apt proxy to configure
1117-
1118+
1119 :param proxy: proxy to set for APT using Acquire:HTTP
1120 :type str
1121 """
1122@@ -409,7 +418,8 @@
1123 self._machine_id,
1124 self._unit_name,
1125 self._join_zookeeper_hosts(),
1126- self._juju_home))
1127+ self._juju_home,
1128+ self._env_id))
1129 if self._provision:
1130 scripts.extend(_provision_scripts(self._join_zookeeper_hosts()))
1131 scripts.extend(_apiserver_scripts(self._join_zookeeper_hosts()))
1132
1133=== modified file 'juju/providers/maas/maas.py'
1134--- juju/providers/maas/maas.py 2012-10-04 11:16:58 +0000
1135+++ juju/providers/maas/maas.py 2013-01-17 11:10:30 +0000
1136@@ -5,7 +5,9 @@
1137
1138 from base64 import b64encode
1139 import json
1140+import logging
1141 import re
1142+from twisted.internet.defer import inlineCallbacks, returnValue
1143 from twisted.python.failure import Failure
1144 from twisted.web.error import Error
1145 from urllib import urlencode
1146@@ -17,6 +19,9 @@
1147 from juju.providers.maas.files import encode_multipart_data
1148
1149
1150+log = logging.getLogger("juju.maas")
1151+
1152+
1153 CONSUMER_SECRET = ""
1154
1155
1156@@ -80,7 +85,7 @@
1157 # error text that it comes with.
1158 if isinstance(error, Error):
1159 raise ProviderError(error.response)
1160-
1161+
1162 return convert_unknown_error(failure)
1163
1164 def get(self, path, params):
1165@@ -172,10 +177,16 @@
1166 params = {"op": "release"}
1167 return self.post(resource_uri, params)
1168
1169+ @inlineCallbacks
1170 def list_tags(self):
1171 """Ask MAAS to return a list of all the tags defined.
1172
1173 :return: A Deferred whose value is the list of tags.
1174 """
1175 params = {"op": "list"}
1176- return self.get("api/1.0/tags/", params)
1177+ try:
1178+ value = yield self.get("api/1.0/tags/", params)
1179+ except ProviderError as e:
1180+ log.error("Listing valid maas-tags failed: %s", e)
1181+ value = []
1182+ returnValue(value)
1183
1184=== modified file 'juju/providers/maas/tests/test_maas.py'
1185--- juju/providers/maas/tests/test_maas.py 2012-10-04 11:16:58 +0000
1186+++ juju/providers/maas/tests/test_maas.py 2013-01-17 11:10:30 +0000
1187@@ -5,9 +5,11 @@
1188
1189 import json
1190 from textwrap import dedent
1191-from twisted.internet.defer import inlineCallbacks, succeed
1192 from urlparse import urlparse
1193
1194+from twisted.internet.defer import inlineCallbacks, fail, succeed
1195+from twisted.web.error import Error
1196+
1197 from juju.errors import ProviderError
1198 from juju.providers.maas.maas import extract_system_id, MAASClient
1199 from juju.providers.maas.tests.testing import (
1200@@ -15,6 +17,13 @@
1201 FakeMAASHTTPConnectionWithNoAvailableNodes, NODE_JSON, TestCase)
1202
1203
1204+class FakeMAASHTTPConnectionWithNoTags(FakeMAASHTTPConnection):
1205+ """Fake client that raises Not Found on tag listing"""
1206+
1207+ def list_tags(self):
1208+ return fail(Error(404, "Not Found", "Tags? What tags?"))
1209+
1210+
1211 class TestFunctions(TestCase):
1212
1213 def assertExtractSystemID(self, system_id, resource_uri):
1214@@ -220,6 +229,18 @@
1215 self.assertEqual("No matching node is available.", str(e))
1216
1217 @inlineCallbacks
1218+ def test_list_tags_unsupported(self):
1219+ """When tags are unspupported just report no valid tags"""
1220+ log = self.setup_connection(
1221+ MAASClient, FakeMAASHTTPConnectionWithNoTags)
1222+ client = MAASClient(CONFIG)
1223+ log = self.capture_logging()
1224+ result = yield client.list_tags()
1225+ self.assertEqual([], result)
1226+ self.assertRegexpMatches(log.getvalue(),
1227+ "(?m)^Listing valid maas-tags failed: ")
1228+
1229+ @inlineCallbacks
1230 def test_start_node(self):
1231 resource_uri = NODE_JSON[0]["resource_uri"]
1232 series = "splendid"
1233
1234=== modified file 'juju/providers/openstack/client.py'
1235--- juju/providers/openstack/client.py 2012-10-03 15:16:42 +0000
1236+++ juju/providers/openstack/client.py 2013-01-17 11:10:30 +0000
1237@@ -391,7 +391,7 @@
1238 return self.delete(["servers", server_id], code=204)
1239
1240 def run_server(self, image_id, flavor_id, name, security_group_names=None,
1241- user_data=None):
1242+ user_data=None, scheduler_hints=None):
1243 server = {
1244 'name': name,
1245 'flavorRef': flavor_id,
1246@@ -402,6 +402,8 @@
1247 if security_group_names is not None:
1248 server["security_groups"] = [{'name': n}
1249 for n in security_group_names]
1250+ if scheduler_hints is not None:
1251+ server["OS-SCH-HNT:scheduler_hints"] = scheduler_hints
1252 return self.post(["servers"], {'server': server},
1253 root="server", code=202)
1254
1255@@ -409,12 +411,18 @@
1256 d = self.get(
1257 ["servers", server_id, "os-security-groups"],
1258 root="security_groups")
1259- # 2012-07-12: Workaround lack of this api in HP cloud
1260- d.addErrback(
1261- lambda f: self.get_server(server_id).addCallback(
1262- operator.itemgetter("security_groups")))
1263+
1264+ # 2012-07-12: kt Workaround lack of this api in HP cloud
1265+ def _get_group_fallback(f):
1266+ log.debug("Falling back to older/diablo sec groups api")
1267+ return self.get_server(server_id).addCallback(
1268+ operator.itemgetter("security_groups"))
1269+ d.addErrback(_get_group_fallback)
1270 return d
1271
1272+ def get_security_group_details(self, group_id):
1273+ return self.get(["os-security-groups", group_id], "security_group")
1274+
1275 def list_security_groups(self):
1276 return self.get(["os-security-groups"], "security_groups")
1277
1278
1279=== modified file 'juju/providers/openstack/launch.py'
1280--- juju/providers/openstack/launch.py 2012-08-21 16:30:17 +0000
1281+++ juju/providers/openstack/launch.py 2013-01-17 11:10:30 +0000
1282@@ -86,19 +86,23 @@
1283 # Find appropriate instance type for the given constraints. Warn
1284 # if deprecated is instance-type is being used.
1285 flavor_name = self._provider.config.get("default-instance-type")
1286- if flavor_name is None:
1287+ if flavor_name is not None:
1288 log.warning(
1289 "default-instance-type is deprecated, use cli --constraints")
1290 flavors = yield self._provider.nova.list_flavor_details()
1291 flavor_id = _solve_flavor(self._constraints, flavor_name, flavors)
1292
1293+ hints = self._constraints["os-scheduler-hints"]
1294+
1295 server = yield self._provider.nova.run_server(
1296 name="juju %s instance %s" %
1297 (self._provider.environment_name, machine_id,),
1298 image_id=image_id,
1299 flavor_id=flavor_id,
1300 security_group_names=security_groups,
1301- user_data=user_data)
1302+ user_data=user_data,
1303+ scheduler_hints=hints,
1304+ )
1305
1306 if self._master:
1307 yield filestorage.put(id_name, StringIO(str(server['id'])))
1308
1309=== modified file 'juju/providers/openstack/ports.py'
1310--- juju/providers/openstack/ports.py 2012-09-19 05:34:00 +0000
1311+++ juju/providers/openstack/ports.py 2013-01-17 11:10:30 +0000
1312@@ -55,21 +55,34 @@
1313 group_name = self._machine_group_name(machine_id)
1314 server_id = machine.instance_id
1315 groups = yield self.nova.get_server_security_groups(server_id)
1316+
1317+ found = False
1318 for group in groups:
1319 if group['name'] == group_name:
1320- returnValue(group)
1321+ found = group
1322+ break
1323+
1324+ # 2012/12/19: kt diablo/hpcloud compatibility
1325+ if found and not 'rules' in group:
1326+ group = yield self.nova.get_security_group_details(group['id'])
1327+ found = True
1328+
1329+ if found:
1330+ returnValue(group)
1331+
1332 raise errors.ProviderInteractionError(
1333 "Missing security group %r for machine %r" %
1334- (group_name, server_id))
1335+ (group_name, server_id))
1336
1337 @inlineCallbacks
1338 def open_port(self, machine, machine_id, port, protocol="tcp"):
1339 """Allow access to a port for the given machine only"""
1340 group = yield self._get_machine_group(machine, machine_id)
1341- yield self.nova.add_security_group_rule(group['id'],
1342+ yield self.nova.add_security_group_rule(
1343+ group['id'],
1344 ip_protocol=protocol, from_port=port, to_port=port)
1345 log.debug("Opened %s/%s on machine %r",
1346- port, protocol, machine.instance_id)
1347+ port, protocol, machine.instance_id)
1348
1349 @inlineCallbacks
1350 def close_port(self, machine, machine_id, port, protocol="tcp"):
1351@@ -78,13 +91,14 @@
1352 for rule in group["rules"]:
1353 if (port == rule["from_port"] == rule["to_port"] and
1354 rule["ip_protocol"] == protocol):
1355+
1356 yield self.nova.delete_security_group_rule(rule["id"])
1357 log.debug("Closed %s/%s on machine %r",
1358 port, protocol, machine.instance_id)
1359 return
1360 raise errors.ProviderInteractionError(
1361 "Couldn't close unopened %s/%s on machine %r",
1362- port, protocol, machine.instance_id)
1363+ port, protocol, machine.instance_id)
1364
1365 @inlineCallbacks
1366 def get_opened_ports(self, machine, machine_id):
1367@@ -113,11 +127,12 @@
1368 juju_group = self._juju_group_name()
1369 if not juju_group in groups_by_name:
1370 log.debug("Creating juju security group %s", juju_group)
1371- sg = yield self.nova.create_security_group(juju_group,
1372+ sg = yield self.nova.create_security_group(
1373+ juju_group,
1374 "juju group for %s" % (self.tag,))
1375 # Add external ssh access
1376- yield self.nova.add_security_group_rule(sg['id'],
1377- ip_protocol="tcp", from_port=22, to_port=22)
1378+ yield self.nova.add_security_group_rule(
1379+ sg['id'], ip_protocol="tcp", from_port=22, to_port=22)
1380 # Add internal group access
1381 yield self.nova.add_security_group_rule(
1382 parent_group_id=sg['id'], group_id=sg['id'],
1383@@ -131,7 +146,8 @@
1384 yield self.nova.delete_security_group(
1385 groups_by_name[machine_group])
1386 log.debug("Creating machine security group %s", machine_group)
1387- yield self.nova.create_security_group(machine_group,
1388+ yield self.nova.create_security_group(
1389+ machine_group,
1390 "juju group for %s machine %s" % (self.tag, machine_id))
1391
1392 returnValue([juju_group, machine_group])
1393@@ -157,7 +173,8 @@
1394 server_id = machine.instance_id
1395 groups = yield self.nova.get_server_security_groups(server_id)
1396 juju_group = self._juju_group_name()
1397- groups_by_name = dict((g['name'], g['id']) for g in groups
1398+ groups_by_name = dict(
1399+ (g['name'], g['id']) for g in groups
1400 if g['name'].startswith(juju_group))
1401 if juju_group not in groups_by_name:
1402 # Not a juju machine, shouldn't touch
1403
1404=== modified file 'juju/providers/openstack/provider.py'
1405--- juju/providers/openstack/provider.py 2012-09-05 11:07:35 +0000
1406+++ juju/providers/openstack/provider.py 2013-01-17 11:10:30 +0000
1407@@ -11,6 +11,7 @@
1408 """
1409
1410 import logging
1411+import json
1412
1413 from twisted.internet.defer import inlineCallbacks, returnValue
1414
1415@@ -33,6 +34,15 @@
1416 log = logging.getLogger("juju.openstack")
1417
1418
1419+def _convert_scheduler_hints(string):
1420+ """Check constraint value suitable for Nova SchedulerHints extension"""
1421+ obj = json.loads(string)
1422+ if not isinstance(obj, dict):
1423+ raise ValueError("Need json object of key/value strings")
1424+ # GZ 2012-10-26: Does nova have other restrictions on what it will accept?
1425+ return obj
1426+
1427+
1428 class MachineProvider(MachineProviderBase):
1429 """MachineProvider for use in an OpenStack environment"""
1430
1431@@ -83,6 +93,13 @@
1432 returnValue(cs)
1433 cs = yield super(MachineProvider, self).get_constraint_set()
1434
1435+ # Pseudo-constraint that does not affect flavor selected but is passed
1436+ # through to server creation for influencing the scheduler, for
1437+ # instance in the placement of the server.
1438+ # Perhaps only register this constraint if the deployment advertises
1439+ # the SchedulerHints extension as available?
1440+ cs.register("os-scheduler-hints", converter=_convert_scheduler_hints)
1441+
1442 # Fetch provider defined instance types (just names)
1443 flavors = yield self.nova.list_flavors()
1444 flavor_names = [f['name'] for f in flavors]
1445
1446=== modified file 'juju/providers/openstack/tests/__init__.py'
1447--- juju/providers/openstack/tests/__init__.py 2012-08-24 19:22:13 +0000
1448+++ juju/providers/openstack/tests/__init__.py 2013-01-17 11:10:30 +0000
1449@@ -47,6 +47,8 @@
1450
1451 def setup_constraints(self):
1452 self.constraint_set = ConstraintSet(self.provider_type)
1453+ self.constraint_set.register("os-scheduler-hints",
1454+ converter=json.loads)
1455 self.constraint_set.register_generics(
1456 [f['name'] for f in self.default_flavors])
1457
1458@@ -100,7 +102,6 @@
1459 "auth-url": self.api_url,
1460 "project-name": "test_project",
1461 "control-bucket": self.environment_name,
1462- "default-instance-type": "standard.xsmall",
1463 "default-image-id": 42,
1464 "use-floating-ip": True,
1465 "ssl-hostname-verification": True,
1466@@ -189,7 +190,6 @@
1467 "auth-url": api_url,
1468 "project-name": "aproject",
1469 "control-bucket": environment_name,
1470- "default-instance-type": "standard.xsmall",
1471 "default-image-id": 42,
1472 "ssl-hostname-verification": True,
1473 }
1474
1475=== modified file 'juju/providers/openstack/tests/test_client.py'
1476--- juju/providers/openstack/tests/test_client.py 2012-09-28 15:53:06 +0000
1477+++ juju/providers/openstack/tests/test_client.py 2013-01-17 11:10:30 +0000
1478@@ -198,6 +198,36 @@
1479 deferred = osc.authenticate()
1480 return self.assertFailure(deferred, errors.SSLVerificationError)
1481
1482+ def is_server_with_hints(self, producer):
1483+ obj = json.loads(producer.content)
1484+ self.assertEqual({"server": {
1485+ "imageRef": "an-image",
1486+ "flavorRef": "a-flavor",
1487+ "name": "Test",
1488+ "OS-SCH-HNT:scheduler_hints": {"hint-key": "hint-val"},
1489+ }}, obj)
1490+ return True
1491+
1492+ @defer.inlineCallbacks
1493+ def test_run_server_passes_hints(self):
1494+ config, osc = self.make_client_legacy()
1495+ self.mock_agent.request("POST",
1496+ "https://testing.invalid/compute/servers", mocker.ANY,
1497+ mocker.MATCH(self.is_server_with_hints))
1498+ response = FakeResponse(202, http_headers.Headers({
1499+ "Content-Type": ["application/json"],
1500+ }),
1501+ json.dumps({'server': {
1502+ }}))
1503+ self.mocker.result(defer.succeed(response))
1504+ self.mocker.replay()
1505+ # Fake having already authenticated by setting token and services
1506+ osc.token = "tok"
1507+ osc.services = {"compute": "https://testing.invalid/compute"}
1508+ novac = client._NovaClient(osc)
1509+ result = yield novac.run_server("an-image", "a-flavor", "Test",
1510+ scheduler_hints={"hint-key": "hint-val"})
1511+
1512
1513 class TestReauthentication(testing.TestCase):
1514
1515
1516=== modified file 'juju/providers/openstack/tests/test_launch.py'
1517--- juju/providers/openstack/tests/test_launch.py 2012-09-10 03:20:20 +0000
1518+++ juju/providers/openstack/tests/test_launch.py 2013-01-17 11:10:30 +0000
1519@@ -32,13 +32,15 @@
1520 self.nova.list_flavor_details()
1521 self.mocker.result(succeed(self.default_flavors))
1522
1523- def expect_run_server(self, machine_id, cc_match, response, flavor_id=1):
1524+ def expect_run_server(self, machine_id, cc_match, response, flavor_id=1,
1525+ hints=None):
1526 self.nova.run_server(
1527 name="juju testing instance " + machine_id,
1528 image_id=42,
1529 flavor_id=flavor_id,
1530 security_group_names=["juju-x", "juju-y"],
1531 user_data=MATCH(cc_match),
1532+ scheduler_hints=hints,
1533 )
1534 self.mocker.result(succeed(response))
1535
1536@@ -113,6 +115,13 @@
1537 def get_cc_matcher(self, machine_id, provider, is_master=False):
1538 return _CloudConfigMatcher(self, machine_id, provider, is_master).match
1539
1540+ def _check_log(self, log, pattern):
1541+ self.assertRegexpMatches(log.getvalue(), pattern)
1542+
1543+ def capture_and_check_log(self, pattern="^$", logname="juju.openstack"):
1544+ log = self.capture_logging(logname)
1545+ self.addCleanup(self._check_log, log, pattern)
1546+
1547 def test_start_machine_with_constraints(self):
1548 provider = MockedLaunchProvider(self.mocker)
1549 provider.expect_zookeeper_machines(1000, "master.invalid")
1550@@ -126,8 +135,43 @@
1551 },
1552 flavor_id=2)
1553 self.mocker.replay()
1554+ self.capture_and_check_log()
1555 return provider.launch("1", constraints=["cpu=2", "mem=3G"])
1556
1557+ def test_start_machine_with_scheduler_hints(self):
1558+ provider = MockedLaunchProvider(self.mocker)
1559+ provider.expect_zookeeper_machines(1000, "master.invalid")
1560+ provider.expect_launch_setup("1")
1561+ provider.expect_run_server(
1562+ "1",
1563+ self.get_cc_matcher("1", provider),
1564+ response={
1565+ 'id': 1001,
1566+ 'addresses': {'public': []},
1567+ },
1568+ hints={"hint-key": "hint-value"})
1569+ self.mocker.replay()
1570+ self.capture_and_check_log()
1571+ return provider.launch("1", constraints=[
1572+ "os-scheduler-hints={\"hint-key\": \"hint-value\"}"])
1573+
1574+ def test_start_machine_with_default_instance_type(self):
1575+ config = dict(MockedLaunchProvider.default_config)
1576+ config['default-instance-type'] = "m1.sample"
1577+ provider = MockedLaunchProvider(self.mocker, config)
1578+ provider.expect_zookeeper_machines(1000, "master.invalid")
1579+ provider.expect_launch_setup("1")
1580+ provider.expect_run_server("1",
1581+ self.get_cc_matcher("1", provider),
1582+ response={
1583+ 'id': 1001,
1584+ 'addresses': {'public': []},
1585+ })
1586+ self.mocker.replay()
1587+ self.capture_and_check_log(
1588+ "^default-instance-type is deprecated.*constraints")
1589+ return provider.launch("1")
1590+
1591 def test_start_machine(self):
1592 provider = MockedLaunchProvider(self.mocker)
1593 provider.expect_zookeeper_machines(1000, "master.invalid")
1594@@ -139,6 +183,7 @@
1595 'addresses': {'public': []},
1596 })
1597 self.mocker.replay()
1598+ self.capture_and_check_log()
1599 return provider.launch("1")
1600
1601 def test_start_machine_delay(self):
1602@@ -159,6 +204,7 @@
1603 provider.expect_available_floating_ip(1001)
1604 self.mocker.result(succeed(None))
1605 self.mocker.replay()
1606+ self.capture_and_check_log()
1607 self.patch(NovaLaunchMachine, "_DELAY_FOR_ADDRESSES", 0)
1608 return provider.launch("1")
1609
1610@@ -175,6 +221,7 @@
1611 provider.provider_actions.save_state({'zookeeper-instances': [1000]})
1612 self.mocker.result(succeed(None))
1613 self.mocker.replay()
1614+ self.capture_and_check_log()
1615 return provider.launch("0", master=True)
1616
1617 def test_start_machine_master(self):
1618
1619=== modified file 'juju/providers/openstack/tests/test_ports.py'
1620--- juju/providers/openstack/tests/test_ports.py 2012-09-19 05:34:00 +0000
1621+++ juju/providers/openstack/tests/test_ports.py 2013-01-17 11:10:30 +0000
1622@@ -39,10 +39,10 @@
1623
1624 def test_open_port(self):
1625 """Opening a port adds the rule to the appropriate security group"""
1626- self.expect_nova_get("servers/1000/os-security-groups",
1627+ self.expect_nova_get(
1628+ "servers/1000/os-security-groups",
1629 response={'security_groups': [
1630- {'name': "juju-testing-1", 'id': 1},
1631- ]})
1632+ {'name': "juju-testing-1", 'id': 1, 'rules': []}]})
1633 self.expect_create_rule(1, "tcp", 80)
1634 self.mocker.replay()
1635
1636@@ -52,9 +52,34 @@
1637
1638 def _check_log(_):
1639 self.assertIn("Opened 80/tcp on machine '1000'",
1640- log.getvalue())
1641+ log.getvalue())
1642 return deferred.addCallback(_check_log)
1643
1644+ def test_diablo_hpcloud_ccompatbility(self):
1645+ """Verify compatibility workarounds for hpcloud/diablo."""
1646+ self.expect_nova_get(
1647+ "servers/1000/os-security-groups",
1648+ response={'security_groups': [
1649+ {'name': "juju-testing-1", 'id': 1}]})
1650+
1651+ self.expect_nova_get(
1652+ "os-security-groups/1",
1653+ response={'security_group': {
1654+ 'name': "juju-testing-1",
1655+ 'id': 1,
1656+ 'rules': [{
1657+ 'id': 1,
1658+ 'parent_group_id': 1,
1659+ 'ip_protocol': 'tcp',
1660+ 'from_port': 80,
1661+ 'to_port': 80}]}})
1662+
1663+ self.mocker.replay()
1664+
1665+ machine = NovaProviderMachine('1000', "server1000.testing.invalid")
1666+ deferred = self.get_provider().get_opened_ports(machine, "1")
1667+ return deferred.addCallback(self.assertEqual, set([(80, "tcp")]))
1668+
1669 def test_open_port_missing_group(self):
1670 """Missing security group raises an error on deleting port"""
1671 self.expect_nova_get("servers/1000/os-security-groups",
1672
1673=== modified file 'juju/providers/openstack/tests/test_provider.py'
1674--- juju/providers/openstack/tests/test_provider.py 2012-08-28 16:11:04 +0000
1675+++ juju/providers/openstack/tests/test_provider.py 2013-01-17 11:10:30 +0000
1676@@ -192,3 +192,32 @@
1677 cs2 = yield provider.get_constraint_set()
1678 self.assertIsInstance(cs2, ConstraintSet)
1679 self.assertEqual(cs, cs2)
1680+
1681+ def create_constraint_set(self):
1682+ self.expect_nova_get("flavors",
1683+ response={'flavors': self.default_flavors})
1684+ self.mocker.replay()
1685+ provider = self.get_provider()
1686+ return provider.get_constraint_set()
1687+
1688+ @inlineCallbacks
1689+ def test_parse_scheduler_hints_one(self):
1690+ cs = yield self.create_constraint_set()
1691+ c = cs.parse(["os-scheduler-hints={\"hint-key\": \"hint-val\"}"])
1692+ self.assertEqual({"hint-key": "hint-val"}, c["os-scheduler-hints"])
1693+
1694+ @inlineCallbacks
1695+ def test_parse_scheduler_hints_bad_value(self):
1696+ cs = yield self.create_constraint_set()
1697+ err = self.assertRaises(errors.ConstraintError,
1698+ cs.parse, ["os-scheduler-hints=notjson"])
1699+ self.assertRegexpMatches(str(err),
1700+ "Bad 'os-scheduler-hints' constraint 'notjson': .*")
1701+
1702+ @inlineCallbacks
1703+ def test_parse_scheduler_hints_bad_array(self):
1704+ cs = yield self.create_constraint_set()
1705+ err = self.assertRaises(errors.ConstraintError,
1706+ cs.parse, ["os-scheduler-hints=[]"])
1707+ self.assertRegexpMatches(str(err),
1708+ "Bad 'os-scheduler-hints' constraint '\\[\\]': .*")
1709
1710=== modified file 'juju/rapi/transport/tests/test_ws.py'
1711--- juju/rapi/transport/tests/test_ws.py 2012-10-04 15:58:43 +0000
1712+++ juju/rapi/transport/tests/test_ws.py 2013-01-17 11:10:30 +0000
1713@@ -74,7 +74,7 @@
1714 self.provider = MachineProvider(
1715 "cloudy", {"default-series": "precise"})
1716 self.ws_factory = WebSocketAPIFactory(
1717- get_test_zookeeper_address(), self.provider)
1718+ get_test_zookeeper_address(), self.provider, "uuid1")
1719 self.ws = self.ws_factory.buildProtocol()
1720 self.ws.transport = self.transport = OWTransport(self.ws)
1721 self.ws._schedule = lambda: 1
1722@@ -92,7 +92,7 @@
1723 def test_greeting(self):
1724
1725 d = self.transport.watch_dict(dict(
1726- version=0, ready=True, provider_type="dummy",
1727+ version=0, ready=True, provider_type="dummy", uuid="uuid1",
1728 default_series="precise"))
1729 self.ws.connectionMade()
1730 return d
1731
1732=== modified file 'juju/rapi/transport/ws.py'
1733--- juju/rapi/transport/ws.py 2012-10-04 15:58:43 +0000
1734+++ juju/rapi/transport/ws.py 2013-01-17 11:10:30 +0000
1735@@ -23,13 +23,15 @@
1736
1737 The client MUST wait for initial handshake from the server.
1738 """
1739- def __init__(self, zk_hosts, provider):
1740+ def __init__(self, zk_hosts, provider, env_uuid):
1741 # Are we currently accepting/processing messages.
1742 self.active = False
1743 # Zk servers to connect to.
1744 self.zk_hosts = zk_hosts
1745 # Env Provider (needed for status and constraint set retrieval).
1746 self.provider = provider
1747+ # Environment UUID
1748+ self.env_uuid = env_uuid
1749 # API Context/Endpoint
1750 self.context = None
1751 # Client to zk.
1752@@ -47,6 +49,7 @@
1753 """
1754 self.transport.write(json.dumps(
1755 {"version": 0, "extensions": [], "ready": True,
1756+ "uuid": self.env_uuid,
1757 "provider_type": self.provider.provider_type,
1758 "default_series": self.provider.config.get("default-series")}))
1759
1760@@ -169,9 +172,10 @@
1761
1762 protocol = APIWebSocket
1763
1764- def __init__(self, zk_hosts, provider):
1765+ def __init__(self, zk_hosts, provider, env_uuid):
1766 self.zk_hosts = zk_hosts
1767 self.provider = provider
1768+ self.env_uuid = env_uuid
1769 self.numClients = 0
1770 self.stream_manager = DeltaStreamManager(
1771 APIContext(ManagedClient(self.zk_hosts), self.provider))
1772@@ -191,7 +195,7 @@
1773
1774 # New connection.
1775 def buildProtocol(self, addr=None):
1776- p = self.protocol(self.zk_hosts, self.provider)
1777+ p = self.protocol(self.zk_hosts, self.provider, self.env_uuid)
1778 p.factory = self
1779 self.stream_manager.add(p)
1780 return p
1781
1782=== modified file 'juju/state/environment.py'
1783--- juju/state/environment.py 2012-09-10 03:20:20 +0000
1784+++ juju/state/environment.py 2013-01-17 11:10:30 +0000
1785@@ -85,7 +85,7 @@
1786 """
1787
1788 def set_provider_type(self, provider_type):
1789- return self._set_value("provider-type", provider_type)
1790+ return self._set_value("provider-type", provider_type, once=True)
1791
1792 def get_provider_type(self):
1793 return self._get_value("provider-type")
1794@@ -102,6 +102,12 @@
1795 """
1796 return self._set_value("debug-log", bool(enabled))
1797
1798+ def set_environment_id(self, uid):
1799+ return self._set_value("env-id", uid, once=True)
1800+
1801+ def get_environment_id(self):
1802+ return self._get_value("env-id")
1803+
1804 @inlineCallbacks
1805 def _get_value(self, key, default=None):
1806 try:
1807@@ -111,13 +117,15 @@
1808 data = serializer.load(content)
1809 returnValue(data.get(key, default))
1810
1811- def _set_value(self, key, value):
1812+ def _set_value(self, key, value, once=False):
1813
1814 def set_value(old_content, stat):
1815 if not old_content:
1816 data = {}
1817 else:
1818 data = serializer.load(old_content)
1819+ if once and key in data:
1820+ raise ValueError("%s can only be set once" % key)
1821 data[key] = value
1822 return serializer.dump(data)
1823
1824
1825=== modified file 'juju/state/hook.py'
1826--- juju/state/hook.py 2012-09-10 03:20:20 +0000
1827+++ juju/state/hook.py 2013-01-17 11:10:30 +0000
1828@@ -1,3 +1,4 @@
1829+import logging
1830 from collections import namedtuple
1831
1832 from twisted.internet.defer import inlineCallbacks, returnValue, succeed, fail
1833@@ -11,6 +12,9 @@
1834 from juju.state.utils import YAMLState
1835
1836
1837+log = logging.getLogger("juju.state.hook")
1838+
1839+
1840 class RelationChange(
1841 namedtuple(
1842 "RelationChange",
1843@@ -103,16 +107,23 @@
1844 self._topology = yield self._read_topology()
1845 service = yield self.get_local_service()
1846 internal_service_id = service.internal_id
1847+ service_unit_state = yield self.get_local_unit_state()
1848 for info in self._topology.get_relations_for_service(
1849 internal_service_id):
1850 service_info = info["service"]
1851- relations.append(
1852- ServiceRelationState(
1853- self._client,
1854- internal_service_id,
1855- info["relation_id"],
1856- info["scope"],
1857- **service_info))
1858+ relation = ServiceRelationState(
1859+ self._client,
1860+ internal_service_id,
1861+ info["relation_id"],
1862+ info["scope"],
1863+ **service_info)
1864+ # Verify that it has unit relation state defined in ZK
1865+ try:
1866+ yield relation.get_unit_state(service_unit_state)
1867+ relations.append(relation)
1868+ except UnitRelationStateNotFound:
1869+ log.debug("Ignoring partially constructed relation: %s",
1870+ relation.relation_ident)
1871 returnValue(relations)
1872
1873 @inlineCallbacks
1874
1875=== modified file 'juju/state/initialize.py'
1876--- juju/state/initialize.py 2012-12-12 19:30:20 +0000
1877+++ juju/state/initialize.py 2013-01-17 11:10:30 +0000
1878@@ -1,4 +1,5 @@
1879 import logging
1880+import uuid
1881
1882 from twisted.internet.defer import inlineCallbacks
1883 from txzookeeper.client import ZOO_OPEN_ACL_UNSAFE
1884@@ -70,6 +71,7 @@
1885 # Setup default global settings information.
1886 settings = GlobalSettingsStateManager(self.client)
1887 yield settings.set_provider_type(self.provider_type)
1888+ yield settings.set_environment_id(uuid.uuid4().get_hex())
1889
1890 # Create a node that only the admin can read.
1891 yield self.client.create("/login", acls=[
1892
1893=== modified file 'juju/state/tests/test_environment.py'
1894--- juju/state/tests/test_environment.py 2012-09-10 03:20:20 +0000
1895+++ juju/state/tests/test_environment.py 2013-01-17 11:10:30 +0000
1896@@ -140,12 +140,25 @@
1897 """Debug logging is off by default."""
1898 self.assertEqual((yield self.manager.get_provider_type()), None)
1899 yield self.manager.set_provider_type("ec2")
1900+ yield self.assertFailure(
1901+ self.manager.set_provider_type("abc"), ValueError)
1902 self.assertEqual((yield self.manager.get_provider_type()), "ec2")
1903 content, stat = yield self.client.get("/settings")
1904 self.assertEqual(serializer.load(content),
1905 {"provider-type": "ec2"})
1906
1907 @inlineCallbacks
1908+ def test_set_get_environment(self):
1909+ self.assertEqual((yield self.manager.get_environment_id()), None)
1910+ yield self.manager.set_environment_id('snowflake')
1911+ yield self.assertFailure(
1912+ self.manager.set_environment_id('snowflake'),
1913+ ValueError)
1914+ self.assertEqual(
1915+ (yield self.manager.get_environment_id()),
1916+ "snowflake")
1917+
1918+ @inlineCallbacks
1919 def test_get_debug_log_enabled_no_settings_default(self):
1920 """Debug logging is off by default."""
1921 value = yield self.manager.is_debug_log_enabled()
1922
1923=== modified file 'juju/state/tests/test_hook.py'
1924--- juju/state/tests/test_hook.py 2012-09-10 03:20:20 +0000
1925+++ juju/state/tests/test_hook.py 2013-01-17 11:10:30 +0000
1926@@ -1,3 +1,5 @@
1927+import logging
1928+
1929 from twisted.internet.defer import inlineCallbacks, returnValue
1930 from juju.lib.pick import pick_attr
1931 from juju.lib import serializer
1932@@ -98,23 +100,61 @@
1933 self.relation = self.mysql_states["relation"]
1934
1935 @inlineCallbacks
1936- def add_another_blog(self, service_name):
1937- blog_ep = RelationEndpoint(
1938- service_name, "client-server", "database", "client")
1939- mysql_ep = RelationEndpoint(
1940- "mysql", "client-server", "db", "server")
1941- yield self.add_relation_service_unit_from_endpoints(
1942- blog_ep, mysql_ep)
1943+ def add_another_blog(self, blog_name):
1944+ blog_ep = RelationEndpoint(blog_name, "client-server", "app", "client")
1945+ # Fully construct states for the relation connecting to this additional blog
1946+ other_mysql_states = yield self.add_relation_service_unit_to_another_endpoint(
1947+ self.mysql_states, blog_ep)
1948+ # Then complete in the opposite direction
1949+ blog_states = yield self.add_opposite_service_unit(other_mysql_states)
1950+ yield blog_states['service_relations'][-1].add_unit_state(
1951+ self.mysql_states['unit'])
1952+ returnValue(blog_states)
1953
1954 @inlineCallbacks
1955 def add_db_admin_tool(self, admin_name):
1956 """Add another relation, using a different relation name"""
1957 admin_ep = RelationEndpoint(
1958 admin_name, "client-server", "admin-app", "client")
1959- mysql_ep = RelationEndpoint(
1960+ mysql_admin_ep = RelationEndpoint(
1961 "mysql", "client-server", "db-admin", "server")
1962- yield self.add_relation_service_unit_from_endpoints(
1963- admin_ep, mysql_ep)
1964+ mysql_admin_states = yield self.reuse_service_unit_in_new_relation(
1965+ self.mysql_states, mysql_admin_ep, admin_ep)
1966+ admin_states = yield self.add_opposite_service_unit(mysql_admin_states)
1967+ returnValue(admin_states)
1968+
1969+ @inlineCallbacks
1970+ def reuse_service_unit_in_new_relation(self, reused_states, *endpoints):
1971+ """Reuse an existing service unit as part of a new relation"""
1972+ service_state = reused_states["service"]
1973+ unit_state = reused_states["unit"]
1974+
1975+ # 1. Setup all service states
1976+ service_states = [service_state]
1977+ for endpoint in endpoints[1:]:
1978+ service_state = yield self.add_service(endpoint.service_name)
1979+ service_states.append(service_state)
1980+
1981+ # 2. And join together in a relation
1982+ relation_state, service_relation_states = \
1983+ yield self.relation_manager.add_relation_state(
1984+ *endpoints)
1985+ # 3. Add a service unit to only the first endpoint - we need
1986+ # to test what happens when service units are added to the
1987+ # other service state (if any), so do so separately
1988+ relation_unit_state = yield service_relation_states[0].add_unit_state(
1989+ unit_state)
1990+
1991+ returnValue({
1992+ "endpoints": list(endpoints),
1993+ "service": service_states[0],
1994+ "services": service_states,
1995+ "unit": unit_state,
1996+ "relation": relation_state,
1997+ "service_relation": service_relation_states[0],
1998+ "unit_relation": relation_unit_state,
1999+ "service_relations": service_relation_states})
2000+
2001
2002
2003 class HookContextTest(HookContextTestBase, CommonHookContextTestsMixin):
2004@@ -162,6 +202,35 @@
2005 set((yield new_context.get_relation_idents(None))),
2006 set(["db:0", "db:1", "db:2", "db-admin:3"]))
2007
2008+ @inlineCallbacks
2009+ def test_get_relation_idents_partial_updates_to_zk(self):
2010+ """Verify relation idents do not reflect partial updates to ZK."""
2011+ log = self.capture_logging(level=logging.DEBUG)
2012+
2013+ # 1. Partial update - no corresponding service relation unit
2014+ yield self.add_relation_service_unit_to_another_endpoint(
2015+ self.mysql_states,
2016+ RelationEndpoint(
2017+ "wordpress2", "client-server", "app", "client"))
2018+
2019+ # 2. Do a complete update of adding another relation and
2020+ # corresponding units
2021+ yield self.add_another_blog("wordpress3")
2022+ context = self.get_context("mysql/0")
2023+
2024+ # 3. Observe only the relation ids for wordpress, wordpress3
2025+ self.assertEqual(
2026+ set((yield context.get_relation_idents("db"))),
2027+ set(["db:0", "db:2"]))
2028+ self.assertIn("Ignoring partially constructed relation: db:1",
2029+ log.getvalue())
2030+
2031+ # 4. Finally, relation ids for a nonexistent relation are
2032+ # still not seen, or cause an error.
2033+ self.assertEqual(
2034+ set((yield context.get_relation_idents("not-a-relation"))),
2035+ set())
2036+
2037
2038 class RelationHookContextTest(HookContextTestBase,
2039 CommonHookContextTestsMixin):
2040@@ -178,10 +247,6 @@
2041 self.wordpress_states, "modified", "mysql/0")
2042
2043 def get_context(self, states, change_type, unit_name):
2044- change = RelationChange(
2045- states["service_relation"].relation_ident,
2046- change_type,
2047- unit_name)
2048 return RelationHookContext(
2049 self.client, states["unit_relation"],
2050 states["service_relation"].relation_ident,
2051@@ -605,7 +670,7 @@
2052 self.assertEqual(
2053 set((yield new_context.get_relation_idents("db"))),
2054 set(["db:1"]))
2055- yield self.assertFailure(
2056+ yield self.assertFailure(
2057 new_context.get_relation_hook_context("db:0"),
2058 RelationStateNotFound)
2059 db1 = yield new_context.get_relation_hook_context("db:1")
2060
2061=== modified file 'juju/state/tests/test_initialize.py'
2062--- juju/state/tests/test_initialize.py 2012-12-12 19:30:20 +0000
2063+++ juju/state/tests/test_initialize.py 2013-01-17 11:10:30 +0000
2064@@ -91,6 +91,8 @@
2065 self.assertEqual(instance_id, "i-abcdef")
2066
2067 settings_manager = GlobalSettingsStateManager(self.client)
2068+ env_id = yield settings_manager.get_environment_id()
2069+ self.assertEqual(len(env_id), 32)
2070 self.assertEqual((yield settings_manager.get_provider_type()), "dummy")
2071 self.assertEqual(
2072 self.log.getvalue().strip(),
2073
2074=== modified file 'juju/unit/deploy.py'
2075--- juju/unit/deploy.py 2012-04-04 20:21:44 +0000
2076+++ juju/unit/deploy.py 2013-01-17 11:10:30 +0000
2077@@ -29,6 +29,7 @@
2078 self.juju_directory = juju_directory
2079 self.service_state_manager = ServiceStateManager(self.client)
2080 self.charm_state_manager = CharmStateManager(self.client)
2081+ self.env_id = None
2082
2083 @property
2084 def charms_directory(self):
2085@@ -38,10 +39,12 @@
2086 def start(self, provider_type=None):
2087 """Starts the unit deployer."""
2088 # Find out what provided the machine, and how to deploy units.
2089+ settings = GlobalSettingsStateManager(self.client)
2090 if provider_type is None:
2091- settings = GlobalSettingsStateManager(self.client)
2092 provider_type = yield settings.get_provider_type()
2093+
2094 self.deploy_factory = get_deploy_factory(provider_type)
2095+ self.env_id = yield settings.get_environment_id()
2096
2097 if not os.path.exists(self.charms_directory):
2098 os.makedirs(self.charms_directory)
2099@@ -89,7 +92,7 @@
2100 if not running:
2101 log.debug("Starting service unit %s...", service_unit_name)
2102 yield deployment.start(
2103- self.machine_id, self.client.servers, bundle)
2104+ self.env_id, self.machine_id, self.client.servers, bundle)
2105 log.info("Started service unit %s", service_unit_name)
2106
2107 @inlineCallbacks
2108
2109=== modified file 'juju/unit/tests/test_address.py'
2110--- juju/unit/tests/test_address.py 2012-10-05 17:49:27 +0000
2111+++ juju/unit/tests/test_address.py 2013-01-17 11:10:30 +0000
2112@@ -1,23 +1,18 @@
2113 import subprocess
2114-import zookeeper
2115
2116 from twisted.internet.defer import inlineCallbacks, succeed, returnValue
2117 from twisted.web import client
2118
2119 from juju.errors import JujuError
2120 from juju.lib.testing import TestCase
2121+from juju.state.tests.common import StateTestBase
2122 from juju.unit.address import (
2123 EC2UnitAddress, LocalUnitAddress, OrchestraUnitAddress, DummyUnitAddress,
2124 MAASUnitAddress, OpenStackUnitAddress, UnitAddress, get_unit_address)
2125 from juju.state.environment import GlobalSettingsStateManager
2126
2127
2128-class AddressTest(TestCase):
2129-
2130- def setUp(self):
2131- zookeeper.set_debug_level(0)
2132- self.client = self.get_zookeeper_client()
2133- return self.client.connect()
2134+class AddressTest(StateTestBase):
2135
2136 @inlineCallbacks
2137 def get_address_for(self, provider_type):
2138
2139=== modified file 'juju/unit/tests/test_deploy.py'
2140--- juju/unit/tests/test_deploy.py 2012-03-28 00:38:16 +0000
2141+++ juju/unit/tests/test_deploy.py 2013-01-17 11:10:30 +0000
2142@@ -12,6 +12,7 @@
2143 from juju.machine.tests.test_constraints import (
2144 dummy_constraints, series_constraints)
2145 from juju.machine.unit import UnitMachineDeployment
2146+from juju.state.environment import GlobalSettingsStateManager
2147 from juju.state.machine import MachineStateManager
2148 from juju.state.service import ServiceStateManager
2149 from juju.state.tests.common import StateTestBase
2150@@ -48,6 +49,9 @@
2151 add_machine_state(series_constraints)
2152 yield self.unit_state.assign_to_machine(self.machine_state)
2153
2154+ self.env_settings = GlobalSettingsStateManager(self.client)
2155+ yield self.env_settings.set_environment_id("snowflake")
2156+
2157 # NOTE machine_id must be a str to use with one of the
2158 # deployment classes
2159 self.juju_dir = self.makeDir()
2160@@ -62,13 +66,22 @@
2161 UnitMachineDeployment)
2162
2163 @inlineCallbacks
2164+ def test_start_with_provider_type(self):
2165+ # bug test against 1100245
2166+ self.unit_manager = UnitDeployer(
2167+ self.client, str(self.machine_state.id), self.juju_dir)
2168+ yield self.unit_manager.start("subordinate")
2169+ self.assertEqual(self.unit_manager.env_id, "snowflake")
2170+
2171+ @inlineCallbacks
2172 def test_charm_download(self):
2173 """Downloading a charm should store the charm locally."""
2174 yield self.unit_manager.download_charm(self.charm_state)
2175 checksum = self.charm.get_sha256()
2176 charm_id = local_charm_id(self.charm)
2177 charm_key = under.quote("%s:%s" % (charm_id, checksum))
2178- charm_path = os.path.join(self.unit_manager.charms_directory, charm_key)
2179+ charm_path = os.path.join(
2180+ self.unit_manager.charms_directory, charm_key)
2181
2182 self.assertTrue(os.path.exists(charm_path))
2183 bundle = CharmBundle(charm_path)
2184@@ -82,10 +95,11 @@
2185 def test_start_service_unit(self):
2186 """Verify starting a service unit kicks off the desired deployment."""
2187 mock_deployment = self.mocker.patch(self.unit_manager.deploy_factory)
2188- mock_deployment.start("0", get_test_zookeeper_address(), MATCH_BUNDLE)
2189+ mock_deployment.start(
2190+ "snowflake", "0", get_test_zookeeper_address(), MATCH_BUNDLE)
2191 test_deferred = Deferred()
2192
2193- def test_complete(machine_id, servers, bundle):
2194+ def test_complete(env_id, machine_id, servers, bundle):
2195 test_deferred.callback(True)
2196
2197 self.mocker.call(test_complete)
2198@@ -95,16 +109,17 @@
2199 yield test_deferred
2200 self.assertLogLines(
2201 self.output.getvalue(),
2202- ["Downloading charm local:series/dummy-1 to %s" % \
2203- os.path.join(self.juju_dir, "charms"),
2204- "Starting service unit myblog/0...",
2205- "Started service unit myblog/0"])
2206+ ["Downloading charm local:series/dummy-1 to %s" %
2207+ os.path.join(self.juju_dir, "charms"),
2208+ "Starting service unit myblog/0...",
2209+ "Started service unit myblog/0"])
2210
2211 @inlineCallbacks
2212 def test_kill_service_unit(self):
2213 """Verify killing a service unit destroys the deployment."""
2214 mock_deployment = self.mocker.patch(self.unit_manager.deploy_factory)
2215- mock_deployment.start("0", get_test_zookeeper_address(), MATCH_BUNDLE)
2216+ mock_deployment.start(
2217+ "snowflake", "0", get_test_zookeeper_address(), MATCH_BUNDLE)
2218 self.mocker.result(succeed(True))
2219 mock_deployment.destroy()
2220 self.mocker.result(succeed(True))
2221@@ -126,8 +141,8 @@
2222 yield test_deferred
2223 self.assertLogLines(
2224 self.output.getvalue(),
2225- ["Downloading charm local:series/dummy-1 to %s" % \
2226- os.path.join(self.juju_dir, "charms"),
2227+ ["Downloading charm local:series/dummy-1 to %s" %
2228+ os.path.join(self.juju_dir, "charms"),
2229 "Starting service unit myblog/0...",
2230 "Started service unit myblog/0",
2231 "Stopping service unit myblog/0...",
2232
2233=== modified file 'juju/unit/tests/test_lifecycle.py'
2234--- juju/unit/tests/test_lifecycle.py 2012-09-10 03:20:20 +0000
2235+++ juju/unit/tests/test_lifecycle.py 2013-01-17 11:10:30 +0000
2236@@ -68,6 +68,7 @@
2237 self.executor.start()
2238 self.change_environment(
2239 PATH=os.environ["PATH"],
2240+ JUJU_ENV_UUID="snowflake",
2241 JUJU_UNIT_NAME="service-unit/0")
2242
2243 @inlineCallbacks
2244@@ -897,14 +898,19 @@
2245 self.write_hook(
2246 "start",
2247 '#!/bin/sh\n echo "hello world"\n')
2248- yield self.add_opposite_service_unit(self.states)
2249+ blog_states = yield self.add_opposite_service_unit(self.states)
2250+ yield blog_states['service_relations'][-1].add_unit_state(
2251+ self.states['unit'])
2252+
2253 for i in range(1, 3):
2254- yield self.add_opposite_service_unit(
2255+ blog_states = yield self.add_opposite_service_unit(
2256 (yield self.add_relation_service_unit_to_another_endpoint(
2257 self.states,
2258 RelationEndpoint(
2259 "wordpress-%d" % i,
2260 "client-server", "db", "client"))))
2261+ yield blog_states['service_relations'][-1].add_unit_state(
2262+ self.states['unit'])
2263
2264 finished = self.wait_on_hook("app-relation-changed", 3)
2265 yield self.lifecycle.start()
2266@@ -1006,6 +1012,7 @@
2267 charm specification of hook invocation."""
2268 self.change_environment(
2269 PATH=os.environ["PATH"],
2270+ JUJU_ENV_UUID="snowflake",
2271 JUJU_UNIT_NAME="service-unit/0")
2272 change = RelationChange("clients:42", "joined", "s/2")
2273 unit_hook_path = self.makeDir()
2274@@ -1014,6 +1021,7 @@
2275 self.assertEqual(environ["JUJU_RELATION"], "clients")
2276 self.assertEqual(environ["JUJU_RELATION_ID"], "clients:42")
2277 self.assertEqual(environ["JUJU_REMOTE_UNIT"], "s/2")
2278+ self.assertEqual(environ["JUJU_ENV_UUID"], "snowflake")
2279 self.assertEqual(environ["CHARM_DIR"],
2280 os.path.join(unit_hook_path, "charm"))
2281
2282@@ -1328,14 +1336,20 @@
2283 # Setup 5 different wordpress services, wordpress, wordpress-1
2284 # through wordpress-4 with one service unit each. Each of these
2285 # will be on the relation app:0 ... app:4
2286- yield self.add_opposite_service_unit(self.states)
2287+ blog_states = yield self.add_opposite_service_unit(self.states)
2288+ yield blog_states['service_relations'][-1].add_unit_state(
2289+ self.states['unit'])
2290+
2291 for i in range(1, 5):
2292- yield self.add_opposite_service_unit(
2293+ blog_states = yield self.add_opposite_service_unit(
2294 (yield self.add_relation_service_unit_to_another_endpoint(
2295 self.states,
2296 RelationEndpoint(
2297 "wordpress-%d" % i,
2298 "client-server", "db", "client"))))
2299+ yield blog_states['service_relations'][-1].add_unit_state(
2300+ self.states['unit'])
2301+
2302 yield self.lifecycle.start()
2303 yield self.wait_on_hook(
2304 sequence=["app-relation-joined", "app-relation-changed"])

Subscribers

People subscribed via source and target branches

to all changes: