Skip to content

Commit c2061ae

Browse files
committed
blueprint_rules: remove references to a validation
The word "validation" is too strict and we don't validate agains a schema here, so we are updating all wordings here.
1 parent 873dcdd commit c2061ae

File tree

3 files changed

+361
-114
lines changed

3 files changed

+361
-114
lines changed

internal/v1/blueprint_rules.go

Lines changed: 66 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v1
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -22,11 +23,8 @@ type validationError struct {
2223
detail string
2324
}
2425

25-
func (e ValidationError) Error() string {
26-
if len(e.HTTPErrorList.Errors) > 0 {
27-
return e.HTTPErrorList.Errors[0].Detail
28-
}
29-
return "validation error"
26+
func (ve validationError) Error() string {
27+
return ve.detail
3028
}
3129

3230
func newValidationError(title, detail string) error {
@@ -50,115 +48,73 @@ func parseOctalMode(modeStr *string) *os.FileMode {
5048
}
5149

5250
// parseFileUser extracts user from File union type
53-
func parseFileUser(fileUser *File_User) interface{} {
51+
func parseFileUser(fileUser *File_User) any {
5452
if fileUser == nil {
5553
return nil
5654
}
5755

5856
if userStr, err := fileUser.AsFileUser0(); err == nil {
5957
return userStr
60-
} else if userInt, err := fileUser.AsFileUser1(); err == nil {
58+
}
59+
if userInt, err := fileUser.AsFileUser1(); err == nil {
6160
return userInt
6261
}
6362
return nil
6463
}
6564

6665
// parseFileGroup extracts group from File union type
67-
func parseFileGroup(fileGroup *File_Group) interface{} {
66+
func parseFileGroup(fileGroup *File_Group) any {
6867
if fileGroup == nil {
6968
return nil
7069
}
7170

7271
if groupStr, err := fileGroup.AsFileGroup0(); err == nil {
7372
return groupStr
74-
} else if groupInt, err := fileGroup.AsFileGroup1(); err == nil {
73+
}
74+
if groupInt, err := fileGroup.AsFileGroup1(); err == nil {
7575
return groupInt
7676
}
7777
return nil
7878
}
7979

8080
// parseDirectoryUser extracts user from Directory union type
81-
func parseDirectoryUser(dirUser *Directory_User) interface{} {
81+
func parseDirectoryUser(dirUser *Directory_User) any {
8282
if dirUser == nil {
8383
return nil
8484
}
8585

8686
if userStr, err := dirUser.AsDirectoryUser0(); err == nil {
8787
return userStr
88-
} else if userInt, err := dirUser.AsDirectoryUser1(); err == nil {
88+
}
89+
if userInt, err := dirUser.AsDirectoryUser1(); err == nil {
8990
return userInt
9091
}
9192
return nil
9293
}
9394

9495
// parseDirectoryGroup extracts group from Directory union type
95-
func parseDirectoryGroup(dirGroup *Directory_Group) interface{} {
96+
func parseDirectoryGroup(dirGroup *Directory_Group) any {
9697
if dirGroup == nil {
9798
return nil
9899
}
99100

100101
if groupStr, err := dirGroup.AsDirectoryGroup0(); err == nil {
101102
return groupStr
102-
} else if groupInt, err := dirGroup.AsDirectoryGroup1(); err == nil {
103-
return groupInt
104-
}
105-
return nil
106-
}
107-
108-
// BlueprintValidator interface for the Chain of Responsibility pattern
109-
type BlueprintValidator interface {
110-
Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error
111-
}
112-
113-
// ValidationChain manages a chain of blueprint validators
114-
type ValidationChain struct {
115-
validators []BlueprintValidator
116-
}
117-
118-
// Validate executes all validators in the chain and collects all errors
119-
func (vc *ValidationChain) Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
120-
var allErrors []HTTPError
121-
122-
for _, validator := range vc.validators {
123-
if err := validator.Validate(ctx, request, existingUsers); err != nil {
124-
if validationErr, ok := err.(ValidationError); ok {
125-
// Collect all errors from this validator
126-
allErrors = append(allErrors, validationErr.HTTPErrorList.Errors...)
127-
} else {
128-
// Handle unexpected error types
129-
allErrors = append(allErrors, HTTPError{
130-
Title: "Validation Error",
131-
Detail: err.Error(),
132-
})
133-
}
134-
}
135103
}
136-
137-
if len(allErrors) > 0 {
138-
return ValidationError{
139-
HTTPErrorList: HTTPErrorList{
140-
Errors: allErrors,
141-
},
142-
}
104+
if groupInt, err := dirGroup.AsDirectoryGroup1(); err == nil {
105+
return groupInt
143106
}
144-
145107
return nil
146108
}
147109

148-
// NameValidator validates blueprint names
149-
type NameValidator struct{}
150-
151-
func (nv *NameValidator) Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
110+
func checkNameRule(request *CreateBlueprintRequest) error {
152111
if !blueprintNameRegex.MatchString(request.Name) {
153-
return newValidationError("Invalid blueprint name", blueprintInvalidNameDetail)
112+
return newValidationError("blueprint name rule violation", blueprintInvalidNameDetail)
154113
}
155114
return nil
156115
}
157116

158-
// UserValidator validates blueprint users
159-
type UserValidator struct{}
160-
161-
func (uv *UserValidator) Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
117+
func checkUserRule(request *CreateBlueprintRequest, existingUsers []User) error {
162118
users := request.Customizations.Users
163119
if users == nil {
164120
return nil
@@ -173,23 +129,20 @@ func (uv *UserValidator) Validate(ctx echo.Context, request *CreateBlueprintRequ
173129
}
174130

175131
if err != nil {
176-
return newValidationError("Invalid user", err.Error())
132+
return newValidationError("user rule violation", err.Error())
177133
}
178134
}
179135
return nil
180136
}
181137

182-
// FileValidator validates blueprint file customizations
183-
type FileValidator struct{}
184-
185-
func (fv *FileValidator) Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
138+
func checkFileRule(request *CreateBlueprintRequest) error {
186139
files := request.Customizations.Files
187140
if files == nil {
188141
return nil
189142
}
190143

144+
var errs []error
191145
for _, file := range *files {
192-
// Convert API types to fsnode types for validation
193146
mode := parseOctalMode(file.Mode)
194147
user := parseFileUser(file.User)
195148
group := parseFileGroup(file.Group)
@@ -199,26 +152,26 @@ func (fv *FileValidator) Validate(ctx echo.Context, request *CreateBlueprintRequ
199152
data = []byte(*file.Data)
200153
}
201154

202-
// Use fsnode.NewFile for validation - this handles all path, mode, user, group validation
203155
_, err := fsnode.NewFile(file.Path, mode, user, group, data)
204156
if err != nil {
205-
return newValidationError("Invalid file customization", fmt.Sprintf("file %q: %s", file.Path, err.Error()))
157+
errs = append(errs, newValidationError(
158+
"file rule violation",
159+
fmt.Sprintf("file %q: %s", file.Path, err.Error()),
160+
))
206161
}
207162
}
208-
return nil
209-
}
210163

211-
// DirectoryValidator validates blueprint directory customizations
212-
type DirectoryValidator struct{}
164+
return errors.Join(errs...)
165+
}
213166

214-
func (dv *DirectoryValidator) Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
167+
func checkDirectoryRule(request *CreateBlueprintRequest) error {
215168
directories := request.Customizations.Directories
216169
if directories == nil {
217170
return nil
218171
}
219172

173+
var errs []error
220174
for _, dir := range *directories {
221-
// Convert API types to fsnode types for validation
222175
mode := parseOctalMode(dir.Mode)
223176
user := parseDirectoryUser(dir.User)
224177
group := parseDirectoryGroup(dir.Group)
@@ -228,60 +181,61 @@ func (dv *DirectoryValidator) Validate(ctx echo.Context, request *CreateBlueprin
228181
ensureParents = *dir.EnsureParents
229182
}
230183

231-
// Use fsnode.NewDirectory for validation - this handles all path, mode, user, group validation
232184
_, err := fsnode.NewDirectory(dir.Path, mode, user, group, ensureParents)
233185
if err != nil {
234-
return newValidationError("Invalid directory customization", fmt.Sprintf("directory %q: %s", dir.Path, err.Error()))
186+
errs = append(errs, newValidationError(
187+
"directory rule violation",
188+
fmt.Sprintf("directory %q: %s", dir.Path, err.Error()),
189+
))
235190
}
236191
}
237-
return nil
238-
}
239192

240-
// FilesystemValidator validates blueprint filesystem customizations
241-
type FilesystemValidator struct{}
193+
return errors.Join(errs...)
194+
}
242195

243-
func (fsv *FilesystemValidator) Validate(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
196+
func checkFilesystemRule(request *CreateBlueprintRequest) error {
244197
filesystem := request.Customizations.Filesystem
245198
if filesystem == nil {
246199
return nil
247200
}
248201

202+
var errs []error
249203
for _, fs := range *filesystem {
250-
// Use the same path validation logic as fsnode (following library patterns)
251204
if fs.Mountpoint == "" {
252-
return newValidationError("Invalid filesystem customization", "mountpoint must not be empty")
253-
}
254-
if fs.Mountpoint[0] != '/' {
255-
return newValidationError("Invalid filesystem customization", fmt.Sprintf("mountpoint %q must be absolute", fs.Mountpoint))
256-
}
257-
if fs.Mountpoint != filepath.Clean(fs.Mountpoint) {
258-
return newValidationError("Invalid filesystem customization", fmt.Sprintf("mountpoint %q must be canonical", fs.Mountpoint))
205+
errs = append(errs, newValidationError(
206+
"filesystem rule violation",
207+
"mountpoint must not be empty",
208+
))
209+
} else if fs.Mountpoint[0] != '/' {
210+
errs = append(errs, newValidationError(
211+
"filesystem rule violation",
212+
fmt.Sprintf("mountpoint %q must be absolute", fs.Mountpoint),
213+
))
214+
} else if fs.Mountpoint != filepath.Clean(fs.Mountpoint) {
215+
errs = append(errs, newValidationError(
216+
"filesystem rule violation",
217+
fmt.Sprintf("mountpoint %q must be canonical", fs.Mountpoint),
218+
))
259219
}
260220

261-
// Validate minimum size is reasonable
262-
if fs.MinSize > 0 && fs.MinSize < 1024*1024 { // 1MB minimum
263-
return newValidationError("Invalid filesystem customization", fmt.Sprintf("mountpoint %q minimum size must be at least 1MB", fs.Mountpoint))
221+
if fs.MinSize > 0 && fs.MinSize < 1024*1024 {
222+
errs = append(errs, newValidationError(
223+
"filesystem rule violation",
224+
fmt.Sprintf("mountpoint %q minimum size must be at least 1MB", fs.Mountpoint),
225+
))
264226
}
265227
}
266-
return nil
267-
}
268228

269-
// NewBlueprintValidationChain creates a new validation chain with all validators
270-
func NewBlueprintValidationChain() *ValidationChain {
271-
return &ValidationChain{
272-
validators: []BlueprintValidator{
273-
&NameValidator{},
274-
&UserValidator{},
275-
&FileValidator{},
276-
&DirectoryValidator{},
277-
&FilesystemValidator{},
278-
},
279-
}
229+
return errors.Join(errs...)
280230
}
281231

282-
// ValidateBlueprintRequest performs common validation for blueprint requests
283-
// using the Chain of Responsibility pattern
284-
func ValidateBlueprintRequest(ctx echo.Context, blueprintRequest *CreateBlueprintRequest, existingUsers []User) error {
285-
chain := NewBlueprintValidationChain()
286-
return chain.Validate(ctx, blueprintRequest, existingUsers)
232+
// CheckBlueprintRules performs common rule checking for blueprint requests
233+
func CheckBlueprintRules(ctx echo.Context, request *CreateBlueprintRequest, existingUsers []User) error {
234+
return errors.Join(
235+
checkNameRule(request),
236+
checkUserRule(request, existingUsers),
237+
checkFileRule(request),
238+
checkDirectoryRule(request),
239+
checkFilesystemRule(request),
240+
)
287241
}

0 commit comments

Comments
 (0)