Merge lp:~pwlars/uci-engine/versioned-imagebuild into lp:uci-engine

Proposed by Paul Larson
Status: Merged
Approved by: Paul Larson
Approved revision: 701
Merged at revision: 704
Proposed branch: lp:~pwlars/uci-engine/versioned-imagebuild
Merge into: lp:uci-engine
Diff against target: 154 lines (+63/-16)
5 files modified
ci-utils/ci_utils/__init__.py (+6/-0)
image-builder/imagebuilder/cloud_image.py (+16/-0)
image-builder/imagebuilder/run_worker.py (+19/-9)
image-builder/imagebuilder/tests/test_worker.py (+19/-0)
lander/bin/lander_service_wrapper.py (+3/-7)
To merge this branch: bzr merge lp:~pwlars/uci-engine/versioned-imagebuild
Reviewer Review Type Date Requested Status
Vincent Ladeuil (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Paul Larson Needs Resubmitting
Review via email: mp+227666@code.launchpad.net

Commit message

New version of the imagebuild API that simply copies an image into glance, rather than trying to modify it.

Description of the change

New version of the imagebuild changes I was working on. This one implements a versioning scheme for the API and also handles making sure the lander doesn't send extra parameters. I'd like to do a little more testing on this first, but wanted to get it out there to look at in an easier form.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:699
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1136/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1136/rebuild

review: Needs Fixing (continuous-integration)
700. By Paul Larson

flake8 fixups

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:700
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1137/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1137/rebuild

review: Approve (continuous-integration)
Revision history for this message
Vincent Ladeuil (vila) wrote :

This is clearer to expose what each version of the API is doing. Thanks !

For the sake of the thought experiment, if I get that right, your plan would be to have the 'lander/bin/lander_service_wrapper.py' part that could be an independent MP landed/deployed last.

So all workers start implementing the new API and can be deployed while they still support the old API.

Then the lander one is deployed and tickets start using that new API.

Is that correct ?

I have several questions I don't have a good answer for yet:

- what's the upgrade story for the webops ?

- what happens if an old worker receives a request for the new API (that one is the most annoying I think ;) ?

- for how long do we keep old lander/workers running ?

- how can we make the "right" workers talk to each other ?

- do we have different kind of API migrations ?

- can we try to sort out the simplest first ? ;-)

- is this MP the right place to ask ?

None of the above have to be answered right now nor should they block that experiment ;)

But a first round of answers can help define the next steps.

review: Needs Information
Revision history for this message
Paul Larson (pwlars) wrote :

> I have several questions I don't have a good answer for yet:
>
> - what's the upgrade story for the webops ?
For a small enough deployment, I think the way we've always done upgrades would work. The risk here would be that in that window while upgrading, a ticket comes in and makes it all the way through to the imagebuilder before the upgrade is complete. This would require the fastest ppa build ever I guess, but we should still be careful of course. The situation we don't want to end up with is where the lander sends a request for an API version that NONE of the imagebuild workers (all one of them) support. For a more paranoid upgrade, see below.

> - what happens if an old worker receives a request for the new API (that one
> is the most annoying I think ;) ?
I think the idea is that to do the upgrade as carefully as possible, you upgrade things from the bottom up. Upgrade workers first. If they get a ticket for the previous version of the API, that's ok. Once they are all upgraded, you upgrade the lander. Also, this makes me realize that I think we might have much deeper problems in the upgrade don't we? What happens if I the upgrade happens while a component is in the middle of something? Does it get upgraded part-way through a ticket?

>
> - for how long do we keep old lander/workers running ?
I think as short a time as possible for now. Eventually we might want to explore bigger testing scenarios where we have some deployed with the new version and some with the old and ease into the upgrade as we see tickets working. I think we need a lot of things in place before we can really do that though.
>
> - how can we make the "right" workers talk to each other ?
That, I don't know. I've not seen a good solution with rabbit to do this. Best I can suggest is perhaps we should reject the request somehow by sending a message back to the lander, and the lander retries the request, maybe after a delay? It's imprecise, but would hopefully result in either trying a different worker or retrying the same worker after it upgrades properly. Also, if we're really sure the workers get upgraded before the lander, then they are all the "right" one to send the request to.
>
> - do we have different kind of API migrations ?
Not sure what you mean here.
>
> - can we try to sort out the simplest first ? ;-)
This MP seemed like as good a place to start as any.
>
> - is this MP the right place to ask ?
Probably not, I think we should have a broader discussion about this.
>
> None of the above have to be answered right now nor should they block that
> experiment ;)
>
> But a first round of answers can help define the next steps.

review: Needs Resubmitting
701. By Paul Larson

turns out the amqp worker still needs the ticket id, even if the image build worker doesn't

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:701
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1151/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1151/rebuild

review: Approve (continuous-integration)
Revision history for this message
Vincent Ladeuil (vila) wrote :

Let's land !

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ci-utils/ci_utils/__init__.py'
2--- ci-utils/ci_utils/__init__.py 2014-07-17 20:05:30 +0000
3+++ ci-utils/ci_utils/__init__.py 2014-07-22 21:12:41 +0000
4@@ -17,6 +17,12 @@
5 import tempfile
6
7
8+# Current supported API Versions
9+API_VERSIONS = {
10+ 'image_builder': '20140721',
11+}
12+
13+
14 SUPPORTED_SERIES = [
15 'precise',
16 'trusty',
17
18=== modified file 'image-builder/imagebuilder/cloud_image.py'
19--- image-builder/imagebuilder/cloud_image.py 2014-07-17 16:00:41 +0000
20+++ image-builder/imagebuilder/cloud_image.py 2014-07-22 21:12:41 +0000
21@@ -300,6 +300,22 @@
22 runcmd('qemu-nbd -d /dev/%s' % self.nbd_dev)
23
24
25+def get_image(image, status_cb=None):
26+ '''Copy the requested cloud image to glance'''
27+ # API Version 20140721
28+ if not status_cb:
29+ status_cb = lambda x: 0 # just provide a no-op
30+
31+ with ci_utils.TmpDir() as tmpdir:
32+ status_cb('downloading %s...' % image)
33+ image_path = _download(image, tmpdir)
34+ glance = ImageStore(ci_utils.unit_config.get_auth_config())
35+ upload_filename = str(uuid.uuid1()) + '.img'
36+ status_cb('Uploading image to glance as %s' % upload_filename)
37+ glance.image_create(upload_filename, image_path)
38+ return upload_filename
39+
40+
41 def modify_image(image, repos, series, packages, ds, status_cb=None):
42 '''Add packges and PPAs to a cloud image mounted in a chroot'''
43 if not status_cb:
44
45=== modified file 'image-builder/imagebuilder/run_worker.py'
46--- image-builder/imagebuilder/run_worker.py 2014-07-15 20:20:00 +0000
47+++ image-builder/imagebuilder/run_worker.py 2014-07-22 21:12:41 +0000
48@@ -19,6 +19,7 @@
49 from ci_utils import amqp_utils, amqp_worker
50
51 from imagebuilder.cloud_image import modify_image
52+from imagebuilder.cloud_image import get_image
53
54
55 class ImageBuildWorker(amqp_worker.AMQPWorker):
56@@ -47,22 +48,31 @@
57 js_status.add_fail('integration test', 'has not run')
58
59 def handle_request(self, params, log):
60+
61 image = params['image']
62- repos = params['ppa_list']
63- series = params['series']
64- packages = params['package_list']
65 trigger = params['progress_trigger']
66- ticket_id = params['ticket_id']
67- ds = self._create_data_store(ticket_id)
68
69 def status_cb(msg):
70 log.info(msg)
71 amqp_utils.progress_update(trigger, {'message': msg})
72
73- image_id, location = modify_image(image, repos, series, packages,
74- ds, status_cb)
75- retval = {'image_id': image_id}
76- self._create_artifact(image_id, location, retval, type='IMAGE')
77+ #Check API version
78+ api = params.get('api_version')
79+ if api == '20140721':
80+ image_id = get_image(image, status_cb)
81+ retval = {'image_id': image_id}
82+ else:
83+ repos = params['ppa_list']
84+ series = params['series']
85+ packages = params['package_list']
86+ ticket_id = params['ticket_id']
87+ ds = self._create_data_store(ticket_id)
88+
89+ image_id, location = modify_image(image, repos, series, packages,
90+ ds, status_cb)
91+ retval = {'image_id': image_id}
92+ self._create_artifact(image_id, location, retval, type='IMAGE')
93+
94 return amqp_utils.progress_completed, retval
95
96
97
98=== modified file 'image-builder/imagebuilder/tests/test_worker.py'
99--- image-builder/imagebuilder/tests/test_worker.py 2014-06-27 18:05:26 +0000
100+++ image-builder/imagebuilder/tests/test_worker.py 2014-07-22 21:12:41 +0000
101@@ -47,3 +47,22 @@
102 self.worker.handle_request(params, mocked_log)
103
104 mocked_create_data_store.assert_called_once_with(params['ticket_id'])
105+
106+ @mock.patch('imagebuilder.run_worker.get_image')
107+ @mock.patch('ci_utils.amqp_utils.progress_completed')
108+ def test_worker_get_image(self, mocked_progress_completed,
109+ mocked_get_image):
110+ params = {
111+ 'api_version': '20140721',
112+ 'image': 'http://fake/location',
113+ 'ticket_id': 1,
114+ 'series': 'saucy',
115+ 'progress_trigger': 'ticket_1_trigger'
116+ }
117+
118+ mocked_log = mock.MagicMock()
119+ mocked_get_image.return_value = ('fake_image_id')
120+
121+ self.worker.handle_request(params, mocked_log)
122+
123+ mocked_get_image.assert_called_once()
124
125=== modified file 'lander/bin/lander_service_wrapper.py'
126--- lander/bin/lander_service_wrapper.py 2014-07-21 06:53:05 +0000
127+++ lander/bin/lander_service_wrapper.py 2014-07-22 21:12:41 +0000
128@@ -28,6 +28,7 @@
129 from ci_utils import (
130 amqp_utils,
131 dump_stack,
132+ API_VERSIONS,
133 )
134
135
136@@ -218,16 +219,11 @@
137
138 # Extract the image parameters from the master request parameters
139 request_parameters = ticket.get_full_ticket()
140- ppa_list = [
141- workflow.get_step('ppa_assigner')['ppa'],
142- request_parameters['master_ppa'],
143- ]
144+
145 params = {
146 'ticket_id': workflow.ticket_id,
147+ 'api_version': API_VERSIONS.get('image_builder'),
148 'image': request_parameters['base_image'],
149- 'series': request_parameters['series'],
150- 'package_list': _get_binary_packages(ticket, request_parameters),
151- 'ppa_list': ppa_list,
152 'progress_trigger': queue,
153 }
154 ticket.set_image_waiting()

Subscribers

People subscribed via source and target branches