Skip to content

Commit 5a5f8e0

Browse files
authored
feat: Add context to preflight check messages (#1210)
**What problem does this PR solve?**: Adds more context to preflight check messages. The storage container check now returns a specific error when we find two storage containers with the same name in the same Prism Central cluster. That should never happen, since storage container names are unique within a Prism Central cluster. **Which issue(s) this PR fixes**: Fixes # **How Has This Been Tested?**: <!-- Please describe the tests that you ran to verify your changes. Provide output from the tests and any manual steps needed to replicate the tests. --> **Special notes for your reviewer**: <!-- Use this to provide any additional information to the reviewers. This may include: - Best way to review the PR. - Where the author wants the most review attention on. - etc. -->
1 parent c997c0f commit 5a5f8e0

File tree

14 files changed

+166
-104
lines changed

14 files changed

+166
-104
lines changed

pkg/webhook/preflight/generic/registry.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ func defaultRegClientGetter(opts ...regclient.Opt) regClientPinger {
5757
return regclient.New(opts...)
5858
}
5959

60-
func pingFailedReasonString(registryURL string, err error) string {
61-
return fmt.Sprintf("Failed to ping registry %s with err: %s", registryURL, err.Error())
60+
func pingFailedMessage(registryURL string, err error) string {
61+
return fmt.Sprintf(
62+
"Failed to ping registry %q: %s. First verify that the management cluster can reach the registry, then retry.", ///nolint:lll // Message is long.
63+
registryURL,
64+
err.Error(),
65+
)
6266
}
6367

6468
func (r *registryCheck) checkRegistry(
@@ -80,8 +84,12 @@ func (r *registryCheck) checkRegistry(
8084
result.InternalError = false
8185
result.Causes = append(result.Causes,
8286
preflight.Cause{
83-
Message: fmt.Sprintf("Failed to parse registry URL %q with error: %s", registryURL, err),
84-
Field: r.field + ".url",
87+
Message: fmt.Sprintf(
88+
"Failed to parse registry URL %q with error: %s. Review the Cluster.", ///nolint:lll // Message is long.",
89+
registryURL,
90+
err,
91+
),
92+
Field: r.field + ".url",
8593
},
8694
)
8795
return result
@@ -94,7 +102,8 @@ func (r *registryCheck) checkRegistry(
94102
result.Causes = append(result.Causes,
95103
preflight.Cause{
96104
Message: fmt.Sprintf(
97-
"Registry URL scheme %q is not supported. Use http or https.",
105+
"The registry URL %q uses the scheme %q. This scheme is not supported. Use either the \"http\" or \"https\" scheme.", ///nolint:lll // Message is long.
106+
registryURL,
98107
registryURLParsed.Scheme,
99108
),
100109
Field: r.field + ".url",
@@ -121,8 +130,11 @@ func (r *registryCheck) checkRegistry(
121130
result.InternalError = false
122131
result.Causes = append(result.Causes,
123132
preflight.Cause{
124-
Message: fmt.Sprintf("Registry credentials Secret %q not found", credentials.SecretRef.Name),
125-
Field: r.field + ".credentials.secretRef",
133+
Message: fmt.Sprintf(
134+
"Registry credentials Secret %q not found. Create the Secret first, then create the Cluster.", ///nolint:lll // Message is long.
135+
credentials.SecretRef.Name,
136+
),
137+
Field: r.field + ".credentials.secretRef",
126138
},
127139
)
128140
return result
@@ -132,7 +144,8 @@ func (r *registryCheck) checkRegistry(
132144
result.InternalError = true
133145
result.Causes = append(result.Causes,
134146
preflight.Cause{
135-
Message: fmt.Sprintf("Failed to get Registry credentials Secret %q: %s",
147+
Message: fmt.Sprintf(
148+
"Failed to get Registry credentials Secret %q: %s. This is usually a temporary error. Please retry.", ///nolint:lll // Message is long.
136149
credentials.SecretRef.Name,
137150
err,
138151
),
@@ -170,7 +183,7 @@ func (r *registryCheck) checkRegistry(
170183
result.Allowed = false
171184
result.Causes = append(result.Causes,
172185
preflight.Cause{
173-
Message: pingFailedReasonString(registryURL, err),
186+
Message: pingFailedMessage(registryURL, err),
174187
Field: r.field,
175188
},
176189
)

pkg/webhook/preflight/generic/registry_test.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,8 @@ func TestRegistryCheck(t *testing.T) {
135135
InternalError: true,
136136
Causes: []preflight.Cause{
137137
{
138-
Message: "Failed to get Registry credentials Secret \"test-secret\": fake error",
139-
//nolint:lll // this is a test for a field.
140-
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.globalImageRegistryMirror.credentials.secretRef",
138+
Message: "Failed to get Registry credentials Secret \"test-secret\": fake error. This is usually a temporary error. Please retry.", ///nolint:lll // Message is long.
139+
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.globalImageRegistryMirror.credentials.secretRef", //nolint:lll // Field is long.
141140
},
142141
},
143142
},
@@ -167,9 +166,8 @@ func TestRegistryCheck(t *testing.T) {
167166
InternalError: false,
168167
Causes: []preflight.Cause{
169168
{
170-
Message: "Registry credentials Secret \"test-secret\" not found",
171-
//nolint:lll // this is a test for a field.
172-
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.globalImageRegistryMirror.credentials.secretRef",
169+
Message: "Registry credentials Secret \"test-secret\" not found. Create the Secret first, then create the Cluster.", ///nolint:lll // Message is long.
170+
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.globalImageRegistryMirror.credentials.secretRef", //nolint:lll // Field is long.
173171
},
174172
},
175173
},
@@ -189,7 +187,7 @@ func TestRegistryCheck(t *testing.T) {
189187
Allowed: false,
190188
Causes: []preflight.Cause{
191189
{
192-
Message: pingFailedReasonString(
190+
Message: pingFailedMessage(
193191
testRegistryURL,
194192
testPingFailedError,
195193
),
@@ -300,11 +298,8 @@ func TestRegistryCheck(t *testing.T) {
300298
InternalError: false,
301299
Causes: []preflight.Cause{
302300
{
303-
Message: fmt.Sprintf(
304-
"Failed to parse registry URL %q with error: parse \"invalid-url\": invalid URI for request",
305-
"invalid-url",
306-
),
307-
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.imageRegistries[0].url",
301+
Message: "Failed to parse registry URL \"invalid-url\" with error: parse \"invalid-url\": invalid URI for request. Review the Cluster.", ///nolint:lll // Message is long.
302+
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.imageRegistries[0].url",
308303
},
309304
},
310305
},
@@ -345,7 +340,7 @@ func TestRegistryCheck(t *testing.T) {
345340
InternalError: false,
346341
Causes: []preflight.Cause{
347342
{
348-
Message: "Registry URL scheme \"tcp\" is not supported. Use http or https.",
343+
Message: "The registry URL \"tcp://some-registry.lol\" uses the scheme \"tcp\". This scheme is not supported. Use either the \"http\" or \"https\" scheme.", ///nolint:lll // Message is long.
349344
Field: "cluster.spec.topology.variables[.name=clusterConfig].value.imageRegistries[0].url",
350345
},
351346
},

pkg/webhook/preflight/generic/spec.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func newConfigurationCheck(
4545
configurationCheck.result.InternalError = true
4646
configurationCheck.result.Causes = append(configurationCheck.result.Causes,
4747
preflight.Cause{
48-
Message: fmt.Sprintf("Failed to unmarshal cluster variable %s: %s",
48+
Message: fmt.Sprintf(
49+
"Failed to unmarshal cluster variable %q: %s. Review the Cluster.", ///nolint:lll // Message is long.
4950
carenv1.ClusterConfigVariableName,
5051
err,
5152
),

pkg/webhook/preflight/generic/spec_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ func TestNewConfigurationCheck(t *testing.T) {
106106
InternalError: true,
107107
Causes: []preflight.Cause{
108108
{
109-
///nolint:lll // The message is long.
110-
Message: "Failed to unmarshal cluster variable clusterConfig: failed to unmarshal json: invalid character 'i' looking for beginning of object key string",
109+
Message: "Failed to unmarshal cluster variable \"clusterConfig\": failed to unmarshal json: invalid character 'i' looking for beginning of object key string. Review the Cluster.", ///nolint:lll // The message is long.
111110
Field: "cluster.spec.topology.variables[.name=clusterConfig].genericClusterConfigSpec",
112111
},
113112
},

pkg/webhook/preflight/nutanix/credentials.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func newCredentialsCheck(
5656
credentialsCheck.result.Allowed = false
5757
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
5858
preflight.Cause{
59-
Message: "Nutanix cluster configuration is not defined in the cluster spec",
59+
Message: "The Nutanix configuration is missing from the Cluster resource. Review the Cluster resource.", ///nolint:lll // Message is long.
6060
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix",
6161
},
6262
)
@@ -72,8 +72,11 @@ func newCredentialsCheck(
7272
credentialsCheck.result.Allowed = false
7373
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
7474
preflight.Cause{
75-
Message: fmt.Sprintf("Failed to parse Prism Central endpoint URL: %s", err),
76-
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.url", ///nolint:lll // Field is long.
75+
Message: fmt.Sprintf(
76+
"Failed to parse the Prism Central endpoint URL %q: %s. Check the URL format and retry.",
77+
prismCentralEndpointSpec.URL,
78+
err),
79+
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.url", ///nolint:lll // Field is long.
7780
},
7881
)
7982
return credentialsCheck
@@ -93,8 +96,10 @@ func newCredentialsCheck(
9396
credentialsCheck.result.InternalError = false
9497
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
9598
preflight.Cause{
96-
Message: fmt.Sprintf("Prism Central credentials Secret %q not found",
97-
prismCentralEndpointSpec.Credentials.SecretRef.Name),
99+
Message: fmt.Sprintf(
100+
"Prism Central credentials Secret %q not found. Create the Secret first, then create the Cluster.", ///nolint:lll // Message is long.
101+
prismCentralEndpointSpec.Credentials.SecretRef.Name,
102+
),
98103
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
99104
},
100105
)
@@ -105,7 +110,8 @@ func newCredentialsCheck(
105110
credentialsCheck.result.InternalError = true
106111
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
107112
preflight.Cause{
108-
Message: fmt.Sprintf("Failed to get Prism Central credentials Secret %q: %s",
113+
Message: fmt.Sprintf(
114+
"Failed to get Prism Central credentials Secret %q: %s. This is usually a temporary error. Please retry.",
109115
prismCentralEndpointSpec.Credentials.SecretRef.Name,
110116
err,
111117
),
@@ -120,7 +126,7 @@ func newCredentialsCheck(
120126
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
121127
preflight.Cause{
122128
Message: fmt.Sprintf(
123-
"Credentials Secret %q is empty",
129+
"Credentials Secret %q is empty. Review the Secret.", ///nolint:lll // Message is long.
124130
prismCentralEndpointSpec.Credentials.SecretRef.Name,
125131
),
126132
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
@@ -135,7 +141,7 @@ func newCredentialsCheck(
135141
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
136142
preflight.Cause{
137143
Message: fmt.Sprintf(
138-
"Credentials Secret %q does not contain key %q",
144+
"Credentials Secret %q does not contain key %q. Review the Secret.", ///nolint:lll // Message is long.
139145
prismCentralEndpointSpec.Credentials.SecretRef.Name,
140146
credentialsSecretDataKey,
141147
),
@@ -150,8 +156,11 @@ func newCredentialsCheck(
150156
credentialsCheck.result.Allowed = false
151157
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
152158
preflight.Cause{
153-
Message: fmt.Sprintf("Failed to parse Prism Central credentials: %s", err),
154-
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
159+
Message: fmt.Sprintf(
160+
"Failed to parse Prism Central credentials: %s. Review the Secret.", ///nolint:lll // Message is long.
161+
err,
162+
),
163+
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
155164
},
156165
)
157166
return credentialsCheck
@@ -173,8 +182,11 @@ func newCredentialsCheck(
173182
credentialsCheck.result.InternalError = true
174183
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
175184
preflight.Cause{
176-
Message: fmt.Sprintf("Failed to initialize Nutanix client: %s", err),
177-
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
185+
Message: fmt.Sprintf(
186+
"Failed to initialize the Nutanix Prism Central API client: %s.", ///nolint:lll // Message is long.",
187+
err,
188+
),
189+
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
178190
},
179191
)
180192
return credentialsCheck
@@ -192,8 +204,11 @@ func newCredentialsCheck(
192204
credentialsCheck.result.Allowed = false
193205
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
194206
preflight.Cause{
195-
Message: fmt.Sprintf("Failed to validate credentials using the v3 API client: %s", err),
196-
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
207+
Message: fmt.Sprintf(
208+
"Failed to validate credentials: %s. Please check the username and/or password.", ///nolint:lll // Message is long.
209+
err,
210+
),
211+
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint.credentials.secretRef", ///nolint:lll // Field is long.
197212
},
198213
)
199214
return credentialsCheck
@@ -203,8 +218,11 @@ func newCredentialsCheck(
203218
credentialsCheck.result.InternalError = true
204219
credentialsCheck.result.Causes = append(credentialsCheck.result.Causes,
205220
preflight.Cause{
206-
Message: fmt.Sprintf("Failed to validate credentials using the v3 API client: %s", err),
207-
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint",
221+
Message: fmt.Sprintf(
222+
"Failed to validate credentials: %s. This is usually a temporary error. Please retry.", ///nolint:lll // Message is long.
223+
err,
224+
),
225+
Field: "$.spec.topology.variables[[email protected]==\"clusterConfig\"].value.nutanix.prismCentralEndpoint",
208226
},
209227
)
210228
return credentialsCheck

pkg/webhook/preflight/nutanix/credentials_test.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ func TestNewCredentialsCheck_MissingNutanixField(t *testing.T) {
5757
assert.False(t, result.Allowed)
5858
assert.False(t, result.InternalError)
5959
assert.NotEmpty(t, result.Causes)
60-
assert.Contains(t, result.Causes[0].Message, "Nutanix cluster configuration is not defined")
60+
assert.Contains(
61+
t,
62+
result.Causes[0].Message,
63+
"The Nutanix configuration is missing from the Cluster resource. Review the Cluster resource.", ///nolint:lll // Message is long.
64+
)
6165
}
6266

6367
func TestNewCredentialsCheck_InvalidURL(t *testing.T) {
@@ -70,7 +74,11 @@ func TestNewCredentialsCheck_InvalidURL(t *testing.T) {
7074
result := check.Run(context.Background())
7175
assert.False(t, result.Allowed)
7276
assert.False(t, result.InternalError)
73-
assert.Contains(t, result.Causes[0].Message, "Failed to parse Prism Central endpoint URL")
77+
assert.Contains(
78+
t,
79+
result.Causes[0].Message,
80+
"Failed to parse the Prism Central endpoint URL \"not-a-url\": error parsing Prism Central URL: parse \"not-a-url\": invalid URI for request. Check the URL format and retry.", ///nolint:lll // Message is long.
81+
)
7482
}
7583

7684
func TestNewCredentialsCheck_SecretNotFound(t *testing.T) {
@@ -199,7 +207,11 @@ func TestNewCredentialsCheck_FailedToCreateClient(t *testing.T) {
199207
result := check.Run(context.Background())
200208
assert.False(t, result.Allowed)
201209
assert.True(t, result.InternalError)
202-
assert.Contains(t, result.Causes[0].Message, "Failed to initialize Nutanix client")
210+
assert.Contains(
211+
t,
212+
result.Causes[0].Message,
213+
"Failed to initialize the Nutanix Prism Central API client: assert.AnError general error for testing.", ///nolint:lll // Message is long.
214+
)
203215
}
204216

205217
func TestNewCredentialsCheck_FailedToGetCurrentLoggedInUser(t *testing.T) {
@@ -212,7 +224,7 @@ func TestNewCredentialsCheck_FailedToGetCurrentLoggedInUser(t *testing.T) {
212224
result := check.Run(context.Background())
213225
assert.False(t, result.Allowed)
214226
assert.True(t, result.InternalError)
215-
assert.Contains(t, result.Causes[0].Message, "Failed to validate credentials using the v3 API client: "+
227+
assert.Contains(t, result.Causes[0].Message, "Failed to validate credentials: "+
216228
assert.AnError.Error())
217229
}
218230

@@ -225,7 +237,7 @@ func TestNewCredentialsCheck_GetCurrentLoggedInUserInvalidCredentials(t *testing
225237
result := check.Run(context.Background())
226238
assert.False(t, result.Allowed)
227239
assert.False(t, result.InternalError)
228-
assert.Contains(t, result.Causes[0].Message, "Failed to validate credentials using the v3 API client: "+
240+
assert.Contains(t, result.Causes[0].Message, "Failed to validate credentials: "+
229241
"invalid Nutanix credentials")
230242
}
231243

pkg/webhook/preflight/nutanix/image.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,25 @@ func (c *imageCheck) Run(ctx context.Context) preflight.CheckResult {
4444
result.Allowed = false
4545
result.InternalError = true
4646
result.Causes = append(result.Causes, preflight.Cause{
47-
Message: fmt.Sprintf("Failed to get VM Image: %s", err),
48-
Field: c.field + ".image",
47+
Message: fmt.Sprintf(
48+
"Failed to get VM Image %q: %s. This is usually a temporary error. Please retry.",
49+
c.machineDetails.Image,
50+
err,
51+
),
52+
Field: c.field + ".image",
4953
})
5054
return result
5155
}
5256

5357
if len(images) != 1 {
5458
result.Allowed = false
5559
result.Causes = append(result.Causes, preflight.Cause{
56-
Message: fmt.Sprintf("Expected to find 1 VM Image, found %d", len(images)),
57-
Field: c.field + ".image",
60+
Message: fmt.Sprintf(
61+
"Found %d VM Images in Prism Central that match identifier %q. There must be exactly 1 VM Image that matches this identifier. Remove duplicate VM Images, use a different VM Image, or identify the VM Image by its UUID, then retry.", ///nolint:lll // Message is long.
62+
len(images),
63+
c.machineDetails.Image,
64+
),
65+
Field: c.field + ".image",
5866
})
5967
return result
6068
}

0 commit comments

Comments
 (0)