Merge lp:~cprov/uci-engine/ticket-conflicts into lp:uci-engine

Proposed by Celso Providelo
Status: Merged
Approved by: Celso Providelo
Approved revision: 799
Merged at revision: 799
Proposed branch: lp:~cprov/uci-engine/ticket-conflicts
Merge into: lp:uci-engine
Diff against target: 373 lines (+206/-30)
6 files modified
ticket_system/ticket/api.py (+32/-27)
ticket_system/ticket/models.py (+29/-0)
ticket_system/ticket/tests/test_full_read_api.py (+2/-0)
ticket_system/ticket/tests/test_models.py (+107/-2)
ticket_system/ticket/tests/test_read_api.py (+28/-0)
webui/tickets/static/tickets/webui.js (+8/-1)
To merge this branch: bzr merge lp:~cprov/uci-engine/ticket-conflicts
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Francis Ginther Approve
Review via email: mp+235301@code.launchpad.net

Commit message

Implement and expose Subticket.conflicts.

Description of the change

Implement and expose Subticket.conflicts.

Each subticket may point to currently active tickets including SPUs/MPs for the same source package.

The conflicting tickets are referred by subtickets in order to give the user a better understanding of which *part* (subticket) of his task (ticket) is likely to result in conflicts when landing.

Full conflicting context will be provided during the landing (publishing) step.

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

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

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

review: Approve (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

Just one question on excluding NEW tickets. Otherwise this looks good and tests look reasonable.

review: Needs Information
Revision history for this message
Celso Providelo (cprov) wrote :

On Fri, Sep 19, 2014 at 5:49 PM, Francis Ginther
<email address hidden> wrote:
> Review: Needs Information
>
> Just one question on excluding NEW tickets. Otherwise this looks good and tests look reasonable.

Thank you.

> What's the reason for not considering 'NEW' tickets as conflicts? Not saying it's wrong, I'm just not seeing the scenario around this.

NEW tickets are not considered by the Lander and I believe it was
implemented this way to cope with the multi-step ticket creation
process we currently have. Recently I've also extended the CLI to
optionally not queue the ticket after creation (create_ticket ... -w).
The intended scenario here is to allow users to register WIP tickets
in a same way we register WIP MPs (to inform/share in-progress tasks).

In the conflicting scenario, NEW (WIP) tickets will list existing
conflict but won't be considered a conflict for other active tickets.
The same behaviour might apply to other static relation/analysis we
can perform on the TS environment.

Does it make more sense ?

--
Celso Providelo
<email address hidden>

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

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

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

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

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

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

review: Approve (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

Approve.

review: Approve
Revision history for this message
Francis Ginther (fginther) wrote :

> On Fri, Sep 19, 2014 at 5:49 PM, Francis Ginther
> <email address hidden> wrote:
> > Review: Needs Information
> >
> > Just one question on excluding NEW tickets. Otherwise this looks good and
> tests look reasonable.
>
> Thank you.
>
> > What's the reason for not considering 'NEW' tickets as conflicts? Not saying
> it's wrong, I'm just not seeing the scenario around this.
>
> NEW tickets are not considered by the Lander and I believe it was
> implemented this way to cope with the multi-step ticket creation
> process we currently have. Recently I've also extended the CLI to
> optionally not queue the ticket after creation (create_ticket ... -w).
> The intended scenario here is to allow users to register WIP tickets
> in a same way we register WIP MPs (to inform/share in-progress tasks).
>
> In the conflicting scenario, NEW (WIP) tickets will list existing
> conflict but won't be considered a conflict for other active tickets.
> The same behaviour might apply to other static relation/analysis we
> can perform on the TS environment.
>
> Does it make more sense ?

Yes, this approach makes sense.

Revision history for this message
Ubuntu CI Bot (uci-bot) wrote :
Download full text (118.2 KiB)

The attempt to merge lp:~cprov/uci-engine/ticket-conflicts into lp:uci-engine failed. Below is the output from the failed tests.

Running cm...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
uploading webui-content.tgz to swift
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Checking juju status
Private PPAs: disabled
Preparing local branch upload...
Uploading local branch, fingerprint b2ef646eb9e6fe5f2ac158ce76894a39e74a9fb3
Building charm: lander
Building charm: wsgi-app
Building charm: rabbitmq-worker
Building charm: webui
Building charm: key-secret-subordinate
Building charm: system-image-server
Building charm: chroot-builder
Installing keys from bzr+ssh://bazaar.launchpad.net/~ci-engineering-private/+junk/ci-airline-dev-keys/
Running juju-deployer -v -c /tmp/tmpAqmn35/deployer/branch-source-builder.yaml -c /tmp/tmpAqmn35/deployer/britney-proxy.yaml -c /tmp/tmpAqmn35/deployer/gatekeeper.yaml -c /tmp/tmpAqmn35/deployer/image-builder.yaml -c /tmp/tmpAqmn35/deployer/lander.yaml -c /tmp/tmpAqmn35/deployer/nf-stats-service.yaml -c /tmp/tmpAqmn35/deployer/ppa-creator.yaml -c /tmp/tmpAqmn35/deployer/publisher.yaml -c /tmp/tmpAqmn35/deployer/relations.yaml -c /tmp/tmpAqmn35/deployer/test-runner.yaml -c /tmp/tmpAqmn35/deployer/ticket-system.yaml -c /tmp/tmpAqmn35/deployer/validator.yaml -c /tmp/tmpAqmn35/deployer/webui.yaml ci-airline
Tests running...
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testConnectFailed ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testProcessQueue ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testRunForever ... OK (0.101 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testSent ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp.TestProgressTrigger.testProgress ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testCancel ... OK (0.107 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testNoQueue ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testNoTicket ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageCalledProcessError ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageFail ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageKilled ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageSimple ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageUnexpected ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testSaveLastRun ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestTimer.testCBRuns ... OK (0.020 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestTimer.testCanCancel ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreConfig.test_invalid_auth_config ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreConfig.test_valid_auth_config ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreFileName.test_get_file_nam...

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

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

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

review: Approve (continuous-integration)
Revision history for this message
Ubuntu CI Bot (uci-bot) wrote :
Download full text (62.2 KiB)

The attempt to merge lp:~cprov/uci-engine/ticket-conflicts into lp:uci-engine failed. Below is the output from the failed tests.

Running cm...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
uploading webui-content.tgz to swift
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
2014-09-24 13:11:07 INFO juju.cmd supercommand.go:37 running jujud [1.20.7.1-precise-amd64 gc]
2014-09-24 13:11:07 DEBUG juju.agent agent.go:377 read agent config, format "1.18"
2014-09-24 13:11:07 INFO juju.jujud unit.go:78 unit agent unit-ci-airline-ts-django-0 start (1.20.7.1-precise-amd64 [gc])
2014-09-24 13:11:07 INFO juju.worker runner.go:260 start "api"
2014-09-24 13:11:07 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-09-24 13:11:07 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-09-24 13:11:07 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-09-24 13:11:07 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-09-24 13:11:16 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-09-24 13:11:16 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-09-24 13:11:16 INFO juju.worker runner.go:260 start "upgrader"
2014-09-24 13:11:16 INFO juju.worker runner.go:260 start "logger"
2014-09-24 13:11:16 DEBUG juju.worker.logger logger.go:35 initial log config: "<root>=DEBUG"
2014-09-24 13:11:16 INFO juju.worker runner.go:260 start "uniter"
2014-09-24 13:11:16 DEBUG juju.worker.logger logger.go:60 logger setup
2014-09-24 13:11:16 INFO juju.worker runner.go:260 start "apiaddressupdater"
2014-09-24 13:11:16 INFO juju.worker runner.go:260 start "rsyslog"
2014-09-24 13:11:16 DEBUG juju.worker.rsyslog worker.go:75 starting rsyslog worker mode 1 for "unit-ci-airline-ts-django-0" "tarmac-local"
2014-09-24 13:11:16 DEBUG juju.worker.logger logger.go:45 reconfiguring logging from "<root>=DEBUG" to "<root>=WARNING;unit=DEBUG"
2014-09-24 13:11:34 INFO juju-log Making dir /srv/ci-airline-ts-django/nagios www-data:www-data 755
2014-09-24 13:11:34 INFO juju-log installing apt packages...
2014-09-24 13:11:38 INFO install gpg: keyring `/tmp/tmpycVR_E/secring.gpg' created
2014-09-24 13:11:38 INFO install gpg: keyring `/tmp/tmpycVR_E/pubring.gpg' created
2014-09-24 13:11:38 INFO install gpg: requesting key 6A8DFC40 from hkp server keyserver.ubuntu.com
2014-09-24 13:11:38 INFO install gpg: /tmp/tmpycVR_E/trustdb.gpg: trustdb created
2014-09-24 13:11:38 INFO install gpg: key 6A8DFC40: public key "Launchpad PPA for Canonical CI Engineering" imported
2014-09-24 13:11:38 INFO install gpg: Total number processed: 1
2014-09-24 13:11:38 INFO install gpg: imported: 1 (RSA: 1)
2014-09-24 13:11:39 INFO install OK
2014-09-24 13:11:42 INFO install Get:1 http://ubuntu-cloud.archive.canonical.com precise-updates/cloud-tools Release.gpg [543 B]
2014-09-24 13:11:42 INFO install Hit http://archive.ubuntu.com precise Release.gpg
2014-09-24 13:11:42 INFO...

Revision history for this message
Ubuntu CI Bot (uci-bot) wrote :
Download full text (62.3 KiB)

The attempt to merge lp:~cprov/uci-engine/ticket-conflicts into lp:uci-engine failed. Below is the output from the failed tests.

Running cm...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
uploading webui-content.tgz to swift
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
2014-09-24 13:40:53 INFO juju.cmd supercommand.go:37 running jujud [1.20.7.1-precise-amd64 gc]
2014-09-24 13:40:53 DEBUG juju.agent agent.go:377 read agent config, format "1.18"
2014-09-24 13:40:53 INFO juju.jujud unit.go:78 unit agent unit-ci-airline-ts-django-0 start (1.20.7.1-precise-amd64 [gc])
2014-09-24 13:40:53 INFO juju.worker runner.go:260 start "api"
2014-09-24 13:40:53 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-09-24 13:40:53 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-09-24 13:40:53 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-09-24 13:40:53 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-09-24 13:40:54 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-09-24 13:40:54 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-09-24 13:40:54 INFO juju.worker runner.go:260 start "upgrader"
2014-09-24 13:40:54 INFO juju.worker runner.go:260 start "logger"
2014-09-24 13:40:54 DEBUG juju.worker.logger logger.go:35 initial log config: "<root>=DEBUG"
2014-09-24 13:40:54 INFO juju.worker runner.go:260 start "uniter"
2014-09-24 13:40:54 DEBUG juju.worker.logger logger.go:60 logger setup
2014-09-24 13:40:54 INFO juju.worker runner.go:260 start "apiaddressupdater"
2014-09-24 13:40:54 INFO juju.worker runner.go:260 start "rsyslog"
2014-09-24 13:40:54 DEBUG juju.worker.rsyslog worker.go:75 starting rsyslog worker mode 1 for "unit-ci-airline-ts-django-0" "tarmac-local"
2014-09-24 13:40:54 DEBUG juju.worker.logger logger.go:45 reconfiguring logging from "<root>=DEBUG" to "<root>=WARNING;unit=DEBUG"
2014-09-24 13:41:03 INFO juju-log Making dir /srv/ci-airline-ts-django/nagios www-data:www-data 755
2014-09-24 13:41:03 INFO juju-log installing apt packages...
2014-09-24 13:41:08 INFO install gpg: keyring `/tmp/tmpfS7NKC/secring.gpg' created
2014-09-24 13:41:08 INFO install gpg: keyring `/tmp/tmpfS7NKC/pubring.gpg' created
2014-09-24 13:41:08 INFO install gpg: requesting key 6A8DFC40 from hkp server keyserver.ubuntu.com
2014-09-24 13:41:08 INFO install gpg: /tmp/tmpfS7NKC/trustdb.gpg: trustdb created
2014-09-24 13:41:08 INFO install gpg: key 6A8DFC40: public key "Launchpad PPA for Canonical CI Engineering" imported
2014-09-24 13:41:08 INFO install gpg: Total number processed: 1
2014-09-24 13:41:08 INFO install gpg: imported: 1 (RSA: 1)
2014-09-24 13:41:08 INFO install OK
2014-09-24 13:41:09 INFO install Hit http://archive.ubuntu.com precise Release.gpg
2014-09-24 13:41:09 INFO install Get:1 http://ppa.launchpad.net precise Release.gpg [316 B]
2014-09-24 13:41:09 INFO install Get:2 http://ubuntu-cloud.ar...

799. By Celso Providelo

merge trunk

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

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

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ticket_system/ticket/api.py'
2--- ticket_system/ticket/api.py 2014-08-29 18:28:56 +0000
3+++ ticket_system/ticket/api.py 2014-09-24 17:47:41 +0000
4@@ -154,22 +154,23 @@
5 return ''
6
7
8-class SubTicketTranslatedResource(ModelResource):
9-
10- def dehydrate_current_workflow_step(self, bundle):
11- return get_enum_title(bundle.data["current_workflow_step"],
12- SubTicketWorkflowStep)
13-
14- def dehydrate_status(self, bundle):
15- return get_enum_title(bundle.data["status"],
16- SubTicketWorkflowStepStatus)
17+class SourcePackageUploadResource(ModelResource):
18+
19+ sourcepackage = fields.ToOneField(
20+ SourcePackageResource, 'sourcepackage', full=True)
21+
22+ class Meta:
23+ queryset = SourcePackageUpload.objects.all()
24+ allowed_methods = ['get', 'post']
25+ authorization = Authorization()
26+ resource_name = 'spu'
27
28
29 class TicketResource(TicketTranslatedResource):
30
31 class Meta:
32 # XXX cprov 20140829: 'private' should only be considered via
33- # authorization mechnisms, not on the local resource queryset.
34+ # authorization mechanisms, not on the local resource queryset.
35 queryset = Ticket.objects.filter(private=False)
36 allowed_methods = ['get', 'post', 'patch']
37 authorization = Authorization()
38@@ -179,22 +180,26 @@
39 paginator_class = PageNumberPaginator
40
41
42-class SourcePackageUploadResource(ModelResource):
43- sourcepackage = fields.ToOneField(SourcePackageResource, 'sourcepackage',
44- full=True)
45-
46- class Meta:
47- queryset = SourcePackageUpload.objects.all()
48- allowed_methods = ['get', 'post']
49- authorization = Authorization()
50- resource_name = 'spu'
51+class SubTicketTranslatedResource(ModelResource):
52+
53+ def dehydrate_current_workflow_step(self, bundle):
54+ return get_enum_title(bundle.data["current_workflow_step"],
55+ SubTicketWorkflowStep)
56+
57+ def dehydrate_status(self, bundle):
58+ return get_enum_title(bundle.data["status"],
59+ SubTicketWorkflowStepStatus)
60+
61+ source_package_upload = fields.ToOneField(
62+ SourcePackageUploadResource, 'source_package_upload', full=True)
63+
64+ conflicts = fields.ToManyField(
65+ TicketResource, 'conflicts', readonly=True, null=True, full=False)
66
67
68 class SubTicketResource(SubTicketTranslatedResource):
69+
70 ticket = fields.ToOneField(TicketResource, 'ticket', full=True)
71- source_package_upload = fields.ToOneField(SourcePackageUploadResource,
72- 'source_package_upload',
73- full=True)
74
75 class Meta:
76 queryset = SubTicket.objects.all()
77@@ -216,8 +221,9 @@
78
79
80 class SubTicketArtifactResource(ModelResource):
81- subticket = fields.ToOneField(SubTicketResource, 'subticket', null=True,
82- full=True)
83+
84+ subticket = fields.ToOneField(SubTicketResource, 'subticket',
85+ readonly=True, null=True, full=True)
86
87 class Meta:
88 queryset = SubTicketArtifact.objects.all()
89@@ -240,9 +246,7 @@
90
91
92 class FullSubTicketResource(SubTicketTranslatedResource):
93- source_package_upload = fields.ToOneField(SourcePackageUploadResource,
94- 'source_package_upload',
95- full=True)
96+
97 artifact = fields.ToManyField(FullSubTicketArtifactResource,
98 'subticketartifact_set', full=True)
99
100@@ -252,6 +256,7 @@
101
102
103 class FullTicketResource(TicketTranslatedResource):
104+
105 subticket = fields.ToManyField(FullSubTicketResource, 'subticket_set',
106 full=True)
107 artifact = fields.ToManyField(FullTicketArtifactResource,
108
109=== modified file 'ticket_system/ticket/models.py'
110--- ticket_system/ticket/models.py 2014-09-09 17:25:59 +0000
111+++ ticket_system/ticket/models.py 2014-09-24 17:47:41 +0000
112@@ -232,6 +232,7 @@
113 class SubTicket(models.Model):
114 class Meta:
115 db_table = 'subticket'
116+ ordering = ['id']
117
118 current_workflow_step = models.IntegerField(
119 choices=_choices(SubTicketWorkflowStep), max_length=4096,
120@@ -246,6 +247,34 @@
121 merge_proposal = models.ForeignKey(MergeProposal, null=True, blank=True,
122 default=None)
123
124+ @property
125+ def conflicts(self):
126+ """Return `Ticket`s that conflict with this subticket task.
127+
128+ A ticket is considered a conflict if it contains a `SubTicket`
129+ with a `SourcePackageUpload` or a `MergeProposal` for the same
130+ 'name' of this `SubTicket`. I.e. conflicts may happen between
131+ a MP and a SPU.
132+
133+ Only *active* (not NEW|FAILED|COMPLETED) tickets are considered
134+ for conflicts.
135+ """
136+ if self.merge_proposal is not None:
137+ sp = self.merge_proposal.sourcepackage
138+ else:
139+ sp = self.source_package_upload.sourcepackage
140+
141+ conflicting_tickets = Ticket.objects.filter(
142+ models.Q(subticket__merge_proposal__sourcepackage=sp)
143+ | models.Q(subticket__source_package_upload__sourcepackage=sp),
144+ ~models.Q(pk=self.ticket.pk),
145+ ~models.Q(status=TicketWorkflowStepStatus.FAILED),
146+ current_workflow_step__range=(
147+ TicketWorkflowStep.QUEUED, TicketWorkflowStep.PKG_PUBLISHING),
148+ )
149+
150+ return conflicting_tickets
151+
152 def save(self, *args, **kwargs):
153 if self.source_package_upload and self.merge_proposal:
154 raise IntegrityError("Subtickets only accept a source package "
155
156=== modified file 'ticket_system/ticket/tests/test_full_read_api.py'
157--- ticket_system/ticket/tests/test_full_read_api.py 2014-09-08 13:32:31 +0000
158+++ ticket_system/ticket/tests/test_full_read_api.py 2014-09-24 17:47:41 +0000
159@@ -98,6 +98,7 @@
160 u'resource_uri':
161 u'/api/v1/spu/{0}/'.format(self.spu.pk)},
162 u'id': self.subticket.pk,
163+ u'conflicts': [],
164 u'resource_uri': u'/api/v1/fullsubticket/{0}/'.format(
165 self.subticket.pk)}],
166 u'bug_id': self.ticket.bug_id,
167@@ -168,6 +169,7 @@
168 u'resource_uri':
169 u'/api/v1/spu/{0}/'.format(self.spu.pk)},
170 u'id': self.subticket.pk,
171+ u'conflicts': [],
172 u'resource_uri': u'/api/v1/fullsubticket/{0}/'.format(
173 self.subticket.pk),
174 })
175
176=== modified file 'ticket_system/ticket/tests/test_models.py'
177--- ticket_system/ticket/tests/test_models.py 2014-08-27 11:59:06 +0000
178+++ ticket_system/ticket/tests/test_models.py 2014-09-24 17:47:41 +0000
179@@ -44,7 +44,7 @@
180 return sourcepackage
181
182
183-def create_sourcepackageupload(sourcepackage='my-package', version='1.1'):
184+def create_sourcepackageupload(sourcepackage, version='1.1'):
185 spu = SourcePackageUpload()
186 spu.sourcepackage = sourcepackage
187 spu.version = version
188@@ -52,7 +52,7 @@
189 return spu
190
191
192-def create_merge_proposal(project='my-project', sourcepackage='my-package',
193+def create_merge_proposal(sourcepackage, project='my-project',
194 lp_url='~foobar/my-project/my-branch/+merge/123456',
195 target_branch='lp:~foobar/my-project/trunk',
196 approved_revno=100, version='1.1'):
197@@ -350,3 +350,108 @@
198 '''make sure we don't allow invalid json'''
199 with self.assertRaises(ValidationError):
200 Workflow.objects.create(name='foo', steps='this is not json')
201+
202+
203+class TestTicketConflicts(TestCase):
204+
205+ def setUp(self):
206+ """Mock container creation and queue dispatching.
207+
208+ Also setup a common ticket conflict eco-system.
209+ """
210+ _m = mock.patch('ticket.models.Ticket.create_container')
211+ _m.start()
212+ self.addCleanup(_m.stop)
213+ _m = mock.patch('ci_utils.amqp_utils.send')
214+ _m.start()
215+ self.addCleanup(_m.stop)
216+
217+ self.ticket_one = create_ticket(title='One')
218+ self.ticket_two = create_ticket(title='Two')
219+ sp_one = create_sourcepackage('ding')
220+ sp_two = create_sourcepackage('dong')
221+ self.spu_one = create_sourcepackageupload(sp_one)
222+ self.spu_two = create_sourcepackageupload(sp_two)
223+ self.mp_one = create_merge_proposal(sp_one)
224+ self.mp_two = create_merge_proposal(sp_two)
225+
226+ def test_no_conflicts(self):
227+ # Conflicts are only detected for `SourcePackageUpload`s with
228+ # the same 'name' and 'version'.
229+ subticket_one = create_subticket(
230+ ticket=self.ticket_one, source_package_upload=self.spu_one)
231+ subticket_two = create_subticket(
232+ ticket=self.ticket_two, source_package_upload=self.spu_two)
233+
234+ self.assertEqual(
235+ [], list(subticket_one.conflicts))
236+ self.assertEqual(
237+ [], list(subticket_two.conflicts))
238+
239+ def test_has_single_conflict(self):
240+ # Conflicts are symmetrical across 'SubTicket's on active `Ticket`s
241+ # (A conflicts with B, thus B conflicts with A) and cover
242+ # `SourcePackageUpload` and `MergeProposal`s.
243+ subticket_one = create_subticket(
244+ ticket=self.ticket_one, source_package_upload=self.spu_one)
245+ subticket_two = create_subticket(
246+ ticket=self.ticket_two, merge_proposal=self.mp_one)
247+
248+ self.assertEqual(
249+ [self.ticket_two], list(subticket_one.conflicts))
250+ self.assertEqual(
251+ [self.ticket_one], list(subticket_two.conflicts))
252+
253+ def test_no_conflicts_for_inactive(self):
254+ # Inactive `Ticket`s (NEW, FAILED or COMPLETED) are not considered
255+ # conflict targets, but still pointing to active conflicts.
256+ subticket_one = create_subticket(
257+ ticket=self.ticket_one, source_package_upload=self.spu_one)
258+ subticket_two = create_subticket(
259+ ticket=self.ticket_two, source_package_upload=self.spu_one)
260+
261+ self.ticket_two.current_workflow_step = TicketWorkflowStep.COMPLETED
262+ self.ticket_two.save()
263+
264+ self.assertEqual([], list(subticket_one.conflicts))
265+ self.assertEqual([self.ticket_one], list(subticket_two.conflicts))
266+
267+ def test_no_conflicts_for_failures(self):
268+ # `Ticket`s that failed in any workflow steps are not considered
269+ # conflict targets.
270+ subticket_one = create_subticket(
271+ ticket=self.ticket_one, source_package_upload=self.spu_one)
272+ subticket_two = create_subticket(
273+ ticket=self.ticket_two, source_package_upload=self.spu_one)
274+
275+ self.ticket_two.current_workflow_step = (
276+ TicketWorkflowStep.PKG_BUILDING)
277+ self.ticket_two.status = TicketWorkflowStepStatus.FAILED
278+ self.ticket_two.save()
279+
280+ self.assertEqual([], list(subticket_one.conflicts))
281+ self.assertEqual([self.ticket_one], list(subticket_two.conflicts))
282+
283+ def test_has_multiple_conflicts(self):
284+ # Multiple conflicting tickets are distinct and ordered by id.
285+ subticket_one = create_subticket(
286+ ticket=self.ticket_one, source_package_upload=self.spu_one)
287+ subticket_two = create_subticket(
288+ ticket=self.ticket_two, source_package_upload=self.spu_one)
289+ # Create a third conflicting ticket on a MP
290+ ticket_three = create_ticket(title='Three')
291+ subticket_three = create_subticket(
292+ ticket=ticket_three, merge_proposal=self.mp_one)
293+ # Force a multi-path join to provoke duplicated results.
294+ create_subticket(
295+ ticket=self.ticket_one, source_package_upload=self.spu_two)
296+ create_subticket(
297+ ticket=self.ticket_two, merge_proposal=self.mp_two)
298+
299+ self.assertEqual(
300+ [self.ticket_two, ticket_three], list(subticket_one.conflicts))
301+ self.assertEqual(
302+ [self.ticket_one, ticket_three], list(subticket_two.conflicts))
303+ self.assertEqual(
304+ [self.ticket_one, self.ticket_two],
305+ list(subticket_three.conflicts))
306
307=== modified file 'ticket_system/ticket/tests/test_read_api.py'
308--- ticket_system/ticket/tests/test_read_api.py 2014-09-10 11:41:44 +0000
309+++ ticket_system/ticket/tests/test_read_api.py 2014-09-24 17:47:41 +0000
310@@ -153,6 +153,7 @@
311 settings.TEST_DATETIME_FORMAT)),
312 u'uuid': self.ticket.uuid},
313 u'id': self.subticket.pk,
314+ u'conflicts': [],
315 u'resource_uri': u'/api/v1/subticket/{0}/'.format(
316 self.subticket.pk)},
317 u'type': unicode(self.artifact_1.type),
318@@ -245,8 +246,35 @@
319 u'updated': unicode(self.ticket.updated.strftime(
320 settings.TEST_DATETIME_FORMAT)),
321 u'uuid': self.ticket.uuid},
322+ u'conflicts': [],
323 })
324
325+ def test_subticket_api_conflicts(self):
326+ # Each `Subticket`s points, individually, to conflicting `Tickets`
327+ # This way users have a clear indication of which part (subticket)
328+ # of their task might result in problem when landing.
329+ another_ticket = mommy.make(
330+ 'Ticket', current_workflow_step=TicketWorkflowStep.QUEUED)
331+ another_spu = mommy.make(
332+ 'SourcePackageUpload', sourcepackage=self.sourcepackage,
333+ version=self.spu.version)
334+ mommy.make(
335+ 'SubTicket', ticket=another_ticket,
336+ source_package_upload=another_spu)
337+
338+ [old, new] = self.getResource('subticket/')['objects']
339+
340+ # The 'old' ticket now conflicts with 'new' (just created).
341+ # Remember, 'conflicts' contains the `Ticket` resource URI.
342+ self.assertEqual(
343+ [new['ticket']['resource_uri']], old['conflicts'])
344+
345+ # The 'new' ticket does not conflict with the 'old' because it's
346+ # still 'NEW' (not active). See 'test_models' for more descriptive
347+ # test on this aspect.
348+ self.assertEqual(
349+ [], new['conflicts'])
350+
351 def test_get_ticket_api(self):
352 obj = self.getResource('ticket/')
353 self.assertEqual(obj['objects'][0], {
354
355=== modified file 'webui/tickets/static/tickets/webui.js'
356--- webui/tickets/static/tickets/webui.js 2014-08-29 18:54:41 +0000
357+++ webui/tickets/static/tickets/webui.js 2014-09-24 17:47:41 +0000
358@@ -185,7 +185,14 @@
359 make_subticket: function (data) {
360 var div = Y.Node.create("<div class='subticket'></div>"),
361 table = div.appendChild(Y.Node.create("<table>")),
362- hidden_fields = Y.Array(['id', 'resource_uri', 'current_workflow_step', 'status', 'source_package_upload']),
363+ hidden_fields = Y.Array([
364+ 'id',
365+ 'resource_uri',
366+ 'current_workflow_step',
367+ 'status',
368+ 'source_package_upload',
369+ 'conflicts',
370+ ]),
371 key,
372 value,
373 tmp_th,

Subscribers

People subscribed via source and target branches