Merge lp:~timrchavez/offspring/fix-lp-1095621 into lp:offspring
- fix-lp-1095621
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Kevin McDermott |
Approved revision: | 164 |
Merged at revision: | 163 |
Proposed branch: | lp:~timrchavez/offspring/fix-lp-1095621 |
Merge into: | lp:offspring |
Diff against target: |
374 lines (+246/-13) 9 files modified
lib/offspring/master/master.py (+2/-1) lib/offspring/master/models.py (+1/-0) lib/offspring/master/tests/helpers.py (+2/-1) lib/offspring/master/tests/test_master.py (+21/-7) lib/offspring/web/queuemanager/admin.py (+5/-2) lib/offspring/web/queuemanager/migrations/0003_auto__add_field_lexbuilder_is_retired.py (+173/-0) lib/offspring/web/queuemanager/models.py (+1/-0) lib/offspring/web/queuemanager/tests/test_views.py (+38/-0) lib/offspring/web/urls.py (+3/-2) |
To merge this branch: | bzr merge lp:~timrchavez/offspring/fix-lp-1095621 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
David Murphy (community) | Needs Information | ||
Kevin McDermott | Approve | ||
Review via email: mp+165286@code.launchpad.net |
Commit message
Description of the change
Introduce a 'is_retired' attribute for Lexbuilder which denotes that the builder has permanently been removed from service. This is different from 'is_active=False' which denotes that a builder is not accepting build jobs due to some intermittent disruption like an on-going upgrade or downtime caused by hardware failure. The "is_retired" flag will hide builders when set to true in the Builders List view and 404 on Builders Detail view. They will not get scanned by the master nor selected for building. They will remain visible in the admin (but the is_retired flag is exposed and filterable for the admin's convenience)
David Murphy (schwuk) wrote : | # |
81 + def test_create_
Should that not be "test_get_
Timothy R. Chavez (timrchavez) wrote : | # |
Good catch Dave. Yep.
- 165. By Timothy R. Chavez
-
Fix sed replace error in test name.
Preview Diff
1 | === modified file 'lib/offspring/master/master.py' |
2 | --- lib/offspring/master/master.py 2013-03-06 21:42:53 +0000 |
3 | +++ lib/offspring/master/master.py 2013-05-23 13:57:23 +0000 |
4 | @@ -97,7 +97,7 @@ |
5 | self.is_online = False |
6 | |
7 | def _get_builders(self): |
8 | - return self.db_store.find(Lexbuilder) |
9 | + return self.db_store.find(Lexbuilder, Lexbuilder.is_retired == False) |
10 | |
11 | def scanSlaves(self): |
12 | """ |
13 | @@ -190,6 +190,7 @@ |
14 | Lexbuilder.current_state == LexbuilderSlave.STATE_IDLE, |
15 | Lexbuilder.is_active == True, |
16 | Lexbuilder.is_okay == True, |
17 | + Lexbuilder.is_retired == False, |
18 | Lexbuilder.machine_type.is_in(builder_types) |
19 | ).order_by(Lexbuilder.id).first() |
20 | if builder is not None: |
21 | |
22 | === modified file 'lib/offspring/master/models.py' |
23 | --- lib/offspring/master/models.py 2012-03-08 18:30:04 +0000 |
24 | +++ lib/offspring/master/models.py 2013-05-23 13:57:23 +0000 |
25 | @@ -34,6 +34,7 @@ |
26 | updated_at = DateTime() |
27 | is_active = Bool(default=True) |
28 | is_okay = Bool(default=True) |
29 | + is_retired = Bool(default=False) |
30 | current_job_id = Int() |
31 | current_job = Reference(current_job_id, "BuildResult.id") |
32 | notes = Unicode() |
33 | |
34 | === modified file 'lib/offspring/master/tests/helpers.py' |
35 | --- lib/offspring/master/tests/helpers.py 2013-03-05 11:31:40 +0000 |
36 | +++ lib/offspring/master/tests/helpers.py 2013-05-23 13:57:23 +0000 |
37 | @@ -177,7 +177,8 @@ |
38 | "'UTC'), " |
39 | "current_job_id INTEGER, " |
40 | "is_okay BOOLEAN NOT NULL, " |
41 | - "is_active BOOLEAN NOT NULL DEFAULT TRUE)") |
42 | + "is_active BOOLEAN NOT NULL DEFAULT TRUE, " |
43 | + "is_retired BOOLEAN NOT NULL DEFAULT FALSE)") |
44 | |
45 | self.connection.execute( |
46 | "CREATE TABLE buildrequests (" |
47 | |
48 | === modified file 'lib/offspring/master/tests/test_master.py' |
49 | --- lib/offspring/master/tests/test_master.py 2013-03-06 21:42:53 +0000 |
50 | +++ lib/offspring/master/tests/test_master.py 2013-05-23 13:57:23 +0000 |
51 | @@ -29,17 +29,18 @@ |
52 | return LexbuilderMaster(self.config, self.db_store, |
53 | mailer=self.mailer.mail) |
54 | |
55 | - def create_builder(self, current_state=Slave.STATE_IDLE, arch=None): |
56 | + def create_builder(self, **kwargs): |
57 | """ |
58 | Create and add a Lexbuilder to the store. |
59 | """ |
60 | - builder = Lexbuilder(u"http://myhost/") |
61 | - builder.name = u"test-builder" |
62 | + builder = Lexbuilder(kwargs.get("uri", u"http://myhost/")) |
63 | + builder.name = unicode(kwargs.get("name", "test-builder")) |
64 | builder.previous_state = Slave.STATE_IDLE |
65 | - builder.current_state = current_state |
66 | - builder.is_active = True |
67 | - builder.is_okay = True |
68 | - builder.machine_type = arch or self.project.arch |
69 | + builder.current_state = kwargs.get("current_state", Slave.STATE_IDLE) |
70 | + builder.is_active = kwargs.get("is_active", True) |
71 | + builder.is_okay = kwargs.get("is_okay", True) |
72 | + builder.is_retired = kwargs.get("is_retired", False) |
73 | + builder.machine_type = kwargs.get("arch", self.project.arch) |
74 | self.db_store.add(builder) |
75 | return builder |
76 | |
77 | @@ -218,6 +219,19 @@ |
78 | "Scan cycle completed\n" % {"builder": builder.name}, |
79 | log_file.getvalue()) |
80 | |
81 | + def test_get_builders_does_not_return_retired_builders(self): |
82 | + """ |
83 | + Test that retired builders are not returned by _get_builders() which |
84 | + is the function the scanner uses to get a list of builders to scan. |
85 | + """ |
86 | + master = self.get_master() |
87 | + builder1 = self.create_builder(name="BuilderA", is_retired=True) |
88 | + builder2 = self.create_builder(name="BuilderB", is_active=False) |
89 | + builder3 = self.create_builder(name="BuilderC") |
90 | + builders = master._get_builders() |
91 | + self.assertEqual(2, builders.count()) |
92 | + self.assertEqual(["BuilderB", "BuilderC"], list(builders.values(Lexbuilder.name))) |
93 | + |
94 | def test_dispatch_builds(self): |
95 | """ |
96 | dispatchBuilds sends BuildRequests to a slave. |
97 | |
98 | === modified file 'lib/offspring/web/queuemanager/admin.py' |
99 | --- lib/offspring/web/queuemanager/admin.py 2013-05-09 14:27:56 +0000 |
100 | +++ lib/offspring/web/queuemanager/admin.py 2013-05-23 13:57:23 +0000 |
101 | @@ -19,8 +19,11 @@ |
102 | |
103 | |
104 | class LexbuilderAdmin(admin.ModelAdmin): |
105 | - list_display = ('name', 'machine_type', 'is_active', 'is_okay', 'current_state', 'current_build', 'updated_at') |
106 | - list_filter = ['current_state', 'machine_type', 'is_active', 'is_okay'] |
107 | + list_display = ('name', 'machine_type', 'is_active', 'is_okay', |
108 | + 'is_retired', 'current_state', 'current_build', |
109 | + 'updated_at') |
110 | + list_filter = ['current_state', 'machine_type', 'is_active', |
111 | + 'is_okay', 'is_retired'] |
112 | actions=['make_enabled', 'make_disabled'] |
113 | |
114 | def make_disabled(self, request, queryset): |
115 | |
116 | === added file 'lib/offspring/web/queuemanager/migrations/0003_auto__add_field_lexbuilder_is_retired.py' |
117 | --- lib/offspring/web/queuemanager/migrations/0003_auto__add_field_lexbuilder_is_retired.py 1970-01-01 00:00:00 +0000 |
118 | +++ lib/offspring/web/queuemanager/migrations/0003_auto__add_field_lexbuilder_is_retired.py 2013-05-23 13:57:23 +0000 |
119 | @@ -0,0 +1,173 @@ |
120 | +# encoding: utf-8 |
121 | +import datetime |
122 | +from south.db import db |
123 | +from south.v2 import SchemaMigration |
124 | +from django.db import models |
125 | + |
126 | +class Migration(SchemaMigration): |
127 | + |
128 | + def forwards(self, orm): |
129 | + |
130 | + # Adding field 'Lexbuilder.is_retired' |
131 | + db.add_column('lexbuilders', 'is_retired', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) |
132 | + |
133 | + |
134 | + def backwards(self, orm): |
135 | + |
136 | + # Deleting field 'Lexbuilder.is_retired' |
137 | + db.delete_column('lexbuilders', 'is_retired') |
138 | + |
139 | + |
140 | + models = { |
141 | + 'auth.group': { |
142 | + 'Meta': {'object_name': 'Group'}, |
143 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
144 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
145 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
146 | + }, |
147 | + 'auth.permission': { |
148 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
149 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
150 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
151 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
152 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
153 | + }, |
154 | + 'auth.user': { |
155 | + 'Meta': {'object_name': 'User'}, |
156 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 5, 22, 4, 7, 28, 104849)'}), |
157 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
158 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
159 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
160 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
161 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
162 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
163 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
164 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 5, 22, 4, 7, 28, 104782)'}), |
165 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
166 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
167 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
168 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
169 | + }, |
170 | + 'contenttypes.contenttype': { |
171 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
172 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
173 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
174 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
175 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
176 | + }, |
177 | + 'django_group_access.accessgroup': { |
178 | + 'Meta': {'ordering': "('name',)", 'object_name': 'AccessGroup'}, |
179 | + 'auto_share_groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'auto_share_groups_rel_+'", 'blank': 'True', 'to': "orm['django_group_access.AccessGroup']"}), |
180 | + 'can_be_shared_with': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
181 | + 'can_share_with': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['django_group_access.AccessGroup']", 'symmetrical': 'False', 'blank': 'True'}), |
182 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
183 | + 'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'}), |
184 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), |
185 | + 'supergroup': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) |
186 | + }, |
187 | + 'queuemanager.buildrequest': { |
188 | + 'Meta': {'object_name': 'BuildRequest', 'db_table': "'buildrequests'"}, |
189 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
190 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
191 | + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.Project']", 'db_column': "'project_name'"}), |
192 | + 'reason': ('django.db.models.fields.TextField', [], {'max_length': '200', 'blank': 'True'}), |
193 | + 'requestor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'db_column': "'requestor_id'", 'blank': 'True'}), |
194 | + 'score': ('django.db.models.fields.IntegerField', [], {'default': '10'}) |
195 | + }, |
196 | + 'queuemanager.buildresult': { |
197 | + 'Meta': {'object_name': 'BuildResult', 'db_table': "'buildresults'"}, |
198 | + 'builder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.Lexbuilder']", 'null': 'True', 'blank': 'True'}), |
199 | + 'dispatched_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
200 | + 'finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
201 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
202 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
203 | + 'notes': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
204 | + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.Project']", 'db_column': "'project_name'"}), |
205 | + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
206 | + 'requested_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), |
207 | + 'requestor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'db_column': "'requestor_id'", 'blank': 'True'}), |
208 | + 'result': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), |
209 | + 'started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) |
210 | + }, |
211 | + 'queuemanager.dailybuildorder': { |
212 | + 'Meta': {'object_name': 'DailyBuildOrder', 'db_table': "'dailybuildorders'"}, |
213 | + 'hour': ('django.db.models.fields.PositiveIntegerField', [], {}), |
214 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
215 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), |
216 | + 'projects': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['queuemanager.Project']", 'symmetrical': 'False'}) |
217 | + }, |
218 | + 'queuemanager.launchpadproject': { |
219 | + 'Meta': {'object_name': 'LaunchpadProject', 'db_table': "'launchpad_projects'"}, |
220 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200', 'primary_key': 'True'}), |
221 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) |
222 | + }, |
223 | + 'queuemanager.launchpadprojectmilestone': { |
224 | + 'Meta': {'object_name': 'LaunchpadProjectMilestone', 'db_table': "'launchpad_project_milestones'"}, |
225 | + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
226 | + 'date_targeted': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), |
227 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
228 | + 'launchpad_project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.LaunchpadProject']", 'null': 'True', 'blank': 'True'}), |
229 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), |
230 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}) |
231 | + }, |
232 | + 'queuemanager.lexbuilder': { |
233 | + 'Meta': {'object_name': 'Lexbuilder', 'db_table': "'lexbuilders'"}, |
234 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
235 | + 'current_job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.BuildResult']", 'null': 'True', 'db_column': "'current_job_id'", 'blank': 'True'}), |
236 | + 'current_state': ('django.db.models.fields.CharField', [], {'default': "'UNKNOWN'", 'max_length': '200'}), |
237 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
238 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
239 | + 'is_okay': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
240 | + 'is_retired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
241 | + 'machine_type': ('django.db.models.fields.CharField', [], {'default': "'x86_64'", 'max_length': '200'}), |
242 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '200', 'db_index': 'True'}), |
243 | + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
244 | + 'previous_state': ('django.db.models.fields.CharField', [], {'default': "'UNKNOWN'", 'max_length': '200'}), |
245 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
246 | + 'uri': ('django.db.models.fields.CharField', [], {'max_length': '200'}) |
247 | + }, |
248 | + 'queuemanager.project': { |
249 | + 'Meta': {'object_name': 'Project', 'db_table': "'projects'"}, |
250 | + 'access_groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['django_group_access.AccessGroup']", 'null': 'True', 'blank': 'True'}), |
251 | + 'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}), |
252 | + 'config_url': ('django.db.models.fields.CharField', [], {'max_length': '200'}), |
253 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
254 | + 'launchpad_project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.LaunchpadProject']", 'null': 'True', 'blank': 'True'}), |
255 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '200', 'primary_key': 'True', 'db_index': 'True'}), |
256 | + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
257 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'project_owner'", 'null': 'True', 'to': "orm['auth.User']"}), |
258 | + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '50'}), |
259 | + 'project_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.ProjectGroup']", 'null': 'True', 'blank': 'True'}), |
260 | + 'series': ('django.db.models.fields.CharField', [], {'default': "'lucid'", 'max_length': '200'}), |
261 | + 'status': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), |
262 | + 'suite': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}), |
263 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '30'}) |
264 | + }, |
265 | + 'queuemanager.projectgroup': { |
266 | + 'Meta': {'object_name': 'ProjectGroup', 'db_table': "'projectgroups'"}, |
267 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200', 'primary_key': 'True'}), |
268 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) |
269 | + }, |
270 | + 'queuemanager.projectnotificationsubscription': { |
271 | + 'Meta': {'object_name': 'ProjectNotificationSubscription', 'db_table': "'project_notification_subscriptions'"}, |
272 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
273 | + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.Project']", 'db_column': "'project_name'"}), |
274 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'db_column': "'user_id'"}) |
275 | + }, |
276 | + 'queuemanager.release': { |
277 | + 'Meta': {'object_name': 'Release', 'db_table': "'releases'"}, |
278 | + 'build': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['queuemanager.BuildResult']", 'unique': 'True', 'primary_key': 'True'}), |
279 | + 'checklist_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), |
280 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
281 | + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
282 | + 'milestone': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['queuemanager.LaunchpadProjectMilestone']", 'null': 'True', 'blank': 'True'}), |
283 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), |
284 | + 'notes': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
285 | + 'published_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
286 | + 'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}), |
287 | + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}), |
288 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
289 | + } |
290 | + } |
291 | + |
292 | + complete_apps = ['queuemanager'] |
293 | |
294 | === modified file 'lib/offspring/web/queuemanager/models.py' |
295 | --- lib/offspring/web/queuemanager/models.py 2013-05-17 13:49:22 +0000 |
296 | +++ lib/offspring/web/queuemanager/models.py 2013-05-23 13:57:23 +0000 |
297 | @@ -164,6 +164,7 @@ |
298 | updated_at = models.DateTimeField('date updated', auto_now=True) |
299 | is_active = models.BooleanField(default=True) |
300 | is_okay = models.BooleanField(default=True, editable=False) |
301 | + is_retired = models.BooleanField(default=False) |
302 | machine_type = models.CharField(max_length=200, default="x86_64") |
303 | current_job = models.ForeignKey("BuildResult", db_column="current_job_id", editable=False, blank=True, null=True) |
304 | notes = models.TextField('whiteboard', blank=True, null=True) |
305 | |
306 | === modified file 'lib/offspring/web/queuemanager/tests/test_views.py' |
307 | --- lib/offspring/web/queuemanager/tests/test_views.py 2013-01-31 18:36:58 +0000 |
308 | +++ lib/offspring/web/queuemanager/tests/test_views.py 2013-05-23 13:57:23 +0000 |
309 | @@ -275,6 +275,44 @@ |
310 | msg_prefix=response.content |
311 | ) |
312 | |
313 | +class BuildersListViewTests(TestCase): |
314 | + def setUp(self): |
315 | + user = factory.make_user() |
316 | + self.builder1 = factory.make_lexbuilder( |
317 | + name="BuilderA", is_retired=True) |
318 | + self.builder2 = factory.make_lexbuilder(name="BuilderB") |
319 | + self.client.login( |
320 | + username=user.username, password=user.username) |
321 | + |
322 | + def test_builderslist_does_not_show_retired_builders(self): |
323 | + """ |
324 | + Test builders list does not show retired builders. |
325 | + """ |
326 | + response = self.client.get(reverse("builders_list")) |
327 | + self.assertNotContains( |
328 | + response, self.builder1.name, status_code=200, |
329 | + msg_prefix=response.content) |
330 | + self.assertContains( |
331 | + response, self.builder2.name, status_code=200, |
332 | + msg_prefix=response.content) |
333 | + |
334 | +class BuilderDetailsViewTests(TestCase): |
335 | + def setUp(self): |
336 | + user = factory.make_user() |
337 | + self.builder1 = factory.make_lexbuilder( |
338 | + name="BuilderA", is_retired=True) |
339 | + self.builder2 = factory.make_lexbuilder(name="BuilderB") |
340 | + self.client.login( |
341 | + username=user.username, password=user.username) |
342 | + |
343 | + def test_builderdetails_404s_for_retired_builders(self): |
344 | + """ |
345 | + Test builder details are not available for retired builders. |
346 | + """ |
347 | + response = self.client.get(reverse("builder_details", kwargs={'slug': self.builder1.name})) |
348 | + self.assertEqual(response.status_code, 404) |
349 | + response = self.client.get(reverse("builder_details", kwargs={'slug': self.builder2.name})) |
350 | + self.assertEqual(response.status_code, 200) |
351 | |
352 | class BuildResultPublishViewTests(TestCase): |
353 | |
354 | |
355 | === modified file 'lib/offspring/web/urls.py' |
356 | --- lib/offspring/web/urls.py 2013-01-22 10:00:39 +0000 |
357 | +++ lib/offspring/web/urls.py 2013-05-23 13:57:23 +0000 |
358 | @@ -58,13 +58,14 @@ |
359 | (r'^openid/', include('django_openid_auth.urls')), |
360 | (r'^logout/', 'django.contrib.auth.views.logout'), |
361 | (r'^builders/$', secure_object_list, { |
362 | - 'queryset' : Lexbuilder.objects.order_by("-is_active", "-machine_type", "-created_at"), |
363 | + 'queryset' : Lexbuilder.objects.filter(is_retired=False).order_by( |
364 | + "-is_active", "-machine_type", "-created_at"), |
365 | 'template_name' : 'queuemanager/builders.html', |
366 | 'template_object_name' : 'builder', |
367 | 'extra_context' : { 'pillar' : 'builders', }, |
368 | }, 'builders_list'), |
369 | (r'^builders/(?P<slug>[^/]+)/$', secure_object_detail, { |
370 | - 'queryset' : Lexbuilder.objects.all(), |
371 | + 'queryset' : Lexbuilder.objects.filter(is_retired=False), |
372 | 'slug_field' : 'name', |
373 | 'template_name' : 'queuemanager/builder_details.html', |
374 | 'template_object_name' : 'builder', |
Looks good to me, +1