Skip to content

Commit 3a4338e

Browse files
lsergiosquakez
authored andcommitted
bugfix(#5755): avoiding deadlock between builds with dependencies build order strategy when the builds share enough dependencies
1 parent 648b4cc commit 3a4338e

File tree

2 files changed

+341
-6
lines changed

2 files changed

+341
-6
lines changed

pkg/apis/camel/v1/build_type_support_test.go

+301
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package v1
1919

2020
import (
2121
"testing"
22+
"time"
2223

2324
"github.com/stretchr/testify/assert"
2425
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -37,6 +38,9 @@ func TestMatchingBuildsPending(t *testing.T) {
3738
"camel:timer",
3839
"camel:log",
3940
},
41+
Runtime: RuntimeSpec{
42+
Version: "3.8.1",
43+
},
4044
},
4145
},
4246
},
@@ -58,6 +62,9 @@ func TestMatchingBuildsPending(t *testing.T) {
5862
"camel:log",
5963
"camel:bean",
6064
},
65+
Runtime: RuntimeSpec{
66+
Version: "3.8.1",
67+
},
6168
},
6269
},
6370
},
@@ -80,6 +87,9 @@ func TestMatchingBuildsPending(t *testing.T) {
8087
"camel:bean",
8188
"camel:zipfile",
8289
},
90+
Runtime: RuntimeSpec{
91+
Version: "3.8.1",
92+
},
8393
},
8494
},
8595
},
@@ -101,6 +111,9 @@ func TestMatchingBuildsPending(t *testing.T) {
101111
"camel:component-a",
102112
"camel:component-b",
103113
},
114+
Runtime: RuntimeSpec{
115+
Version: "3.8.1",
116+
},
104117
},
105118
},
106119
},
@@ -126,3 +139,291 @@ func TestMatchingBuildsPending(t *testing.T) {
126139
assert.False(t, matches)
127140
assert.Nil(t, buildMatch)
128141
}
142+
143+
func TestMatchingBuildsSchedulingSharedDependencies(t *testing.T) {
144+
timestamp, _ := time.Parse("2006-01-02T15:04:05-0700", "2024-08-09T10:00:00Z")
145+
creationTimestamp := v1.Time{Time: timestamp}
146+
buildA := Build{
147+
ObjectMeta: v1.ObjectMeta{
148+
Name: "buildA",
149+
},
150+
Spec: BuildSpec{
151+
Tasks: []Task{
152+
{
153+
Builder: &BuilderTask{
154+
Dependencies: []string{
155+
"camel:core",
156+
"camel:rest",
157+
"mvn:org.apache.camel.k:camel-k-runtime",
158+
"mvn:org.apache.camel.quarkus:camel-quarkus-yaml-dsl",
159+
},
160+
Runtime: RuntimeSpec{
161+
Version: "3.8.1",
162+
},
163+
},
164+
},
165+
},
166+
},
167+
Status: BuildStatus{
168+
Phase: BuildPhaseScheduling,
169+
},
170+
}
171+
buildB := Build{
172+
ObjectMeta: v1.ObjectMeta{
173+
Name: "buildB",
174+
CreationTimestamp: creationTimestamp,
175+
},
176+
Spec: BuildSpec{
177+
Tasks: []Task{
178+
{
179+
Builder: &BuilderTask{
180+
Dependencies: []string{
181+
"camel:quartz",
182+
"mvn:org.apache.camel.k:camel-k-cron",
183+
"mvn:org.apache.camel.k:camel-k-runtime",
184+
"mvn:org.apache.camel.quarkus:camel-quarkus-yaml-dsl",
185+
},
186+
Runtime: RuntimeSpec{
187+
Version: "3.8.1",
188+
}},
189+
},
190+
},
191+
},
192+
Status: BuildStatus{
193+
Phase: BuildPhaseScheduling,
194+
},
195+
}
196+
197+
buildList := BuildList{
198+
Items: []Build{buildA, buildB},
199+
}
200+
201+
// both builds share dependencies and have the same creationTimestamp
202+
// buildA should be prioritized so there should be not matching build for it
203+
204+
matches, buildMatch := buildList.HasMatchingBuild(&buildA)
205+
assert.False(t, matches)
206+
assert.Nil(t, buildMatch)
207+
matches, buildMatch = buildList.HasMatchingBuild(&buildB)
208+
assert.True(t, matches)
209+
assert.True(t, buildMatch.Name == buildA.Name)
210+
}
211+
212+
func TestMatchingBuildsSchedulingSameDependenciesDIfferentRuntimes(t *testing.T) {
213+
timestamp, _ := time.Parse("2006-01-02T15:04:05-0700", "2024-08-09T10:00:00Z")
214+
creationTimestamp := v1.Time{Time: timestamp}
215+
buildA := Build{
216+
ObjectMeta: v1.ObjectMeta{
217+
Name: "buildA",
218+
},
219+
Spec: BuildSpec{
220+
Tasks: []Task{
221+
{
222+
Builder: &BuilderTask{
223+
Dependencies: []string{
224+
"camel:quartz",
225+
"mvn:org.apache.camel.k:camel-k-cron",
226+
"mvn:org.apache.camel.k:camel-k-runtime",
227+
"mvn:org.apache.camel.quarkus:camel-quarkus-yaml-dsl",
228+
},
229+
Runtime: RuntimeSpec{
230+
Version: "3.8.1",
231+
},
232+
},
233+
},
234+
},
235+
},
236+
Status: BuildStatus{
237+
Phase: BuildPhaseScheduling,
238+
},
239+
}
240+
buildB := Build{
241+
ObjectMeta: v1.ObjectMeta{
242+
Name: "buildB",
243+
CreationTimestamp: creationTimestamp,
244+
},
245+
Spec: BuildSpec{
246+
Tasks: []Task{
247+
{
248+
Builder: &BuilderTask{
249+
Dependencies: []string{
250+
"camel:quartz",
251+
"mvn:org.apache.camel.k:camel-k-cron",
252+
"mvn:org.apache.camel.k:camel-k-runtime",
253+
"mvn:org.apache.camel.quarkus:camel-quarkus-yaml-dsl",
254+
},
255+
Runtime: RuntimeSpec{
256+
Version: "3.2.3",
257+
},
258+
},
259+
},
260+
},
261+
},
262+
Status: BuildStatus{
263+
Phase: BuildPhaseScheduling,
264+
},
265+
}
266+
267+
buildList := BuildList{
268+
Items: []Build{buildA, buildB},
269+
}
270+
271+
// each build uses a different runtime, so they should not match
272+
273+
matches, buildMatch := buildList.HasMatchingBuild(&buildA)
274+
assert.False(t, matches)
275+
assert.Nil(t, buildMatch)
276+
matches, buildMatch = buildList.HasMatchingBuild(&buildB)
277+
assert.False(t, matches)
278+
assert.Nil(t, buildMatch)
279+
}
280+
281+
func TestMatchingBuildsSchedulingSameDependenciesSameRuntime(t *testing.T) {
282+
timestamp, _ := time.Parse("2006-01-02T15:04:05-0700", "2024-08-09T10:00:00Z")
283+
creationTimestamp := v1.Time{Time: timestamp}
284+
buildA := Build{
285+
ObjectMeta: v1.ObjectMeta{
286+
Name: "buildA",
287+
},
288+
Spec: BuildSpec{
289+
Tasks: []Task{
290+
{
291+
Builder: &BuilderTask{
292+
Dependencies: []string{
293+
"camel:quartz",
294+
"mvn:org.apache.camel.k:camel-k-cron",
295+
"mvn:org.apache.camel.k:camel-k-runtime",
296+
"mvn:org.apache.camel.quarkus:camel-quarkus-yaml-dsl",
297+
},
298+
Runtime: RuntimeSpec{
299+
Version: "3.8.1",
300+
},
301+
},
302+
},
303+
},
304+
},
305+
Status: BuildStatus{
306+
Phase: BuildPhaseScheduling,
307+
},
308+
}
309+
buildB := Build{
310+
ObjectMeta: v1.ObjectMeta{
311+
Name: "buildB",
312+
CreationTimestamp: creationTimestamp,
313+
},
314+
Spec: BuildSpec{
315+
Tasks: []Task{
316+
{
317+
Builder: &BuilderTask{
318+
Dependencies: []string{
319+
"camel:quartz",
320+
"mvn:org.apache.camel.k:camel-k-cron",
321+
"mvn:org.apache.camel.k:camel-k-runtime",
322+
"mvn:org.apache.camel.quarkus:camel-quarkus-yaml-dsl",
323+
},
324+
Runtime: RuntimeSpec{
325+
Version: "3.8.1",
326+
},
327+
},
328+
},
329+
},
330+
},
331+
Status: BuildStatus{
332+
Phase: BuildPhaseScheduling,
333+
},
334+
}
335+
336+
buildList := BuildList{
337+
Items: []Build{buildA, buildB},
338+
}
339+
340+
// ebuilds have the same dependencies, runtime and creation timestamp
341+
342+
matches, buildMatch := buildList.HasMatchingBuild(&buildA)
343+
assert.False(t, matches)
344+
assert.Nil(t, buildMatch)
345+
matches, buildMatch = buildList.HasMatchingBuild(&buildB)
346+
assert.True(t, matches)
347+
assert.True(t, buildMatch.Name == buildA.Name)
348+
}
349+
350+
func TestMatchingBuildsSchedulingFewCommonDependencies(t *testing.T) {
351+
timestamp, _ := time.Parse("2006-01-02T15:04:05-0700", "2024-08-09T10:00:00Z")
352+
creationTimestamp := v1.Time{Time: timestamp}
353+
buildA := Build{
354+
ObjectMeta: v1.ObjectMeta{
355+
Name: "buildA",
356+
},
357+
Spec: BuildSpec{
358+
Tasks: []Task{
359+
{
360+
Builder: &BuilderTask{
361+
Dependencies: []string{
362+
"camel:quartz",
363+
"camel:componenta1",
364+
"camel:componentb1",
365+
"camel:componentc1",
366+
"camel:componentd1",
367+
"camel:componente1",
368+
"camel:componentf1",
369+
"camel:componentg1",
370+
"camel:componenth1",
371+
"camel:componenti1",
372+
},
373+
Runtime: RuntimeSpec{
374+
Version: "3.8.1",
375+
},
376+
},
377+
},
378+
},
379+
},
380+
Status: BuildStatus{
381+
Phase: BuildPhaseScheduling,
382+
},
383+
}
384+
buildB := Build{
385+
ObjectMeta: v1.ObjectMeta{
386+
Name: "buildB",
387+
CreationTimestamp: creationTimestamp,
388+
},
389+
Spec: BuildSpec{
390+
Tasks: []Task{
391+
{
392+
Builder: &BuilderTask{
393+
Dependencies: []string{
394+
"camel:quartz",
395+
"camel:componenta2",
396+
"camel:componentb2",
397+
"camel:componentc2",
398+
"camel:componentd2",
399+
"camel:componente2",
400+
"camel:componentf2",
401+
"camel:componentg2",
402+
"camel:componenth2",
403+
"camel:componenti2",
404+
},
405+
Runtime: RuntimeSpec{
406+
Version: "3.8.1",
407+
},
408+
},
409+
},
410+
},
411+
},
412+
Status: BuildStatus{
413+
Phase: BuildPhaseScheduling,
414+
},
415+
}
416+
417+
buildList := BuildList{
418+
Items: []Build{buildA, buildB},
419+
}
420+
421+
// builds have only 1 out of 10 shared dependencies. they should not match
422+
423+
matches, buildMatch := buildList.HasMatchingBuild(&buildA)
424+
assert.False(t, matches)
425+
assert.Nil(t, buildMatch)
426+
matches, buildMatch = buildList.HasMatchingBuild(&buildB)
427+
assert.False(t, matches)
428+
assert.Nil(t, buildMatch)
429+
}

0 commit comments

Comments
 (0)