Merge lp:~allanlesage/helipad/slurp-one into lp:helipad/ci

Proposed by Allan LeSage
Status: Needs review
Proposed branch: lp:~allanlesage/helipad/slurp-one
Merge into: lp:helipad/ci
Diff against target: 332 lines (+197/-61)
2 files modified
management/commands/jenkins_pull_ci.py (+59/-30)
tests/test_jenkins_pull_ci.py (+138/-31)
To merge this branch: bzr merge lp:~allanlesage/helipad/slurp-one
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
helipad-team Pending
Review via email: mp+177956@code.launchpad.net

Commit message

Enable pulling just one stack.

Description of the change

Here we can slurp just one stack, e.g. head.indicators ; I've tested a pull but have found that we're missing the integration_job being set in Cu2dStack, am addressing in another MP.

To post a comment you must log in.
Revision history for this message
Allan LeSage (allanlesage) wrote :

Syntax for this is ./manage.py jenkins_pull_ci -I head.indicators (or substitute your fav). cjohnston, fginther, what do you think of this? It seems like there would be other options available, e.g. update all builds relevant to a project (launchpad_project)--which is what we're doing for the update script. Maybe these two should be brought into alignment, e.g.

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

FAILED: Continuous integration, rev:34
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~allanlesage/helipad/slurp-one/+merge/177956/+edit-commit-message

http://s-jenkins:8080/job/helipad-ci-ci/60/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins:8080/job/helipad-ci-ci/60/rebuild

review: Needs Fixing (continuous-integration)

Unmerged revisions

34. By Allan LeSage

Add command-line option and tests.

33. By Allan LeSage

Use patch.stopall.

32. By Allan LeSage

Improve tests, having split function.

31. By Allan LeSage

Move the stack update to its own function.

30. By Allan LeSage

Add a comment and shift artifact to config for clarity.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'management/commands/jenkins_pull_ci.py'
--- management/commands/jenkins_pull_ci.py 2013-07-29 17:12:18 +0000
+++ management/commands/jenkins_pull_ci.py 2013-07-31 22:16:40 +0000
@@ -121,7 +121,7 @@
121 page = jenkins_get(url, as_json=False)121 page = jenkins_get(url, as_json=False)
122 return yaml.safe_load(page)122 return yaml.safe_load(page)
123123
124 def _get_artifact_url_list(self):124 def _get_config_url_list(self):
125 # Get the jenkins job which contains the data dump125 # Get the jenkins job which contains the data dump
126 jenkapi = jenkinsapi.api.Jenkins(settings.JENKINS_URL)126 jenkapi = jenkinsapi.api.Jenkins(settings.JENKINS_URL)
127 job = jenkapi.get_job(CUPSTREAM2DISTRO_CONFIG_DATA_SOURCE)127 job = jenkapi.get_job(CUPSTREAM2DISTRO_CONFIG_DATA_SOURCE)
@@ -138,40 +138,69 @@
138 break138 break
139 return artifact_list139 return artifact_list
140140
141 def initial_run(self):141 def _retrieve_config_url(self, stack_name, url_list):
142 url_list = self._get_artifact_url_list()142 """Find a regex match for stack_name in url_list."""
143143
144 # For each artifact
145 for url in url_list:144 for url in url_list:
146 # Parse the stack release and name from the url145 if re.search("{}.yaml".format(stack_name), url):
147 (release, config_name) = self._extract_release_and_config_name(url)146 return url
148 if config_is_on_blacklist(config_name):147 return None
149 logger.debug("Skipping cu2d config at {} due to blacklist "148
150 "match.".format(config_name))149 def stack_initial_run(self, stack_config_url):
151 continue150 # Parse the stack release and name from the url
152151 (release, config_name) = self._extract_release_and_config_name(
153 data = self._load_artifact(url)152 stack_config_url)
154 # Process the stack153 if config_is_on_blacklist(config_name):
155 if not data:154 logger.info("Skipping cu2d config at {} due to blacklist "
156 # Stack has no contents155 "match.".format(config_name))
157 logger.debug("No projects found in {}".format(url))156 return
158 continue157 data = self._load_artifact(stack_config_url)
159 if 'projects' not in data:158
160 # Stack has no projects159 # Process the stack
161 logger.debug("No projects found in {}".format(url))160 if not data:
162 continue161 # Stack has no contents
163 stack_name = self._create_stack(data, release)162 logger.debug("No projects found in {}".format(stack_config_url))
164163 return
165 # Process the projects164
166 if stack_name:165 if 'projects' not in data:
167 for project_name in data['projects']:166 # Stack has no projects
168 self._create_project(data, release, stack_name,167 logger.debug("No projects found in {}".format(stack_config_url))
169 project_name)168 return
170 return169 stack_name = self._create_stack(data, release)
170
171 if not stack_name:
172 logger.debug("Failed to create stack.")
173 return
174
175 # Process the projects
176 for project_name in data['projects']:
177 self._create_project(
178 data, release, stack_name, project_name)
179
180 def initial_run(self, stack_list=[]):
181 """Pull Jenkins builds and jobs relevant to stacks in given list.
182
183 Stacks as 'release.project', e.g. 'head.indicators', 'saucy.mir'.
184 """
185
186 # A Jenkins job dumps all .yaml configs from lp:cupstream2distro-config
187 # as artifacts in a Jenkins workspace, so we can wget 'em, e.g.
188 config_url_list = self._get_config_url_list()
189 if len(stack_list) == 0:
190 pull_url_list = config_url_list
191 else:
192 pull_url_list = []
193 for stack_name in stack_list:
194 config_url = self._retrieve_config_url(
195 stack_name, config_url_list)
196 if config_url is not None:
197 pull_url_list.append(config_url)
198 for config_url in pull_url_list:
199 self.stack_initial_run(config_url)
171200
172 def handle(self, *args, **options):201 def handle(self, *args, **options):
173 if options.get('initial_run'):202 if options.get('initial_run'):
174 self.initial_run()203 self.initial_run(stack_list=args)
175 else:204 else:
176 self.update_projects(project_list=args)205 self.update_projects(project_list=args)
177 self.update_cu2d_stacks()206 self.update_cu2d_stacks()
178207
=== modified file 'tests/test_jenkins_pull_ci.py'
--- tests/test_jenkins_pull_ci.py 2013-07-28 21:26:11 +0000
+++ tests/test_jenkins_pull_ci.py 2013-07-31 22:16:40 +0000
@@ -55,14 +55,23 @@
55 Command,55 Command,
56 '__init__',56 '__init__',
57 new=command_mock)57 new=command_mock)
58 self.load_artifact_patch = patch.object(
59 jenkins_pull_ci.Command,
60 '_load_artifact')
61 self.load_artifact_mock = self.load_artifact_patch.start()
62 self.get_config_url_list_patch = patch.object(
63 jenkins_pull_ci.Command,
64 '_get_config_url_list')
65 self.get_config_url_list_mock = self.get_config_url_list_patch.start()
58 self.command_patch.start()66 self.command_patch.start()
59 self.command = Command()67 self.command = Command()
60 self.logger_patch = patch('ci.models.logger')68 self.logger_patch = patch('ci.models.logger')
61 self.logger_patch.start()69 self.logger_patch.start()
6270
63
64 def tearDown(self):71 def tearDown(self):
65 self.command_patch.stop()72 self.command_patch.stop()
73 self.get_config_url_list_patch.stop()
74 self.load_artifact_patch.stop()
66 self.logger_patch.stop()75 self.logger_patch.stop()
6776
6877
@@ -73,6 +82,11 @@
73 result = self.command.handle(initial_run=True)82 result = self.command.handle(initial_run=True)
74 self.assertTrue(initial_run.called)83 self.assertTrue(initial_run.called)
7584
85 def test_handle_initial_run_some_stacks(self):
86 with patch.object(self.command, 'initial_run') as initial_run:
87 result = self.command.handle('fake_stack', initial_run=True)
88 initial_run.assert_called_with(stack_list=('fake_stack',))
89
76 def test_handle_update_projects_none(self):90 def test_handle_update_projects_none(self):
77 with patch.object(self.command, 'update_projects') as update_projects:91 with patch.object(self.command, 'update_projects') as update_projects:
78 result = self.command.handle()92 result = self.command.handle()
@@ -85,36 +99,130 @@
8599
86100
87class TestInitialRun(JenkinsPullCiCommandTestCase):101class TestInitialRun(JenkinsPullCiCommandTestCase):
88 def test_no_urls(self):102
89 self.command._get_artifact_url_list = MagicMock(return_value=[])103 def setUp(self):
90 self.command._load_artifact = MagicMock()104 super(TestInitialRun, self).setUp()
91 result = self.command.initial_run()105 self.stack_initial_run_patch = patch.object(
92 self.assertFalse(self.command._load_artifact.called)106 jenkins_pull_ci.Command,
107 'stack_initial_run')
108 self.stack_initial_run_mock = self.stack_initial_run_patch.start()
109 self.create_stack_patch = patch.object(
110 jenkins_pull_ci.Command,
111 '_create_stack')
112 self.create_stack_mock = self.create_stack_patch.start()
113 self.create_project_patch = patch.object(
114 jenkins_pull_ci.Command,
115 '_create_project')
116 self.create_project_mock = self.create_project_patch.start()
117
118 def tearDown(self):
119 super(TestInitialRun, self).tearDown()
120 patch.stopall()
121
122 def test_no_stack_given_no_stacks_initted(self):
123 self.get_config_url_list_mock.return_value = []
124 result = self.command.initial_run()
125 self.assertFalse(self.stack_initial_run_mock.called)
126
127 def test_no_stack_given_some_stacks_initted(self):
128 self.get_config_url_list_mock.return_value = [
129 'http://fake_stack_url/one']
130 result = self.command.initial_run()
131 self.stack_initial_run_mock.assert_called_with(
132 'http://fake_stack_url/one')
133
134 def test_one_stack_given_one_stack_initted(self):
135 fake_config_url = 'http://fake_jenkins_bla/head.indicators.yaml'
136 self.get_config_url_list_mock.return_value = [
137 fake_config_url]
138 result = self.command.initial_run(["head.indicators"])
139 self.stack_initial_run_mock.assert_called_with(fake_config_url)
140
141 def test_gibberish_stack_inits_nothing(self):
142 fake_config_url = 'http://fake_jenkins_bla/head.indicators.yaml'
143 self.get_config_url_list_mock.return_value = [
144 fake_config_url]
145 result = self.command.initial_run(["bla.indicators"])
146 self.stack_initial_run_mock.assert_not_called()
147
148
149class TestRetrieveConfigUrlFromList(JenkinsPullCiCommandTestCase):
150
151 def test_return_matching_config(self):
152 url_list = ['http://jenkins.url/artifacts/head.mir.yaml',
153 'http://jenkins.url/artifacts/head.indicators.yaml']
154 result = self.command._retrieve_config_url(
155 'head.indicators',
156 url_list)
157 self.assertEqual(
158 'http://jenkins.url/artifacts/head.indicators.yaml',
159 result)
160
161 def test_return_none_on_gibberish(self):
162 url_list = ['http://jenkins.url/artifacts/head.mir.yaml',
163 'http://jenkins.url/artifacts/head.indicators.yaml']
164 result = self.command._retrieve_config_url(
165 'head.flobnicator',
166 url_list)
167 self.assertEqual(None, result)
168
169
170class TestStackInitialRun(JenkinsPullCiCommandTestCase):
171
172 def setUp(self):
173 super(TestStackInitialRun, self).setUp()
174 self.stack_initial_run_patch = patch.object(
175 jenkins_pull_ci.Command,
176 'stack_initial_run')
177 self.create_stack_patch = patch.object(
178 jenkins_pull_ci.Command,
179 '_create_stack')
180 self.create_stack_mock = self.create_stack_patch.start()
181 self.create_project_patch = patch.object(
182 jenkins_pull_ci.Command,
183 '_create_project')
184 self.create_project_mock = self.create_project_patch.start()
185 self.extract_release_and_config_name_patch = patch.object(
186 jenkins_pull_ci.Command,
187 '_extract_release_and_config_name')
188 self.extract_release_and_config_name_mock = \
189 self.extract_release_and_config_name_patch.start()
190 self.extract_release_and_config_name_mock.return_value = (
191 'fake_release_name', 'fake_config_name')
192 self.config_is_on_blacklist_patch = patch(
193 'ci.management.commands.jenkins_pull_ci.config_is_on_blacklist')
194 self.config_is_on_blacklist_mock = \
195 self.config_is_on_blacklist_patch.start()
196
197 def tearDown(self):
198 super(TestStackInitialRun, self).tearDown()
199 patch.stopall()
200
201 def test_config_is_on_blacklist(self):
202 self.config_is_on_blacklist_mock.return_value = True
203 result = self.command.stack_initial_run("fake_url")
204 self.command._load_artifact.assert_not_called()
205 self.assertFalse(self.create_stack_mock.called)
206 self.assertFalse(self.create_project_mock.called)
93207
94 def test_no_data(self):208 def test_no_data(self):
95 self.command._get_artifact_url_list = MagicMock(209 self.config_is_on_blacklist_mock.return_value = False
96 return_value=['http://jenkins/blah/blah/release.stack.yaml'])210 self.load_artifact_mock.return_value = []
97 self.command._load_artifact = MagicMock(return_value=None)211 result = self.command.stack_initial_run("fake_url")
98 self.command._create_stack = MagicMock()
99 self.command._create_project = MagicMock()
100 result = self.command.initial_run()
101 self.command._load_artifact.assert_called()212 self.command._load_artifact.assert_called()
102 self.assertFalse(self.command._create_stack.called)213 self.assertFalse(self.create_stack_mock.called)
103 self.assertFalse(self.command._create_project.called)214 self.assertFalse(self.create_project_mock.called)
104215
105 def test_no_projects(self):216 def test_no_projects(self):
106 stack = {'name': 'fake'}217 self.config_is_on_blacklist_mock.return_value = False
107 self.command._get_artifact_url_list = MagicMock(218 self.load_artifact_mock.return_value = MagicMock(name='fake')
108 return_value=['http://jenkins/blah/blah/release.stack.yaml'])219 result = self.command.stack_initial_run("fake_url")
109 self.command._load_artifact = MagicMock(return_value=stack)
110 self.command._create_stack = MagicMock()
111 self.command._create_project = MagicMock()
112 result = self.command.initial_run()
113 self.command._load_artifact.assert_called()220 self.command._load_artifact.assert_called()
114 self.assertFalse(self.command._create_stack.called)221 self.assertFalse(self.create_stack_mock.called)
115 self.assertFalse(self.command._create_project.called)222 self.assertFalse(self.create_project_mock.called)
116223
117 def test_simple_stack(self):224 def test_simple_stack(self):
225 self.config_is_on_blacklist_mock.return_value = False
118 stack = {'name': 'fake',226 stack = {'name': 'fake',
119 'ppa': 'ppa:project/ppa',227 'ppa': 'ppa:project/ppa',
120 'series': 'saucy',228 'series': 'saucy',
@@ -122,15 +230,12 @@
122 'project-1': {230 'project-1': {
123 'ci': 'project-1-ci',231 'ci': 'project-1-ci',
124 'autolanding': 'project-1-autolanding'}}}232 'autolanding': 'project-1-autolanding'}}}
125 self.command._get_artifact_url_list = MagicMock(
126 return_value=['http://jenkins/blah/blah/release.stack.yaml'])
127 self.command._load_artifact = MagicMock(return_value=stack)233 self.command._load_artifact = MagicMock(return_value=stack)
128 self.command._create_stack = MagicMock()234 result = self.command.stack_initial_run("fake_url")
129 self.command._create_project = MagicMock()235 self.load_artifact_mock.assert_called()
130 result = self.command.initial_run()236 self.create_stack_mock.assert_called_with(
131 self.command._load_artifact.assert_called()237 stack, "fake_release_name")
132 self.command._create_stack.assert_called()238 self.create_project_mock.assert_called()
133 self.command._create_project.assert_called()
134239
135240
136class TestExtractReleaseAndConfigName(JenkinsPullCiCommandTestCase):241class TestExtractReleaseAndConfigName(JenkinsPullCiCommandTestCase):
@@ -189,3 +294,5 @@
189 self.command.update_projects(294 self.command.update_projects(
190 [MagicMock(name="fake_project")])295 [MagicMock(name="fake_project")])
191 update_jenkins_job_mock.assert_called_with(fake_jenkins_job)296 update_jenkins_job_mock.assert_called_with(fake_jenkins_job)
297
298

Subscribers

People subscribed via source and target branches

to all changes: