Skip to content

Commit 2638a37

Browse files
feat(preflight): Add a check for storage containers
Add pre-flight checks to ensure the storage container mentioned in CSI Provider storage class config parameters is present in all relevant AOS clusters i.e. control plane and workers. Co-authored-by: Daniel Lipovetsky <daniel.lipovetsky@nutanix.com>
1 parent 35fd169 commit 2638a37

File tree

7 files changed

+1598
-40
lines changed

7 files changed

+1598
-40
lines changed

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ require (
2020
github.com/google/uuid v1.6.0
2121
github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api v0.0.0-00010101000000-000000000000
2222
github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common v0.7.0
23-
github.com/nutanix-cloud-native/prism-go-client v0.5.1
23+
github.com/nutanix-cloud-native/prism-go-client v0.5.2-0.20250602134145-662333927e6d
2424
github.com/nutanix/ntnx-api-golang-clients/clustermgmt-go-client/v4 v4.0.1-beta.2
2525
github.com/nutanix/ntnx-api-golang-clients/networking-go-client/v4 v4.0.2-beta.1
2626
github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1
@@ -105,7 +105,7 @@ require (
105105
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect
106106
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
107107
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
108-
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
108+
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
109109
github.com/huandu/xstrings v1.5.0 // indirect
110110
github.com/inconshreveable/mousetrap v1.1.0 // indirect
111111
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -122,7 +122,6 @@ require (
122122
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
123123
github.com/modern-go/reflect2 v1.0.2 // indirect
124124
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
125-
github.com/nutanix/ntnx-api-golang-clients/storage-go-client/v4 v4.0.2-alpha.3 // indirect
126125
github.com/nutanix/ntnx-api-golang-clients/volumes-go-client/v4 v4.0.1-beta.1 // indirect
127126
github.com/oklog/ulid v1.3.1 // indirect
128127
github.com/olekukonko/tablewriter v0.0.5 // indirect

go.sum

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,12 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy
166166
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
167167
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
168168
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
169-
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
170169
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
171170
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
172-
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
173-
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
174-
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
175-
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
171+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
172+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
173+
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
174+
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
176175
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
177176
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
178177
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -228,16 +227,14 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
228227
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
229228
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
230229
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
231-
github.com/nutanix-cloud-native/prism-go-client v0.5.1 h1:ykiXPCILzEMORHz7XvI8KXNomChsdLIpOAlT/YqBCmo=
232-
github.com/nutanix-cloud-native/prism-go-client v0.5.1/go.mod h1:QhLX+sEep0cStzHVYU6mPgIlnA8U3DySskagrbDprRk=
230+
github.com/nutanix-cloud-native/prism-go-client v0.5.2-0.20250602134145-662333927e6d h1:ZjrHbyZLeaWMhtBNCe8uIfvAHs0ebqx8WxJRW59hnMg=
231+
github.com/nutanix-cloud-native/prism-go-client v0.5.2-0.20250602134145-662333927e6d/go.mod h1:N/O9fz5fimjb30RxlPbKbGs/Z2lqMgDqrb6CrsZvQrA=
233232
github.com/nutanix/ntnx-api-golang-clients/clustermgmt-go-client/v4 v4.0.1-beta.2 h1:s1u5/GEw3mTZakepJoTD1OvPVU1YuioRxmKZin+W99s=
234233
github.com/nutanix/ntnx-api-golang-clients/clustermgmt-go-client/v4 v4.0.1-beta.2/go.mod h1:sd4Fnk6MVfEDVY+8WyRoQTmLhi2SgZ3riySWErVHf8E=
235234
github.com/nutanix/ntnx-api-golang-clients/networking-go-client/v4 v4.0.2-beta.1 h1:PvZQwYhhJtxmzLpnzEhHTpp2fV6woc6W65PHGsHzVfs=
236235
github.com/nutanix/ntnx-api-golang-clients/networking-go-client/v4 v4.0.2-beta.1/go.mod h1:+eZgV1+xL/r84qmuFSVt5R8OFRO70rEz92jOnVgJNco=
237236
github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1 h1:hvy3QCc2SgVidYxTq0rRPOazJOt1PP8A86kW7j6sywU=
238237
github.com/nutanix/ntnx-api-golang-clients/prism-go-client/v4 v4.0.1-beta.1/go.mod h1:Yhk+xD4mN90OKEHnk5ARf97CX5p4+MEC/B/YIVoZeZ0=
239-
github.com/nutanix/ntnx-api-golang-clients/storage-go-client/v4 v4.0.2-alpha.3 h1:K3I9YtqKcKKxSL4+tcxnFeLOoaptiVlpsOJ9Xzq3shM=
240-
github.com/nutanix/ntnx-api-golang-clients/storage-go-client/v4 v4.0.2-alpha.3/go.mod h1:kz3gO87xtWnPOCP2kN7yw5LvCDVRnvg8BOWL7CarqXA=
241238
github.com/nutanix/ntnx-api-golang-clients/vmm-go-client/v4 v4.0.1-beta.1 h1:XuTRvYu1kiNjdXOYVwyjhKlFWyo9nMit6GsOYV8+5Cg=
242239
github.com/nutanix/ntnx-api-golang-clients/vmm-go-client/v4 v4.0.1-beta.1/go.mod h1:CaWm4GFpAjQQDc6YXl/dUDrHpuW54h8j6Cj7EslE4Qk=
243240
github.com/nutanix/ntnx-api-golang-clients/volumes-go-client/v4 v4.0.1-beta.1 h1:VJSaQDnnYeNEk1mkQqEbt573OdM62+5s/B0e9kszdas=
@@ -304,7 +301,6 @@ github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8w
304301
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
305302
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
306303
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
307-
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
308304
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
309305
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
310306
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

pkg/webhook/preflight/nutanix/checker_test.go

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,24 @@ func (m *mockCheck) Run(ctx context.Context) preflight.CheckResult {
3333

3434
func TestNutanixChecker_Init(t *testing.T) {
3535
tests := []struct {
36-
name string
37-
nutanixConfig *carenv1.NutanixClusterConfigSpec
38-
workerNodeConfigs map[string]*carenv1.NutanixWorkerNodeConfigSpec
39-
expectedCheckCount int
40-
expectedFirstCheckName string
41-
expectedSecondCheckName string
42-
vmImageCheckCount int
36+
name string
37+
nutanixConfig *carenv1.NutanixClusterConfigSpec
38+
workerNodeConfigs map[string]*carenv1.NutanixWorkerNodeConfigSpec
39+
expectedCheckCount int
40+
expectedFirstCheckName string
41+
expectedSecondCheckName string
42+
vmImageCheckCount int
43+
storageContainerCheckCount int
4344
}{
4445
{
45-
name: "basic initialization with no configs",
46-
nutanixConfig: nil,
47-
workerNodeConfigs: nil,
48-
expectedCheckCount: 2, // config check and credentials check
49-
expectedFirstCheckName: "NutanixConfiguration",
50-
expectedSecondCheckName: "NutanixCredentials",
51-
vmImageCheckCount: 0,
46+
name: "basic initialization with no configs",
47+
nutanixConfig: nil,
48+
workerNodeConfigs: nil,
49+
expectedCheckCount: 2, // config check and credentials check
50+
expectedFirstCheckName: "NutanixConfiguration",
51+
expectedSecondCheckName: "NutanixCredentials",
52+
vmImageCheckCount: 0,
53+
storageContainerCheckCount: 0,
5254
},
5355
{
5456
name: "initialization with control plane config",
@@ -57,11 +59,12 @@ func TestNutanixChecker_Init(t *testing.T) {
5759
Nutanix: &carenv1.NutanixNodeSpec{},
5860
},
5961
},
60-
workerNodeConfigs: nil,
61-
expectedCheckCount: 3, // config check, credentials check, 1 VM image check
62-
expectedFirstCheckName: "NutanixConfiguration",
63-
expectedSecondCheckName: "NutanixCredentials",
64-
vmImageCheckCount: 1,
62+
workerNodeConfigs: nil,
63+
expectedCheckCount: 4, // config check, credentials check, 1 VM image check, 1 storage container check
64+
expectedFirstCheckName: "NutanixConfiguration",
65+
expectedSecondCheckName: "NutanixCredentials",
66+
vmImageCheckCount: 1,
67+
storageContainerCheckCount: 1,
6568
},
6669
{
6770
name: "initialization with worker node configs",
@@ -74,10 +77,11 @@ func TestNutanixChecker_Init(t *testing.T) {
7477
Nutanix: &carenv1.NutanixNodeSpec{},
7578
},
7679
},
77-
expectedCheckCount: 4, // config check, credentials check, 2 VM image checks
78-
expectedFirstCheckName: "NutanixConfiguration",
79-
expectedSecondCheckName: "NutanixCredentials",
80-
vmImageCheckCount: 2,
80+
expectedCheckCount: 6, // config check, credentials check, 2 VM image checks, 2 storage container checks
81+
expectedFirstCheckName: "NutanixConfiguration",
82+
expectedSecondCheckName: "NutanixCredentials",
83+
vmImageCheckCount: 2,
84+
storageContainerCheckCount: 2,
8185
},
8286
{
8387
name: "initialization with both control plane and worker node configs",
@@ -91,10 +95,12 @@ func TestNutanixChecker_Init(t *testing.T) {
9195
Nutanix: &carenv1.NutanixNodeSpec{},
9296
},
9397
},
94-
expectedCheckCount: 4, // config check, credentials check, 2 VM image checks (1 CP + 1 worker)
95-
expectedFirstCheckName: "NutanixConfiguration",
96-
expectedSecondCheckName: "NutanixCredentials",
97-
vmImageCheckCount: 2,
98+
// config check, credentials check, 2 VM image checks (1 CP + 1 worker), 2 storage container checks (1 CP + 1 worker)
99+
expectedCheckCount: 6,
100+
expectedFirstCheckName: "NutanixConfiguration",
101+
expectedSecondCheckName: "NutanixCredentials",
102+
vmImageCheckCount: 2,
103+
storageContainerCheckCount: 2,
98104
},
99105
}
100106

@@ -107,6 +113,7 @@ func TestNutanixChecker_Init(t *testing.T) {
107113
configCheckCalled := false
108114
credsCheckCalled := false
109115
vmImageCheckCount := 0
116+
storageContainerCheckCount := 0
110117

111118
checker.configurationCheckFactory = func(cd *checkDependencies) preflight.Check {
112119
configCheckCalled = true
@@ -144,6 +151,19 @@ func TestNutanixChecker_Init(t *testing.T) {
144151
return checks
145152
}
146153

154+
checker.initStorageContainerChecksFunc = func(n *nutanixChecker) []preflight.Check {
155+
checks := []preflight.Check{}
156+
for i := 0; i < tt.storageContainerCheckCount; i++ {
157+
storageContainerCheckCount++
158+
checks = append(checks, func(ctx context.Context) preflight.CheckResult {
159+
return preflight.CheckResult{
160+
Name: fmt.Sprintf("StorageContainerCheck-%d", i),
161+
}
162+
})
163+
}
164+
return checks
165+
}
166+
147167
// Call Init
148168
ctx := context.Background()
149169
checks := checker.Init(ctx, nil, &clusterv1.Cluster{
@@ -160,6 +180,12 @@ func TestNutanixChecker_Init(t *testing.T) {
160180
assert.True(t, configCheckCalled, "initNutanixConfiguration should have been called")
161181
assert.True(t, credsCheckCalled, "initCredentialsCheck should have been called")
162182
assert.Equal(t, tt.vmImageCheckCount, vmImageCheckCount, "Wrong number of VM image checks")
183+
assert.Equal(
184+
t,
185+
tt.storageContainerCheckCount,
186+
storageContainerCheckCount,
187+
"Wrong number of storage container checks",
188+
)
163189

164190
// Verify the first two checks when we have results
165191
if len(checks) >= 2 {

pkg/webhook/preflight/nutanix/clients.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"fmt"
99

10+
clustermgmtv4 "github.com/nutanix/ntnx-api-golang-clients/clustermgmt-go-client/v4/models/clustermgmt/v4/config"
1011
vmmv4 "github.com/nutanix/ntnx-api-golang-clients/vmm-go-client/v4/models/vmm/v4/content"
1112

1213
prismgoclient "github.com/nutanix-cloud-native/prism-go-client"
@@ -29,6 +30,24 @@ type client interface {
2930
*vmmv4.ListImagesApiResponse,
3031
error,
3132
)
33+
GetClusterById(id *string) (*clustermgmtv4.GetClusterApiResponse, error)
34+
ListClusters(
35+
page_ *int,
36+
limit_ *int,
37+
filter_ *string,
38+
orderby_ *string,
39+
apply_ *string,
40+
select_ *string,
41+
args ...map[string]interface{},
42+
) (*clustermgmtv4.ListClustersApiResponse, error)
43+
ListStorageContainers(
44+
page_ *int,
45+
limit_ *int,
46+
filter_ *string,
47+
orderby_ *string,
48+
select_ *string,
49+
args ...map[string]interface{},
50+
) (*clustermgmtv4.ListStorageContainersApiResponse, error)
3251
}
3352

3453
// clientWrapper implements the client interface and wraps both v3 and v4 clients.
@@ -90,3 +109,59 @@ func (c *clientWrapper) ListImages(page_ *int,
90109
}
91110
return resp, nil
92111
}
112+
113+
func (c *clientWrapper) GetClusterById(id *string) (*clustermgmtv4.GetClusterApiResponse, error) {
114+
resp, err := c.v4client.ClustersApiInstance.GetClusterById(id)
115+
if err != nil {
116+
return nil, err
117+
}
118+
return resp, nil
119+
}
120+
121+
func (c *clientWrapper) ListClusters(
122+
page_ *int,
123+
limit_ *int,
124+
filter_ *string,
125+
orderby_ *string,
126+
apply_ *string,
127+
select_ *string,
128+
args ...map[string]interface{},
129+
) (*clustermgmtv4.ListClustersApiResponse, error) {
130+
resp, err := c.v4client.ClustersApiInstance.ListClusters(
131+
page_,
132+
limit_,
133+
filter_,
134+
orderby_,
135+
apply_,
136+
select_,
137+
args...,
138+
)
139+
if err != nil {
140+
return nil, err
141+
}
142+
return resp, nil
143+
}
144+
145+
func (c *clientWrapper) ListStorageContainers(
146+
page_ *int,
147+
limit_ *int,
148+
filter_ *string,
149+
orderby_ *string,
150+
select_ *string,
151+
args ...map[string]interface{},
152+
) (*clustermgmtv4.ListStorageContainersApiResponse, error) {
153+
resp, err := c.v4client.StorageContainerAPI.ListStorageContainers(
154+
page_,
155+
limit_,
156+
filter_,
157+
orderby_,
158+
select_,
159+
args...,
160+
)
161+
if err != nil {
162+
return nil, err
163+
}
164+
return resp, nil
165+
}
166+
167+

pkg/webhook/preflight/nutanix/clients_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package nutanix
66
import (
77
"context"
88

9+
clustermgmtv4 "github.com/nutanix/ntnx-api-golang-clients/clustermgmt-go-client/v4/models/clustermgmt/v4/config"
910
vmmv4 "github.com/nutanix/ntnx-api-golang-clients/vmm-go-client/v4/models/vmm/v4/content"
1011

1112
prismv3 "github.com/nutanix-cloud-native/prism-go-client/v3"
@@ -35,6 +36,27 @@ type mocknclient struct {
3536
*vmmv4.ListImagesApiResponse,
3637
error,
3738
)
39+
40+
getClusterByIdFunc func(id *string) (*clustermgmtv4.GetClusterApiResponse, error)
41+
42+
listClustersFunc func(
43+
page,
44+
limit *int,
45+
filter,
46+
orderby,
47+
apply,
48+
select_ *string,
49+
args ...map[string]interface{},
50+
) (*clustermgmtv4.ListClustersApiResponse, error)
51+
52+
listStorageContainersFunc func(
53+
page,
54+
limit *int,
55+
filter,
56+
orderby,
57+
select_ *string,
58+
args ...map[string]interface{},
59+
) (*clustermgmtv4.ListStorageContainersApiResponse, error)
3860
}
3961

4062
func (m *mocknclient) GetCurrentLoggedInUser(ctx context.Context) (*prismv3.UserIntentResponse, error) {
@@ -52,3 +74,23 @@ func (m *mocknclient) ListImages(
5274
) (*vmmv4.ListImagesApiResponse, error) {
5375
return m.listImagesFunc(page, limit, filter, orderby, select_)
5476
}
77+
78+
func (m *mocknclient) GetClusterById(id *string) (*clustermgmtv4.GetClusterApiResponse, error) {
79+
return m.getClusterByIdFunc(id)
80+
}
81+
82+
func (m *mocknclient) ListClusters(
83+
page, limit *int,
84+
filter, orderby, apply, select_ *string,
85+
args ...map[string]interface{},
86+
) (*clustermgmtv4.ListClustersApiResponse, error) {
87+
return m.listClustersFunc(page, limit, filter, orderby, apply, select_, args...)
88+
}
89+
90+
func (m *mocknclient) ListStorageContainers(
91+
page, limit *int,
92+
filter, orderby, select_ *string,
93+
args ...map[string]interface{},
94+
) (*clustermgmtv4.ListStorageContainersApiResponse, error) {
95+
return m.listStorageContainersFunc(page, limit, filter, orderby, select_, args...)
96+
}

0 commit comments

Comments
 (0)