Skip to content

Commit fdb736f

Browse files
committed
fix: tests
1 parent 883f556 commit fdb736f

File tree

12 files changed

+409
-49
lines changed

12 files changed

+409
-49
lines changed

cmd/controller/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ limitations under the License.
1616
package main
1717

1818
import (
19+
"os"
20+
1921
ibmcloud "github.com/pfeifferj/karpenter-provider-ibm-cloud/pkg/cloudprovider"
2022
"github.com/pfeifferj/karpenter-provider-ibm-cloud/pkg/controllers"
2123
"github.com/pfeifferj/karpenter-provider-ibm-cloud/pkg/operator"
2224
"github.com/pfeifferj/karpenter-provider-ibm-cloud/pkg/operator/options"
2325

26+
"sigs.k8s.io/controller-runtime/pkg/log"
2427
"sigs.k8s.io/karpenter/pkg/cloudprovider/metrics"
2528
corecontrollers "sigs.k8s.io/karpenter/pkg/controllers"
2629
"sigs.k8s.io/karpenter/pkg/controllers/state"
@@ -63,7 +66,8 @@ func main() {
6366

6467
// Register bootstrap controller
6568
if err := controllers.RegisterBootstrapController(op.Manager); err != nil {
66-
panic(err)
69+
log.FromContext(ctx).Error(err, "failed to register bootstrap controller")
70+
os.Exit(1)
6771
}
6872

6973
// Register controllers using the proper operator pattern

go.sum

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
github.com/IBM/go-sdk-core/v5 v5.20.1 h1:dzeyifh1kfRLw8VfAIIS5okZYuqLTqplPZP/Kcsgdlo=
22
github.com/IBM/go-sdk-core/v5 v5.20.1/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw=
3-
github.com/IBM/platform-services-go-sdk v0.85.0 h1:JuoinnP9qMvnz5mIl574QWBd+wj6isG8NkrFM6mPdP0=
4-
github.com/IBM/platform-services-go-sdk v0.85.0/go.mod h1:aGD045m6I8pfcB77wft8w2cHqWOJjcM3YSSV55BX0Js=
53
github.com/IBM/platform-services-go-sdk v0.85.1 h1:lrBEeGaIajhSPMB6cPVAx53XTtVGrKOeA36gIXh2FYI=
64
github.com/IBM/platform-services-go-sdk v0.85.1/go.mod h1:aGD045m6I8pfcB77wft8w2cHqWOJjcM3YSSV55BX0Js=
75
github.com/IBM/vpc-go-sdk v0.70.1 h1:6NsbRkiA5gDNxe7cjNx8Pi1j9s0PlhwNQj29wsKZxAo=
@@ -132,8 +130,6 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
132130
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
133131
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
134132
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
135-
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
136-
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
137133
github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
138134
github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
139135
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -145,18 +141,12 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
145141
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
146142
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
147143
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
148-
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
149-
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
150144
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
151145
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
152146
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
153147
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
154-
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
155-
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
156148
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
157149
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
158-
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
159-
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
160150
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
161151
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
162152
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -203,8 +193,6 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
203193
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
204194
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
205195
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
206-
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
207-
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
208196
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
209197
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
210198
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -215,12 +203,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
215203
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
216204
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
217205
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
218-
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
219-
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
220206
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
221207
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
222-
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
223-
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
224208
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
225209
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
226210
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -300,8 +284,6 @@ sigs.k8s.io/controller-tools v0.18.0 h1:rGxGZCZTV2wJreeRgqVoWab/mfcumTMmSwKzoM9x
300284
sigs.k8s.io/controller-tools v0.18.0/go.mod h1:gLKoiGBriyNh+x1rWtUQnakUYEujErjXs9pf+x/8n1U=
301285
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
302286
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
303-
sigs.k8s.io/karpenter v1.6.0 h1:82SIWF3FIaTkD5WFx+2wjCqgkZK5m1CHY5p+uIu4ScI=
304-
sigs.k8s.io/karpenter v1.6.0/go.mod h1:AxCaeRjv1Pgw/Ff7vT4aqyXcg8v1UdBcfzWMCaKSVjA=
305287
sigs.k8s.io/karpenter v1.6.1 h1:ZAC802Prk/GyKoGUu0LuzEn9fFmJLfUtMfo64derQgw=
306288
sigs.k8s.io/karpenter v1.6.1/go.mod h1:AxCaeRjv1Pgw/Ff7vT4aqyXcg8v1UdBcfzWMCaKSVjA=
307289
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=

pkg/apis/group_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package apis
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
func TestGroup(t *testing.T) {
26+
t.Run("Group constant is correctly defined", func(t *testing.T) {
27+
assert.Equal(t, "karpenter.ibm.sh", Group)
28+
assert.NotEmpty(t, Group)
29+
})
30+
31+
t.Run("Group follows Kubernetes API group naming convention", func(t *testing.T) {
32+
assert.Contains(t, Group, ".")
33+
assert.Contains(t, Group, "karpenter")
34+
assert.Contains(t, Group, "ibm")
35+
assert.NotContains(t, Group, " ")
36+
assert.NotContains(t, Group, "/")
37+
})
38+
}

pkg/cache/cache_test.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ import (
2323
"github.com/stretchr/testify/assert"
2424
)
2525

26+
// pollUntil polls a condition until it's true or timeout
27+
func pollUntil(t *testing.T, condition func() bool, timeout time.Duration, message string) {
28+
t.Helper()
29+
deadline := time.Now().Add(timeout)
30+
for time.Now().Before(deadline) {
31+
if condition() {
32+
return
33+
}
34+
time.Sleep(5 * time.Millisecond) // Small polling interval
35+
}
36+
t.Fatal(message)
37+
}
38+
2639
func TestNewCache(t *testing.T) {
2740
ttl := 5 * time.Minute
2841
cache := New(ttl)
@@ -75,8 +88,11 @@ func TestCacheSetWithTTL(t *testing.T) {
7588
assert.True(t, exists)
7689
assert.Equal(t, value, retrieved)
7790

78-
// Wait for expiration
79-
time.Sleep(150 * time.Millisecond)
91+
// Wait for expiration using polling
92+
pollUntil(t, func() bool {
93+
_, expired := cache.Get(key)
94+
return !expired
95+
}, 500*time.Millisecond, "cache entry should have expired")
8096

8197
// Should be expired
8298
retrieved, exists = cache.Get(key)
@@ -211,8 +227,12 @@ func TestCacheGetOrSetWithTTL(t *testing.T) {
211227
assert.Equal(t, expectedValue, value)
212228
assert.Equal(t, 1, callCount)
213229

214-
// Wait for expiration
215-
time.Sleep(150 * time.Millisecond)
230+
// Wait for expiration using polling
231+
pollUntil(t, func() bool {
232+
// Try to get the value - if it doesn't exist, it means it expired
233+
_, expired := cache.Get(key)
234+
return !expired
235+
}, 500*time.Millisecond, "cache entry should have expired")
216236

217237
// Should fetch again after expiration
218238
value, err = cache.GetOrSetWithTTL(key, ttl, fetchFunc)
@@ -231,8 +251,11 @@ func TestCacheExpiration(t *testing.T) {
231251
cache.Set(key, value)
232252
assert.True(t, cache.Has(key))
233253

234-
// Wait for expiration
235-
time.Sleep(100 * time.Millisecond)
254+
// Wait for expiration using polling
255+
pollUntil(t, func() bool {
256+
_, expired := cache.Get(key)
257+
return !expired
258+
}, 500*time.Millisecond, "cache entry should have expired")
236259

237260
// Get should remove expired item and return false
238261
retrieved, exists := cache.Get(key)
@@ -250,8 +273,10 @@ func TestCacheCleanup(t *testing.T) {
250273
cache.Set(key, value)
251274
assert.Equal(t, 1, cache.Size())
252275

253-
// Wait for cleanup to run (cleanup runs every TTL/2)
254-
time.Sleep(100 * time.Millisecond)
276+
// Wait for cleanup to run using polling (cleanup runs every TTL/2)
277+
pollUntil(t, func() bool {
278+
return cache.Size() == 0
279+
}, 500*time.Millisecond, "cache should be cleaned up")
255280

256281
// Size should be 0 after cleanup removes expired items
257282
assert.Equal(t, 0, cache.Size())

pkg/cloudprovider/circuitbreaker_test.go

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ import (
1111
"github.com/stretchr/testify/require"
1212
)
1313

14+
// pollUntil polls a condition until it's true or timeout
15+
func pollUntil(t *testing.T, condition func() bool, timeout time.Duration, message string) {
16+
t.Helper()
17+
deadline := time.Now().Add(timeout)
18+
for time.Now().Before(deadline) {
19+
if condition() {
20+
return
21+
}
22+
time.Sleep(5 * time.Millisecond)
23+
}
24+
t.Fatal(message)
25+
}
26+
1427
func TestNewCircuitBreaker(t *testing.T) {
1528
tests := []struct {
1629
name string
@@ -176,16 +189,18 @@ func TestCircuitBreaker_RecoveryFlow(t *testing.T) {
176189
cb.RecordFailure("test-nodeclass", "us-south", fmt.Errorf("test error 2"))
177190
assert.Equal(t, CircuitBreakerOpen, cb.state)
178191

179-
// Wait for recovery timeout
180-
time.Sleep(150 * time.Millisecond)
192+
// Wait for recovery timeout using polling
193+
pollUntil(t, func() bool {
194+
// Check if we can provision - if so, circuit breaker is in half-open
195+
err := cb.CanProvision(ctx, "test-nodeclass", "us-south", 0)
196+
return err == nil
197+
}, 500*time.Millisecond, "circuit breaker should transition to half-open")
181198

182-
// Should transition to half-open and allow limited requests
183-
err := cb.CanProvision(ctx, "test-nodeclass", "us-south", 0)
184-
assert.NoError(t, err)
199+
// Should be in half-open state now
185200
assert.Equal(t, CircuitBreakerHalfOpen, cb.state)
186201

187202
// Second request should be blocked (exceeds HalfOpenMaxRequests)
188-
err = cb.CanProvision(ctx, "test-nodeclass", "us-south", 0)
203+
err := cb.CanProvision(ctx, "test-nodeclass", "us-south", 0)
189204
assert.Error(t, err)
190205
assert.IsType(t, &CircuitBreakerError{}, err)
191206

@@ -215,11 +230,11 @@ func TestCircuitBreaker_HalfOpenFailureReturnsToOpen(t *testing.T) {
215230
cb.RecordFailure("test-nodeclass", "us-south", fmt.Errorf("test error 2"))
216231
assert.Equal(t, CircuitBreakerOpen, cb.state)
217232

218-
// Wait for recovery and transition to half-open
219-
time.Sleep(150 * time.Millisecond)
220-
err := cb.CanProvision(context.Background(), "test-nodeclass", "us-south", 0)
221-
assert.NoError(t, err)
222-
assert.Equal(t, CircuitBreakerHalfOpen, cb.state)
233+
// Wait for recovery and transition to half-open using polling
234+
pollUntil(t, func() bool {
235+
err := cb.CanProvision(context.Background(), "test-nodeclass", "us-south", 0)
236+
return err == nil && cb.state == CircuitBreakerHalfOpen
237+
}, 500*time.Millisecond, "circuit breaker should transition to half-open")
223238

224239
// Record failure in half-open state should return to open
225240
cb.RecordFailure("test-nodeclass", "us-south", fmt.Errorf("test error 3"))
@@ -362,13 +377,16 @@ func TestCircuitBreaker_CleanOldFailures(t *testing.T) {
362377
require.NoError(t, err)
363378
assert.Equal(t, 2, status.RecentFailures)
364379

365-
// Wait for failures to age out
366-
time.Sleep(150 * time.Millisecond)
380+
// Wait for failures to age out using polling
381+
pollUntil(t, func() bool {
382+
st, stErr := cb.GetState()
383+
return stErr == nil && st.RecentFailures == 0
384+
}, 500*time.Millisecond, "old failures should be cleaned up")
367385

368-
// Trigger cleanup by calling GetState
386+
// Verify cleanup occurred
369387
status, err = cb.GetState()
370388
require.NoError(t, err)
371-
assert.Equal(t, 0, status.RecentFailures) // Old failures should be cleaned up
389+
assert.Equal(t, 0, status.RecentFailures)
372390
}
373391

374392
func TestCircuitBreaker_ErrorTypes(t *testing.T) {

pkg/constants/constants_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package constants
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
func TestConstants(t *testing.T) {
26+
t.Run("Group constant is correctly defined", func(t *testing.T) {
27+
assert.Equal(t, "karpenter.ibm.sh", Group)
28+
assert.NotEmpty(t, Group)
29+
})
30+
31+
t.Run("Group constant follows Kubernetes naming convention", func(t *testing.T) {
32+
assert.Contains(t, Group, ".")
33+
assert.Contains(t, Group, "karpenter")
34+
assert.Contains(t, Group, "ibm")
35+
})
36+
}

0 commit comments

Comments
 (0)