Skip to content

Commit f4498e8

Browse files
authored
fix(alertgroups): add expression validator to detect new lines on expressions (#807)
1 parent 9cd402e commit f4498e8

File tree

5 files changed

+111
-1
lines changed

5 files changed

+111
-1
lines changed

stackit/internal/services/observability/alertgroup/resource.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ func (a *alertGroupResource) Schema(_ context.Context, _ resource.SchemaRequest,
207207
Required: true,
208208
Validators: []validator.String{
209209
stringvalidator.LengthBetween(1, 600),
210+
// The API currently accepts expressions with trailing newlines but does not return them,
211+
// leading to inconsistent Terraform results. This issue has been reported to the Obs team.
212+
// Until it is resolved, we proactively notify users if their input contains a trailing newline.
213+
validate.ValidNoTrailingNewline(),
210214
},
211215
},
212216
"for": schema.StringAttribute{

stackit/internal/services/observability/log-alertgroup/resource.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ func (l *logAlertGroupResource) Schema(_ context.Context, _ resource.SchemaReque
207207
Required: true,
208208
Validators: []validator.String{
209209
stringvalidator.LengthBetween(1, 600),
210+
// The API currently accepts expressions with trailing newlines but does not return them,
211+
// leading to inconsistent Terraform results. This issue has been reported to the Obs team.
212+
// Until it is resolved, we proactively notify users if their input contains a trailing newline.
213+
validate.ValidNoTrailingNewline(),
210214
},
211215
},
212216
"for": schema.StringAttribute{

stackit/internal/services/ske/cluster/resource.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ func toNodepoolsPayload(ctx context.Context, m *Model, availableMachineVersions
997997
if v.IsNull() || v.IsUnknown() {
998998
continue
999999
}
1000-
s, err := conversion.ToString(context.TODO(), v)
1000+
s, err := conversion.ToString(ctx, v)
10011001
if err != nil {
10021002
continue
10031003
}

stackit/internal/validate/validate.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,34 @@ func ValidDurationString() *Validator {
313313
},
314314
}
315315
}
316+
317+
// ValidNoTrailingNewline returns a Validator that checks if the input string has no trailing newline
318+
// character ("\n" or "\r\n"). If a trailing newline is present, a diagnostic error will be appended.
319+
func ValidNoTrailingNewline() *Validator {
320+
description := `The value must not have a trailing newline character ("\n" or "\r\n"). You can remove a trailing newline by using Terraform's built-in chomp() function.`
321+
322+
return &Validator{
323+
description: description,
324+
validate: func(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
325+
val := req.ConfigValue.ValueString()
326+
if val == "" {
327+
return
328+
}
329+
if len(val) >= 2 && val[len(val)-2:] == "\r\n" {
330+
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
331+
req.Path,
332+
description,
333+
val,
334+
))
335+
return
336+
}
337+
if val[len(val)-1] == '\n' {
338+
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
339+
req.Path,
340+
description,
341+
val,
342+
))
343+
}
344+
},
345+
}
346+
}

stackit/internal/validate/validate_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,3 +845,74 @@ func TestValidTtlDuration(t *testing.T) {
845845
})
846846
}
847847
}
848+
849+
func TestValidNoTrailingNewline(t *testing.T) {
850+
tests := []struct {
851+
description string
852+
input string
853+
isValid bool
854+
}{
855+
{
856+
"string with no trailing newline",
857+
"abc",
858+
true,
859+
},
860+
{
861+
"string with trailing \\n",
862+
"abc\n",
863+
false,
864+
},
865+
{
866+
"string with trailing \\r\\n",
867+
"abc\r\n",
868+
false,
869+
},
870+
{
871+
"string with internal newlines but not trailing",
872+
"abc\ndef\nghi",
873+
true,
874+
},
875+
{
876+
"empty string",
877+
"",
878+
true,
879+
},
880+
{
881+
"string that is just \\n",
882+
"\n",
883+
false,
884+
},
885+
{
886+
"string that is just \\r\\n",
887+
"\r\n",
888+
false,
889+
},
890+
{
891+
"string with multiple newlines, trailing",
892+
"abc\n\n",
893+
false,
894+
},
895+
{
896+
"string with newlines but ends with character",
897+
"abc\ndef\n",
898+
false,
899+
},
900+
}
901+
902+
for _, tt := range tests {
903+
t.Run(tt.description, func(t *testing.T) {
904+
r := validator.StringResponse{}
905+
va := ValidNoTrailingNewline()
906+
va.ValidateString(context.Background(), validator.StringRequest{
907+
ConfigValue: types.StringValue(tt.input),
908+
}, &r)
909+
910+
if !tt.isValid && !r.Diagnostics.HasError() {
911+
t.Fatalf("Expected validation to fail for input: %q", tt.input)
912+
}
913+
if tt.isValid && r.Diagnostics.HasError() {
914+
t.Fatalf("Expected validation to succeed for input: %q, but got errors: %v", tt.input, r.Diagnostics.Errors())
915+
}
916+
})
917+
}
918+
}

0 commit comments

Comments
 (0)