From 6ee9188dd1ce9aab13d9b9598b7bf57ba4fcfbc2 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 5 Nov 2024 21:38:05 +0000 Subject: [PATCH 1/3] fix: use `codersdk` functions for validating name attributes --- .golangci.yml | 6 +-- internal/codersdkvalidator/display_name.go | 47 +++++++++++++++++++ internal/codersdkvalidator/name.go | 47 +++++++++++++++++++ .../template_version_name.go | 47 +++++++++++++++++++ internal/codersdkvalidator/user_real_name.go | 47 +++++++++++++++++++ internal/provider/group_resource.go | 8 ++-- internal/provider/template_resource.go | 10 ++-- internal/provider/user_resource.go | 6 +-- internal/provider/util.go | 7 --- 9 files changed, 201 insertions(+), 24 deletions(-) create mode 100644 internal/codersdkvalidator/display_name.go create mode 100644 internal/codersdkvalidator/name.go create mode 100644 internal/codersdkvalidator/template_version_name.go create mode 100644 internal/codersdkvalidator/user_real_name.go diff --git a/.golangci.yml b/.golangci.yml index 223cf95..679a35a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ -# Visit https://golangci-lint.run/ for usage documentation -# and information on other useful linters +# Visit https://golangci-lint.run/ for usage documentation and information on +# other useful linters issues: max-per-linter: 0 max-same-issues: 0 @@ -24,4 +24,4 @@ linters: - unconvert - unparam - unused - - vet \ No newline at end of file + - vet diff --git a/internal/codersdkvalidator/display_name.go b/internal/codersdkvalidator/display_name.go new file mode 100644 index 0000000..2c08c78 --- /dev/null +++ b/internal/codersdkvalidator/display_name.go @@ -0,0 +1,47 @@ +package codersdkvalidator + +import ( + "context" + + "github.com/coder/coder/v2/codersdk" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type displayNameValidator struct { + err error +} + +func DisplayName() validator.String { + return displayNameValidator{} +} + +var _ validator.String = displayNameValidator{} + +func (v displayNameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + name := req.ConfigValue.ValueString() + if v.err = codersdk.DisplayNameValid(name); v.err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + name, + )) + } +} + +var _ validator.Describer = displayNameValidator{} + +func (v displayNameValidator) Description(_ context.Context) string { + if v.err != nil { + return v.err.Error() + } + return "value must be a valid display name" +} + +func (v displayNameValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} diff --git a/internal/codersdkvalidator/name.go b/internal/codersdkvalidator/name.go new file mode 100644 index 0000000..2c82fc0 --- /dev/null +++ b/internal/codersdkvalidator/name.go @@ -0,0 +1,47 @@ +package codersdkvalidator + +import ( + "context" + + "github.com/coder/coder/v2/codersdk" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type nameValidator struct { + err error +} + +func Name() validator.String { + return nameValidator{} +} + +var _ validator.String = nameValidator{} + +func (v nameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + name := req.ConfigValue.ValueString() + if v.err = codersdk.NameValid(name); v.err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + name, + )) + } +} + +var _ validator.Describer = nameValidator{} + +func (v nameValidator) Description(_ context.Context) string { + if v.err != nil { + return v.err.Error() + } + return "value must be a valid name" +} + +func (v nameValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} diff --git a/internal/codersdkvalidator/template_version_name.go b/internal/codersdkvalidator/template_version_name.go new file mode 100644 index 0000000..729182f --- /dev/null +++ b/internal/codersdkvalidator/template_version_name.go @@ -0,0 +1,47 @@ +package codersdkvalidator + +import ( + "context" + + "github.com/coder/coder/v2/codersdk" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type templateVersionNameValidator struct { + err error +} + +func TemplateVersionName() validator.String { + return templateVersionNameValidator{} +} + +var _ validator.String = templateVersionNameValidator{} + +func (v templateVersionNameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + name := req.ConfigValue.ValueString() + if v.err = codersdk.TemplateVersionNameValid(name); v.err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + name, + )) + } +} + +var _ validator.Describer = templateVersionNameValidator{} + +func (v templateVersionNameValidator) Description(_ context.Context) string { + if v.err != nil { + return v.err.Error() + } + return "value must be a valid template version name" +} + +func (v templateVersionNameValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} diff --git a/internal/codersdkvalidator/user_real_name.go b/internal/codersdkvalidator/user_real_name.go new file mode 100644 index 0000000..e33f5a0 --- /dev/null +++ b/internal/codersdkvalidator/user_real_name.go @@ -0,0 +1,47 @@ +package codersdkvalidator + +import ( + "context" + + "github.com/coder/coder/v2/codersdk" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type userRealNameValidator struct { + err error +} + +func UserRealName() validator.String { + return userRealNameValidator{} +} + +var _ validator.String = userRealNameValidator{} + +func (v userRealNameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + name := req.ConfigValue.ValueString() + if v.err = codersdk.UserRealNameValid(name); v.err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + name, + )) + } +} + +var _ validator.Describer = userRealNameValidator{} + +func (v userRealNameValidator) Description(_ context.Context) string { + if v.err != nil { + return v.err.Error() + } + return "value must be a valid name for a user" +} + +func (v userRealNameValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} diff --git a/internal/provider/group_resource.go b/internal/provider/group_resource.go index a7dcd6a..cbafac6 100644 --- a/internal/provider/group_resource.go +++ b/internal/provider/group_resource.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/coder/coder/v2/codersdk" + "github.com/coder/terraform-provider-coderd/internal/codersdkvalidator" "github.com/google/uuid" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" @@ -77,8 +77,7 @@ func (r *GroupResource) Schema(ctx context.Context, req resource.SchemaRequest, MarkdownDescription: "The unique name of the group.", Required: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 36), - stringvalidator.RegexMatches(nameValidRegex, "Group names must be alpahnumeric with hyphens."), + codersdkvalidator.Name(), }, }, "display_name": schema.StringAttribute{ @@ -86,8 +85,7 @@ func (r *GroupResource) Schema(ctx context.Context, req resource.SchemaRequest, Computed: true, Optional: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 64), - stringvalidator.RegexMatches(displayNameRegex, "Group display names must be alphanumeric with spaces"), + codersdkvalidator.DisplayName(), }, Default: stringdefault.StaticString(""), }, diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index f1d7adc..a14d8d6 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -12,6 +12,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/provisionersdk" + "github.com/coder/terraform-provider-coderd/internal/codersdkvalidator" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" @@ -257,8 +258,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques MarkdownDescription: "The name of the template.", Required: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 32), - stringvalidator.RegexMatches(nameValidRegex, "Template names must be alphanumeric with hyphens."), + codersdkvalidator.Name(), }, }, "display_name": schema.StringAttribute{ @@ -266,8 +266,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques Optional: true, Computed: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 64), - stringvalidator.RegexMatches(displayNameRegex, "Template display names must be alphanumeric with spaces."), + codersdkvalidator.DisplayName(), }, }, "description": schema.StringAttribute{ @@ -417,8 +416,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques Optional: true, Computed: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 64), - stringvalidator.RegexMatches(templateVersionNameRegex, "Template version names must be alphanumeric with underscores and dots."), + codersdkvalidator.TemplateVersionName(), }, }, "message": schema.StringAttribute{ diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index a560df5..3fa570e 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/coder/coder/v2/codersdk" + "github.com/coder/terraform-provider-coderd/internal/codersdkvalidator" ) // Ensure provider defined types fully satisfy framework interfaces. @@ -71,8 +72,7 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r MarkdownDescription: "Username of the user.", Required: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 32), - stringvalidator.RegexMatches(nameValidRegex, "Username must be alphanumeric with hyphens."), + codersdkvalidator.Name(), }, }, "name": schema.StringAttribute{ @@ -80,7 +80,7 @@ func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, r Computed: true, Optional: true, Validators: []validator.String{ - stringvalidator.LengthBetween(1, 128), + codersdkvalidator.UserRealName(), }, }, "email": schema.StringAttribute{ diff --git a/internal/provider/util.go b/internal/provider/util.go index 12be3f3..d56fadb 100644 --- a/internal/provider/util.go +++ b/internal/provider/util.go @@ -8,18 +8,11 @@ import ( "net/http" "os" "path/filepath" - "regexp" "github.com/coder/coder/v2/codersdk" "github.com/google/uuid" ) -var ( - nameValidRegex = regexp.MustCompile("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$") - templateVersionNameRegex = regexp.MustCompile(`^[a-zA-Z0-9]+(?:[_.-]{1}[a-zA-Z0-9]+)*$`) - displayNameRegex = regexp.MustCompile(`^[^\s](.*[^\s])?$`) -) - func PtrTo[T any](v T) *T { return &v } From 3a61f81bff603cf3cfc389b569d4df5b33097135 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 5 Nov 2024 21:49:04 +0000 Subject: [PATCH 2/3] oh nice --- internal/codersdkvalidator/display_name.go | 39 +------------- internal/codersdkvalidator/name.go | 39 +------------- .../template_version_name.go | 39 +------------- internal/codersdkvalidator/user_real_name.go | 39 +------------- .../codersdkvalidator/validator_from_func.go | 51 +++++++++++++++++++ 5 files changed, 55 insertions(+), 152 deletions(-) create mode 100644 internal/codersdkvalidator/validator_from_func.go diff --git a/internal/codersdkvalidator/display_name.go b/internal/codersdkvalidator/display_name.go index 2c08c78..1000c32 100644 --- a/internal/codersdkvalidator/display_name.go +++ b/internal/codersdkvalidator/display_name.go @@ -1,47 +1,10 @@ package codersdkvalidator import ( - "context" - "github.com/coder/coder/v2/codersdk" - "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -type displayNameValidator struct { - err error -} - func DisplayName() validator.String { - return displayNameValidator{} -} - -var _ validator.String = displayNameValidator{} - -func (v displayNameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - name := req.ConfigValue.ValueString() - if v.err = codersdk.DisplayNameValid(name); v.err != nil { - resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - req.Path, - v.Description(ctx), - name, - )) - } -} - -var _ validator.Describer = displayNameValidator{} - -func (v displayNameValidator) Description(_ context.Context) string { - if v.err != nil { - return v.err.Error() - } - return "value must be a valid display name" -} - -func (v displayNameValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) + return validatorFromFunc(codersdk.DisplayNameValid, "value must be a valid display name") } diff --git a/internal/codersdkvalidator/name.go b/internal/codersdkvalidator/name.go index 2c82fc0..14adb25 100644 --- a/internal/codersdkvalidator/name.go +++ b/internal/codersdkvalidator/name.go @@ -1,47 +1,10 @@ package codersdkvalidator import ( - "context" - "github.com/coder/coder/v2/codersdk" - "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -type nameValidator struct { - err error -} - func Name() validator.String { - return nameValidator{} -} - -var _ validator.String = nameValidator{} - -func (v nameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - name := req.ConfigValue.ValueString() - if v.err = codersdk.NameValid(name); v.err != nil { - resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - req.Path, - v.Description(ctx), - name, - )) - } -} - -var _ validator.Describer = nameValidator{} - -func (v nameValidator) Description(_ context.Context) string { - if v.err != nil { - return v.err.Error() - } - return "value must be a valid name" -} - -func (v nameValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) + return validatorFromFunc(codersdk.NameValid, "value must be a valid name") } diff --git a/internal/codersdkvalidator/template_version_name.go b/internal/codersdkvalidator/template_version_name.go index 729182f..32c69d6 100644 --- a/internal/codersdkvalidator/template_version_name.go +++ b/internal/codersdkvalidator/template_version_name.go @@ -1,47 +1,10 @@ package codersdkvalidator import ( - "context" - "github.com/coder/coder/v2/codersdk" - "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -type templateVersionNameValidator struct { - err error -} - func TemplateVersionName() validator.String { - return templateVersionNameValidator{} -} - -var _ validator.String = templateVersionNameValidator{} - -func (v templateVersionNameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - name := req.ConfigValue.ValueString() - if v.err = codersdk.TemplateVersionNameValid(name); v.err != nil { - resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - req.Path, - v.Description(ctx), - name, - )) - } -} - -var _ validator.Describer = templateVersionNameValidator{} - -func (v templateVersionNameValidator) Description(_ context.Context) string { - if v.err != nil { - return v.err.Error() - } - return "value must be a valid template version name" -} - -func (v templateVersionNameValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) + return validatorFromFunc(codersdk.TemplateVersionNameValid, "value must be a valid template version name") } diff --git a/internal/codersdkvalidator/user_real_name.go b/internal/codersdkvalidator/user_real_name.go index e33f5a0..5bf9686 100644 --- a/internal/codersdkvalidator/user_real_name.go +++ b/internal/codersdkvalidator/user_real_name.go @@ -1,47 +1,10 @@ package codersdkvalidator import ( - "context" - "github.com/coder/coder/v2/codersdk" - "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -type userRealNameValidator struct { - err error -} - func UserRealName() validator.String { - return userRealNameValidator{} -} - -var _ validator.String = userRealNameValidator{} - -func (v userRealNameValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { - if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { - return - } - - name := req.ConfigValue.ValueString() - if v.err = codersdk.UserRealNameValid(name); v.err != nil { - resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - req.Path, - v.Description(ctx), - name, - )) - } -} - -var _ validator.Describer = userRealNameValidator{} - -func (v userRealNameValidator) Description(_ context.Context) string { - if v.err != nil { - return v.err.Error() - } - return "value must be a valid name for a user" -} - -func (v userRealNameValidator) MarkdownDescription(ctx context.Context) string { - return v.Description(ctx) + return validatorFromFunc(codersdk.UserRealNameValid, "value must be a valid name for a user") } diff --git a/internal/codersdkvalidator/validator_from_func.go b/internal/codersdkvalidator/validator_from_func.go new file mode 100644 index 0000000..e7e9993 --- /dev/null +++ b/internal/codersdkvalidator/validator_from_func.go @@ -0,0 +1,51 @@ +package codersdkvalidator + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type functionValidator struct { + check func(string) error + defaultMessage string + err error +} + +func validatorFromFunc(check func(string) error, defaultMessage string) functionValidator { + return functionValidator{ + check: check, + defaultMessage: defaultMessage, + } +} + +var _ validator.String = functionValidator{} + +func (v functionValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + name := req.ConfigValue.ValueString() + if v.err = v.check(name); v.err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + name, + )) + } +} + +var _ validator.Describer = functionValidator{} + +func (v functionValidator) Description(_ context.Context) string { + if v.err != nil { + return v.err.Error() + } + return "value must be a valid name" +} + +func (v functionValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} From 0d27b318d829cc5793d8de88477ace93a93dfee6 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 7 Nov 2024 18:10:08 +0000 Subject: [PATCH 3/3] review feedback --- internal/codersdkvalidator/group_name.go | 10 ++++++++++ internal/codersdkvalidator/validator_from_func.go | 2 +- internal/provider/group_resource.go | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 internal/codersdkvalidator/group_name.go diff --git a/internal/codersdkvalidator/group_name.go b/internal/codersdkvalidator/group_name.go new file mode 100644 index 0000000..20313db --- /dev/null +++ b/internal/codersdkvalidator/group_name.go @@ -0,0 +1,10 @@ +package codersdkvalidator + +import ( + "github.com/coder/coder/v2/codersdk" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func GroupName() validator.String { + return validatorFromFunc(codersdk.GroupNameValid, "value must be a valid group name") +} diff --git a/internal/codersdkvalidator/validator_from_func.go b/internal/codersdkvalidator/validator_from_func.go index e7e9993..9d5e631 100644 --- a/internal/codersdkvalidator/validator_from_func.go +++ b/internal/codersdkvalidator/validator_from_func.go @@ -43,7 +43,7 @@ func (v functionValidator) Description(_ context.Context) string { if v.err != nil { return v.err.Error() } - return "value must be a valid name" + return v.defaultMessage } func (v functionValidator) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/group_resource.go b/internal/provider/group_resource.go index cbafac6..fa370a0 100644 --- a/internal/provider/group_resource.go +++ b/internal/provider/group_resource.go @@ -77,7 +77,7 @@ func (r *GroupResource) Schema(ctx context.Context, req resource.SchemaRequest, MarkdownDescription: "The unique name of the group.", Required: true, Validators: []validator.String{ - codersdkvalidator.Name(), + codersdkvalidator.GroupName(), }, }, "display_name": schema.StringAttribute{