@@ -25,6 +25,7 @@ import (
25
25
"testing"
26
26
"time"
27
27
28
+ "k8s.io/node-problem-detector/pkg/util/tomb"
28
29
"k8s.io/node-problem-detector/test/e2e/lib/gce"
29
30
"k8s.io/test-infra/boskos/client"
30
31
@@ -54,6 +55,16 @@ var boskosWaitDuration = flag.Duration("boskos-wait-duration", 2*time.Minute,
54
55
55
56
var computeService * compute.Service
56
57
58
+ // boskosClient helps renting project from Boskos, and is only initialized on Ginkgo node 1.
59
+ var boskosClient * client.Client
60
+
61
+ // boskosRenewingTomb stops the goroutine keep renewing the Boskos resources.
62
+ var boskosRenewingTomb * tomb.Tomb
63
+
64
+ // SynchronizedBeforeSuite and SynchronizedAfterSuite help manages singleton resource (a Boskos project) across Ginkgo nodes.
65
+ var _ = ginkgo .SynchronizedBeforeSuite (rentBoskosProjectIfNeededOnNode1 , acceptBoskosProjectIfNeededFromNode1 )
66
+ var _ = ginkgo .SynchronizedAfterSuite (func () {}, releaseBoskosResourcesOnNode1 )
67
+
57
68
func TestNPD (t * testing.T ) {
58
69
if testing .Short () {
59
70
t .Skip ("skipping test in short mode." )
@@ -65,13 +76,6 @@ func TestNPD(t *testing.T) {
65
76
panic (fmt .Sprintf ("Unable to create gcloud compute service using defaults. Make sure you are authenticated. %v" , err ))
66
77
}
67
78
68
- if * project == "" {
69
- boskosClient := client .NewClient (* jobName , * boskosServerURL )
70
- * project = acquireProjectOrDie (boskosClient )
71
-
72
- defer releaseProjectOrDie (boskosClient )
73
- }
74
-
75
79
if * artifactsDir != "" {
76
80
_ , err := os .Stat (* artifactsDir )
77
81
if err != nil && os .IsNotExist (err ) {
@@ -84,30 +88,72 @@ func TestNPD(t *testing.T) {
84
88
ginkgo .RunSpecsWithDefaultAndCustomReporters (t , "NPD Metric-only Suite" , []ginkgo.Reporter {junitReporter })
85
89
}
86
90
87
- func acquireProjectOrDie (boskosClient * client.Client ) string {
91
+ // rentBoskosProjectIfNeededOnNode1 rents a GCP project from Boskos if no GCP project is specified.
92
+ //
93
+ // rentBoskosProjectIfNeededOnNode1 returns a byte slice containing the project name.
94
+ // rentBoskosProjectIfNeededOnNode1 also initializes boskosClient if necessary.
95
+ // When the tests run in parallel mode in Ginkgo, this rentBoskosProjectIfNeededOnNode1 runs only on
96
+ // Ginkgo node 1. The output should be shared with all other Gingko nodes so that they all use the same
97
+ // GCP project.
98
+ func rentBoskosProjectIfNeededOnNode1 () []byte {
99
+ if * project != "" {
100
+ return []byte {}
101
+ }
102
+
88
103
fmt .Printf ("Renting project from Boskos\n " )
104
+ boskosClient = client .NewClient (* jobName , * boskosServerURL )
105
+ boskosRenewingTomb = tomb .NewTomb ()
106
+
89
107
ctx , cancel := context .WithTimeout (context .Background (), * boskosWaitDuration )
90
108
defer cancel ()
91
109
p , err := boskosClient .AcquireWait (ctx , * boskosProjectType , "free" , "busy" )
92
110
Expect (err ).NotTo (HaveOccurred (), fmt .Sprintf ("Unable to rent project from Boskos: %v\n " , err ))
111
+ fmt .Printf ("Rented project %q from Boskos\n " , p .Name )
112
+
113
+ go renewBoskosProject (boskosClient , p .Name , boskosRenewingTomb )
93
114
94
- fmt .Printf ("Rented project %s from Boskos" , p .Name )
115
+ return []byte (p .Name )
116
+ }
117
+
118
+ // acceptBoskosProjectIfNeededFromNode1 accepts a GCP project rented from Boskos by Ginkgo node 1.
119
+ //
120
+ // acceptBoskosProjectIfNeededFromNode1 takes the output of rentBoskosProjectIfNeededOnNode1.
121
+ // When the tests run in parallel mode in Ginkgo, this function runs on all Ginkgo nodes.
122
+ func acceptBoskosProjectIfNeededFromNode1 (data []byte ) {
123
+ if * project != "" {
124
+ return
125
+ }
126
+
127
+ boskosProject := string (data )
128
+ fmt .Printf ("Received Boskos project %q from Ginkgo node 1.\n " , boskosProject )
129
+ * project = boskosProject
130
+ }
95
131
96
- go func (boskosClient * client.Client , projectName string ) {
97
- for range time .Tick (5 * time .Minute ) {
132
+ func renewBoskosProject (boskosClient * client.Client , projectName string , boskosRenewingTomb * tomb.Tomb ) {
133
+ defer boskosRenewingTomb .Done ()
134
+ for {
135
+ select {
136
+ case <- time .Tick (5 * time .Minute ):
137
+ fmt .Printf ("Renewing boskosProject %q\n " , projectName )
98
138
if err := boskosClient .UpdateOne (projectName , "busy" , nil ); err != nil {
99
- fmt .Printf ("Failed to update status for project %s with Boskos: %v\n " , projectName , err )
139
+ fmt .Printf ("Failed to update status for project %q with Boskos: %v\n " , projectName , err )
100
140
}
141
+ case <- boskosRenewingTomb .Stopping ():
142
+ return
101
143
}
102
- }(boskosClient , p .Name )
103
-
104
- return p .Name
144
+ }
105
145
}
106
146
107
- func releaseProjectOrDie (boskosClient * client.Client ) {
147
+ // releaseBoskosResourcesOnNode1 releases all rented Boskos resources if there is any.
148
+ func releaseBoskosResourcesOnNode1 () {
149
+ if boskosClient == nil {
150
+ return
151
+ }
152
+ boskosRenewingTomb .Stop ()
108
153
if ! boskosClient .HasResource () {
109
154
return
110
155
}
156
+ fmt .Printf ("Releasing all Boskos resources.\n " )
111
157
err := boskosClient .ReleaseAll ("dirty" )
112
158
Expect (err ).NotTo (HaveOccurred (), fmt .Sprintf ("Failed to release project to Boskos: %v" , err ))
113
159
}
0 commit comments