Skip to content

Commit d752315

Browse files
fix(preflight): improved error reporting for storage container and VM image checks (#1180)
- Handle nil responses of get storage container and get cluster API calls. - Improve error wording. - Correctly construct error responses of list API calls. - Test error response of list clusters API call. - Treat a missing storageContainer as a failed check, but not an internal error. --------- Co-authored-by: Daniel Lipovetsky <daniel.lipovetsky@nutanix.com>
1 parent 9db3e90 commit d752315

File tree

3 files changed

+217
-57
lines changed

3 files changed

+217
-57
lines changed

pkg/webhook/preflight/nutanix/image_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/go-logr/logr/testr"
1212
vmmv4 "github.com/nutanix/ntnx-api-golang-clients/vmm-go-client/v4/models/vmm/v4/content"
13+
vmmv4error "github.com/nutanix/ntnx-api-golang-clients/vmm-go-client/v4/models/vmm/v4/error"
1314
"github.com/stretchr/testify/assert"
1415
"github.com/stretchr/testify/require"
1516
"k8s.io/utils/ptr"
@@ -230,6 +231,42 @@ func TestVMImageCheck(t *testing.T) {
230231
},
231232
},
232233
},
234+
{
235+
name: "listing images returns an error response",
236+
nclient: &mocknclient{
237+
listImagesFunc: func(page,
238+
limit *int,
239+
filter,
240+
orderby,
241+
select_ *string,
242+
args ...map[string]interface{},
243+
) (
244+
*vmmv4.ListImagesApiResponse,
245+
error,
246+
) {
247+
resp := &vmmv4.ListImagesApiResponse{}
248+
err := resp.SetData(*vmmv4error.NewErrorResponse())
249+
require.NoError(t, err)
250+
return resp, nil
251+
},
252+
},
253+
machineDetails: &carenv1.NutanixMachineDetails{
254+
Image: &capxv1.NutanixResourceIdentifier{
255+
Type: capxv1.NutanixIdentifierName,
256+
Name: ptr.To("test-image"),
257+
},
258+
},
259+
want: preflight.CheckResult{
260+
Allowed: false,
261+
Error: true,
262+
Causes: []preflight.Cause{
263+
{
264+
Message: "failed to get VM Image: failed to get data returned by ListImages",
265+
Field: "test-field",
266+
},
267+
},
268+
},
269+
},
233270
{
234271
name: "neither image nor imageLookup specified",
235272
nclient: &mocknclient{},

pkg/webhook/preflight/nutanix/storagecontainer.go

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ package nutanix
66
import (
77
"context"
88
"fmt"
9+
"sync"
910

1011
clustermgmtv4 "github.com/nutanix/ntnx-api-golang-clients/clustermgmt-go-client/v4/models/clustermgmt/v4/config"
11-
"k8s.io/utils/ptr"
1212

1313
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1"
1414
carenv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
@@ -62,6 +62,14 @@ func (c *storageContainerCheck) Run(ctx context.Context) preflight.CheckResult {
6262
return result
6363
}
6464

65+
clusterIdentifier := &c.nodeSpec.MachineDetails.Cluster
66+
67+
// We wait to get the cluster until we know we need to.
68+
// We should only get the cluster once.
69+
getClusterOnce := sync.OnceValues(func() (*clustermgmtv4.Cluster, error) {
70+
return getCluster(c.nclient, clusterIdentifier)
71+
})
72+
6573
for _, storageClassConfig := range c.csiSpec.StorageClassConfigs {
6674
if storageClassConfig.Parameters == nil {
6775
continue
@@ -72,19 +80,62 @@ func (c *storageContainerCheck) Run(ctx context.Context) preflight.CheckResult {
7280
continue
7381
}
7482

75-
if _, err := getStorageContainer(c.nclient, c.nodeSpec, storageContainer); err != nil {
83+
cluster, err := getClusterOnce()
84+
if err != nil {
85+
result.Allowed = false
86+
result.Error = true
87+
result.Causes = append(result.Causes, preflight.Cause{
88+
Message: fmt.Sprintf(
89+
"failed to check if storage container %q exists: failed to get cluster %q: %s",
90+
storageContainer,
91+
clusterIdentifier,
92+
err,
93+
),
94+
Field: c.field,
95+
})
96+
continue
97+
}
98+
99+
containers, err := getStorageContainers(c.nclient, *cluster.ExtId, storageContainer)
100+
if err != nil {
76101
result.Allowed = false
77102
result.Error = true
78103
result.Causes = append(result.Causes, preflight.Cause{
79104
Message: fmt.Sprintf(
80-
"failed to check if storage container named %q exists: %s",
105+
"failed to check if storage container %q exists in cluster %q: %s",
81106
storageContainer,
107+
clusterIdentifier,
82108
err,
83109
),
84110
Field: c.field,
85111
})
112+
continue
113+
}
114+
115+
if len(containers) == 0 {
116+
result.Allowed = false
117+
result.Causes = append(result.Causes, preflight.Cause{
118+
Message: fmt.Sprintf(
119+
"storage container %q not found on cluster %q",
120+
storageContainer,
121+
clusterIdentifier,
122+
),
123+
Field: c.field,
124+
})
125+
continue
126+
}
86127

87-
return result
128+
if len(containers) > 1 {
129+
result.Allowed = false
130+
result.Causes = append(result.Causes, preflight.Cause{
131+
Message: fmt.Sprintf(
132+
"multiple storage containers named %q found on cluster %q",
133+
storageContainer,
134+
clusterIdentifier,
135+
),
136+
Field: c.field,
137+
})
138+
continue
88139
}
89140
}
90141

@@ -137,44 +188,25 @@ func newStorageContainerChecks(cd *checkDependencies) []preflight.Check {
137188
return checks
138189
}
139190

140-
func getStorageContainer(
191+
func getStorageContainers(
141192
client client,
142-
nodeSpec *carenv1.NutanixNodeSpec,
193+
clusterUUID string,
143194
storageContainerName string,
144-
) (*clustermgmtv4.StorageContainer, error) {
145-
cluster, err := getCluster(client, &nodeSpec.MachineDetails.Cluster)
146-
if err != nil {
147-
return nil, fmt.Errorf("failed to get cluster: %w", err)
148-
}
149-
150-
fltr := fmt.Sprintf("name eq '%s' and clusterExtId eq '%s'", storageContainerName, *cluster.ExtId)
195+
) ([]clustermgmtv4.StorageContainer, error) {
196+
fltr := fmt.Sprintf("name eq '%s' and clusterExtId eq '%s'", storageContainerName, clusterUUID)
151197
resp, err := client.ListStorageContainers(nil, nil, &fltr, nil, nil)
152198
if err != nil {
153-
return nil, fmt.Errorf("failed to list storage containers: %w", err)
199+
return nil, err
200+
}
201+
if resp == nil || resp.GetData() == nil {
202+
// No images were returned.
203+
return []clustermgmtv4.StorageContainer{}, nil
154204
}
155-
156205
containers, ok := resp.GetData().([]clustermgmtv4.StorageContainer)
157206
if !ok {
158207
return nil, fmt.Errorf("failed to get data returned by ListStorageContainers(filter=%q)", fltr)
159208
}
160-
161-
if len(containers) == 0 {
162-
return nil, fmt.Errorf(
163-
"no storage container named %q found on cluster named %q",
164-
storageContainerName,
165-
*cluster.Name,
166-
)
167-
}
168-
169-
if len(containers) > 1 {
170-
return nil, fmt.Errorf(
171-
"multiple storage containers found with name %q on cluster %q",
172-
storageContainerName,
173-
*cluster.Name,
174-
)
175-
}
176-
177-
return ptr.To(containers[0]), nil
209+
return containers, nil
178210
}
179211

180212
func getCluster(

0 commit comments

Comments
 (0)