Merge lp:~vila/ubuntu-ci-services-itself/ppa-assigner-integration-test into lp:ubuntu-ci-services-itself

Proposed by Vincent Ladeuil
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: 49
Merged at revision: 51
Proposed branch: lp:~vila/ubuntu-ci-services-itself/ppa-assigner-integration-test
Merge into: lp:ubuntu-ci-services-itself
Diff against target: 210 lines (+174/-2)
4 files modified
README (+55/-0)
TRICKS (+66/-0)
juju-deployer/ppa-assigner.yaml (+5/-2)
tests/ppa_assigner/test.py (+48/-0)
To merge this branch: bzr merge lp:~vila/ubuntu-ci-services-itself/ppa-assigner-integration-test
Reviewer Review Type Date Requested Status
Para Siva (community) Approve
Review via email: mp+199513@code.launchpad.net

Commit message

First integration tests for ppa assigner using amulet and juju deployer.

Description of the change

Here is a first succesful attempt at running an integration test using juju
deployer and amulet.

Andy's ppa-assigner juju deployer script was chosen for that, sorry Andy ;)
Or rather, thanks Andy !

With the help of Martin we diagnosed hundreds^W a bunch of various issues
that we tried to capture but... long story short, we had to take a few
shortcuts ;)

Still, any of us should be able to reproduce the experience following the
instructions in the README.

To summarize:
- cloud credentials are assumed to be available, so source your novarc first.
- sshuttle needs to be setup manually each time you run 'juju bootstrap'

- you need to install amulet from lp:~vila/amulet/ci and setup your env
  appropriately (my PYTHONPATH includes ~/lib/python so I create a symlink
  there to the amulet sources)

From that point, the test run is automatic and can be re-run at will.

It will deploy the ppa-assigner or reuse the one you deploy before running
the test.

A single test has been created for now and the next step will be to turn it
into a regular python unittest one but we wanted to share that first
significant step.

That test issue a request to get the list of the ppas. The request succeeds
(200, OK) but the objects in the content is empty. I don't know if that's
because we've mis-configured something or a genuine bug, please advise !

But more importantly, try this at home and tell us what works and what
doesn't so we can find the Right Way to automate this further.

To post a comment you must log in.
Revision history for this message
Evan (ev) wrote :

On 18 December 2013 17:51, Vincent Ladeuil <email address hidden> wrote:
> === added file 'tests/ppa_assigner/test.py'
> --- tests/ppa_assigner/test.py 1970-01-01 00:00:00 +0000
> +++ tests/ppa_assigner/test.py 2013-12-18 17:42:04 +0000
> +opd = os.path.dirname
> +
> +root_dir = opd(opd(opd((os.path.abspath(__file__)))))

Surely:

os.path.abspath(os.path.join ('../../../', __file__))

Revision history for this message
Andy Doan (doanac) wrote :

The README is great info, but its almost too much and too specific to canonistack. I wonder if we should create something like "README-juju" or README-canonistack for that stuff. I'm talking roughly about:

18 +Configure juju for canonistack (or whatever you feel is appropriate), make
to
50 +You'll need to interrupt this command once you're run 'juju destroy-environment' to ensure you didn't leave pending connections to juju instances that should not exist anymore.

Can you explain why:

142 + expose: True

was needed? I'm trying to understand if my charming was done incorrectly or amulet has some need of that or what?

I'm not sure about:

185 + script = {depl.juju_env: jd_script.values()[0]}

Should we be explicit and load jd_script['ppa-assigner-staging']?

As for the remainder of the script - i can see plenty of room for us to expand there soon! thanks

Revision history for this message
Vincent Ladeuil (vila) wrote :

> On 18 December 2013 17:51, Vincent Ladeuil <email address hidden> wrote:
> > === added file 'tests/ppa_assigner/test.py'
> > --- tests/ppa_assigner/test.py 1970-01-01 00:00:00 +0000
> > +++ tests/ppa_assigner/test.py 2013-12-18 17:42:04 +0000
> > +opd = os.path.dirname
> > +
> > +root_dir = opd(opd(opd((os.path.abspath(__file__)))))
>
> Surely:
>
> os.path.abspath(os.path.join ('../../../', __file__))

Yeah, right, as explained using '/' instead of os.dirsep is not portable. We don't care here, but old good habits... never die ;)

Plus, if __file__ is a relative path you end up with os.path.abspath('') which won't work I think.

So, yeah, this is hackish but should disappear so, I'll add a comment to that effect.

Revision history for this message
Vincent Ladeuil (vila) wrote :

> The README is great info, but its almost too much and too specific to
> canonistack. I wonder if we should create something like "README-juju" or
> README-canonistack for that stuff. I'm talking roughly about:
>
> 18 +Configure juju for canonistack (or whatever you feel is appropriate),
> make
> to
> 50 +You'll need to interrupt this command once you're run 'juju destroy-
> environment' to ensure you didn't leave pending connections to juju instances
> that should not exist anymore.

Will do.

>
>
> Can you explain why:
>
> 142 + expose: True
>
> was needed? I'm trying to understand if my charming was done incorrectly or
> amulet has some need of that or what?

Damn, sorry, I wanted to explain that part and will add a comment.

According to Martin, you can try to set it to False and from your desktop you shouldn't be able to access it anymore. So it's required to allow access from outside the cloud.

>
> I'm not sure about:
>
> 185 + script = {depl.juju_env: jd_script.values()[0]}
>
> Should we be explicit and load jd_script['ppa-assigner-staging']?

Well, we chose not to so if that name change, the test won't have to be updated.

>
> As for the remainder of the script - i can see plenty of room for us to expand
> there soon! thanks

Great.

Revision history for this message
Vincent Ladeuil (vila) wrote :

> > The README is great info, but its almost too much and too specific to
> > canonistack. I wonder if we should create something like "README-juju" or
> > README-canonistack for that stuff. I'm talking roughly about:
> >
> > 18 +Configure juju for canonistack (or whatever you feel is
> appropriate),
> > make
> > to
> > 50 +You'll need to interrupt this command once you're run 'juju
> destroy-
> > environment' to ensure you didn't leave pending connections to juju
> instances
> > that should not exist anymore.
>
> Will do.

Hmm, that's only ~30 lines, so I'd rather make sure we properly curate a single README file when these steps are automated rather than introducing several READMEs.

I feel more comfortable with too much instructions rather than too little (despite the discussion about shuttle on the mailing list we still had to re-discover some of the tricks explained here).

Revision history for this message
Chris Johnston (cjohnston) wrote :

Voting does not meet specified criteria. Required: Approve >= 1. Got: 1 Pending.

Revision history for this message
Para Siva (psivaa) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2013-12-12 22:34:51 +0000
3+++ README 2013-12-18 17:42:04 +0000
4@@ -64,3 +64,58 @@
5
6 Each service should include a juju-deployer config under the juju-deployer
7 directory.
8+
9+ppa assigner testing with juju-deployer and amulet
10+--------------------------------------------------
11+
12+Install pre-requisites:
13+
14+$ sudo apt-get install juju juju-deployer python-httplib2
15+
16+Branch lp:amulet and make it reachable.
17+
18+Configure juju for canonistack (or whatever you feel is appropriate), make
19+sure you select the region there (see OS_REGION_NAME/NOVA_REGION):
20+
21+$ . ~/.canonistack/novarc
22+
23+Create a proper ~/.juju/environments.yaml, example:
24+
25+environments:
26+ ## https://juju.ubuntu.com/docs/config-openstack.html
27+ lcy02:
28+ type: openstack
29+ admin-secret: <your admin secret, juju can auto-create it>
30+ # Globally unique swift bucket name
31+ control-bucket: <you control-bucket secret, juju can auto-create it>
32+ # Usually set via the env variable OS_REGION_NAME, but can be specified here
33+ region: lcy02
34+
35+$ juju bootstrap
36+
37+Get the IP address of the juju state server from 'nova list --name machine-0':
38+
39+$ nova list --name machine-0 --fields networks
40++--------------------------------------+-------------------------+
41+| ID | Networks |
42++--------------------------------------+-------------------------+
43+| 0463d358-750c-4fc4-b38b-a618e2e7d034 | canonistack=10.55.32.64 |
44++--------------------------------------+-------------------------+
45+
46+Start sshuttle in the foreground from a different terminal where you've sourced your credentials (we need a way to get that in a more script friendly way):
47+
48+$ sshuttle -r ubuntu@10.55.32.64 10.55.0.0/16
49+
50+You'll need to interrupt this command once you're run 'juju destroy-environment' to ensure you didn't leave pending connections to juju instances that should not exist anymore.
51+
52+Deploy ppa-assigner (mostly a smoke check, amulet will deploy it anyway or
53+reuse the already deployed one):
54+
55+$ juju-deployer -c juju-deployer/ppa-assigner.yaml ppa-assigner-staging
56+
57+Run the ppa assigner test:
58+
59+$ tests/ppa_assigner/test.py
60+
61+It currently fails because an empty ppa list is returned, feedback/fixes
62+welcome :-)
63
64=== added file 'TRICKS'
65--- TRICKS 1970-01-01 00:00:00 +0000
66+++ TRICKS 2013-12-18 17:42:04 +0000
67@@ -0,0 +1,66 @@
68+====================================================
69+Tricks used while debugging juju/juju=deployer stuff
70+====================================================
71+
72+Setup a public IP for the juju state server
73+===========================================
74+
75+Used when trying to allow juju status to be reached without settting up
76+~/.ssh/config to proxy via chinstrap.canonistack.com.
77+
78+Declare a public ip address where we can connect to the juju state server on
79+canonistack via ssh.
80+
81+$ OS_REGION_NAME=lcy02 nova list
82++--------------------------------------+----------------------+--------+------------+-------------+--------------------------+
83+| ID | Name | Status | Task State | Power State | Networks |
84++--------------------------------------+----------------------+--------+------------+-------------+--------------------------+
85+| a5f350da-b576-4e4e-be7e-ee639cbb9683 | juju-lcy02-machine-0 | ACTIVE | None | Running | canonistack=10.55.32.186 |
86++--------------------------------------+----------------------+--------+------------+-------------+--------------------------+
87+
88+The ID above (a5f350da-b576-4e4e-be7e-ee639cbb9683) is the juju state server.
89+
90+We need a public IP to reach it:
91+
92+$ OS_REGION_NAME=lcy02 nova floating-ip-create
93++---------------+-------------+----------+-------+
94+| Ip | Instance Id | Fixed Ip | Pool |
95++---------------+-------------+----------+-------+
96+| 162.213.35.63 | None | None | lcy02 |
97++---------------+-------------+----------+-------+
98+
99+Assign that IP address to the juju state server:
100+
101+$ OS_REGION_NAME=lcy02 nova add-floating-ip a5f350da-b576-4e4e-be7e-ee639cbb9683 162.213.35.63
102+
103+This was used to try to get juju-deployer working but didn't work in the end
104+as canonistack has an known issue about public IPs routing from inside the
105+region.
106+
107+This was diagnosed from, connecting to some juju instance:
108+
109+$ ssh 10.55.32.111
110+
111+ubuntu@juju-lcy02-machine-1:~$ cd /var/log/juju
112+ubuntu@juju-lcy02-machine-1:/var/log/juju$ tail machine-1.log
113+
114+2013-12-17 17:52:07 INFO juju runner.go:245 worker: restarting "api" in 3s
115+2013-12-17 17:52:10 INFO juju runner.go:253 worker: start "api"
116+2013-12-17 17:52:10 INFO juju apiclient.go:111 state/api: dialing "wss://162.213.35.63:17070/"
117+2013-12-17 17:53:13 ERROR juju apiclient.go:116 state/api: websocket.Dial wss://162.213.35.63:17070/: dial tcp 162.213.35.63:17070: connection timed out
118+
119+Which shows that canonistack can't reach a public IP from inside the cloud itself :-/
120+
121+Make sure juju tears down stuff properly
122+========================================
123+
124+After 'juju destroy-environment' make sure 'nova list' also comes back
125+empty.
126+
127+
128+amulet things it has charms in the current directory
129+====================================================
130+
131+We need:
132+vila:~/ci/ubuntu-ci-services-itself/trunk :) $ ln -s precise/python-django django
133+
134
135=== modified file 'juju-deployer/ppa-assigner.yaml'
136--- juju-deployer/ppa-assigner.yaml 2013-12-13 20:00:59 +0000
137+++ juju-deployer/ppa-assigner.yaml 2013-12-18 17:42:04 +0000
138@@ -2,6 +2,7 @@
139 series: precise
140 services:
141 django:
142+ expose: True
143 charm: python-django
144 branch: lp:~canonical-ci-engineering/charms/precise/python-django/ci-services
145 options:
146@@ -23,5 +24,7 @@
147 options:
148 python_path: /srv/django/ci-utils:/srv/django/ppa-assigner
149 relations:
150- - [django, "postgres:db"]
151- - [django, gunicorn]
152+ # Relations should be explicit as amulet can't infer them otherwise
153+ # even if there is a single one
154+ - ["django:pgsql", "postgres:db"]
155+ - ["django:wsgi", "gunicorn:wsgi-file"]
156
157=== added directory 'tests'
158=== added directory 'tests/ppa_assigner'
159=== added file 'tests/ppa_assigner/test.py'
160--- tests/ppa_assigner/test.py 1970-01-01 00:00:00 +0000
161+++ tests/ppa_assigner/test.py 2013-12-18 17:42:04 +0000
162@@ -0,0 +1,48 @@
163+#!/usr/bin/env python
164+
165+import json
166+import httplib2
167+import yaml
168+import os
169+import sys
170+
171+opd = os.path.dirname
172+
173+root_dir = opd(opd(opd((os.path.abspath(__file__)))))
174+
175+import amulet
176+
177+def main():
178+ # FIXME: Running amulet with sentries is vurrently bogus in multiple ways
179+ # and we can get juju status ourselves -- vila 2013-12-18
180+ depl = amulet.Deployment(sentries=False)
181+ jd_path = os.path.join(root_dir, 'juju-deployer', 'ppa-assigner.yaml')
182+ with open(jd_path) as f:
183+ jd_script = yaml.safe_load(f.read())
184+ # Override the juju env provided by ppa-assigner or amulet gets confused
185+ script = {depl.juju_env: jd_script.values()[0]}
186+ depl.load(script)
187+ # This is a no-op if (as expected) the ppa-assigner has already been
188+ # deployed, but it *will* be deployed if needed anyway.
189+ depl.setup()
190+
191+ status = amulet.waiter.status(depl.juju_env)
192+ units = status['services']['django']['units']
193+ ip = units['django/0']['public-address']
194+ port, _ = units['django/0']['open-ports'][0].split('/')
195+ # Issue a simple command against the ppa assigner API
196+ url = 'http://{}:{}/api/v1/ppa'.format(ip, port)
197+ client = httplib2.Http()
198+ resp, content = client.request(url, 'GET')
199+ jcontent = json.loads(content)
200+ # Target
201+ # self.assertEqual('200', resp['status'])
202+ # self.assertNotEqual([], jcontent['objects'])
203+ if '200' != resp['status']:
204+ sys.exit(1)
205+ if [] == jcontent['objects']:
206+ sys.exit(2)
207+
208+
209+if __name__ == "__main__":
210+ main()

Subscribers

People subscribed via source and target branches