-
Notifications
You must be signed in to change notification settings - Fork 505
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding a better error message for download mode #1492
base: main
Are you sure you want to change the base?
Changes from 16 commits
0224e53
fa2631c
834eba4
f45a03b
4715103
e928a0f
32e7c28
a846557
25b5860
36b6d62
2c6b90b
5b85142
65662f5
e962a4d
1b29dcf
d6a7cbd
de71cb7
cc70e12
a5eda16
198d574
ee8f4d1
2cc9f84
9695c36
726b77c
4320101
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package config | ||
|
||
// Validator can validate a config struct. If you implement this, | ||
// validate all of the configuration in your struct. It will | ||
// automatically be called when Athens starts | ||
type Validator interface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely correct me if I'm wrong but: Do we need a validator interface? I don't see any code actually dealing with the "interface" aspect of things. All the Validate functions are being called explicitly. AKA nothing is being abstracted here :) I think we can make a Also the documentation here I think might be a little misleading:
To the same point above, we need to explicitly call "Validate" and so there's nothing automatic happening right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, I got ahead of myself here. My intention was to have the I'll leave |
||
Validate() error | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,15 +18,33 @@ type Mode string | |
|
||
// DownloadMode constants. For more information see config.dev.toml | ||
const ( | ||
Sync Mode = "sync" | ||
Async Mode = "async" | ||
Redirect Mode = "redirect" | ||
AsyncRedirect Mode = "async_redirect" | ||
None Mode = "none" | ||
downloadModeErr = "download mode is not set" | ||
invalidModeErr = "unrecognized download mode: %s" | ||
Sync Mode = "sync" | ||
Async Mode = "async" | ||
Redirect Mode = "redirect" | ||
AsyncRedirect Mode = "async_redirect" | ||
None Mode = "none" | ||
// This is the URL that logs will show when the DownloadMode | ||
// config value is invalid | ||
downloadModeURL = "https://docs.gomods.io/configuration/download/" | ||
) | ||
|
||
// Validate ensures that m is a valid mode. If this function returns nil, you are | ||
// guaranteed that m is valid | ||
func (m Mode) Validate() error { | ||
const op errors.Op = "Mode.Validate" | ||
switch m { | ||
case Sync, Async, Redirect, AsyncRedirect, None: | ||
return nil | ||
default: | ||
return errors.Config( | ||
op, | ||
"mode", | ||
fmt.Sprintf("%s isn't a valid value.", m), | ||
"https://docs.gomods.io/configuration/download/", | ||
) | ||
} | ||
} | ||
|
||
// DownloadFile represents a custom HCL format of | ||
// how to handle module@version requests that are | ||
// not found in storage. | ||
|
@@ -44,6 +62,20 @@ type DownloadPath struct { | |
DownloadURL string `hcl:"downloadURL,optional"` | ||
} | ||
|
||
// Validate implements config.Validator | ||
func (d DownloadPath) Validate() error { | ||
const op errors.Op = "DownloadPath.Validate" | ||
if d.DownloadURL == "" && (d.Mode == Redirect || d.Mode == AsyncRedirect) { | ||
return errors.Config( | ||
op, | ||
fmt.Sprintf("DownloadURL (inside %s in the download file)", d.Pattern), | ||
"You must set a value when the download mode is 'redirect' or 'async_redirect'", | ||
"https://docs.gomods.io/configuration/download/", | ||
) | ||
} | ||
return nil | ||
} | ||
|
||
// NewFile takes a mode and returns a DownloadFile. | ||
// Mode can be one of the constants declared above | ||
// or a custom HCL file. To pass a custom HCL file, | ||
|
@@ -53,8 +85,8 @@ type DownloadPath struct { | |
func NewFile(m Mode, downloadURL string) (*DownloadFile, error) { | ||
const op errors.Op = "downloadMode.NewFile" | ||
|
||
if m == "" { | ||
return nil, errors.E(op, downloadModeErr) | ||
if err := m.Validate(); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this error is now less readable, because before it used to say I think in the Sprintf of the validate function we should pass a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about if we pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds great |
||
return nil, err | ||
} | ||
|
||
if strings.HasPrefix(string(m), "file:") { | ||
|
@@ -72,12 +104,11 @@ func NewFile(m Mode, downloadURL string) (*DownloadFile, error) { | |
return parseFile(bts) | ||
} | ||
|
||
switch m { | ||
case Sync, Async, Redirect, AsyncRedirect, None: | ||
return &DownloadFile{Mode: m, DownloadURL: downloadURL}, nil | ||
default: | ||
return nil, errors.E(op, errors.KindBadRequest, fmt.Sprintf(invalidModeErr, m)) | ||
retFile := &DownloadFile{Mode: m, DownloadURL: downloadURL} | ||
marwan-at-work marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err := retFile.Validate(); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct me if I'm wrong but: The old code that was removed here no longer works because in the old code we are checking the top level Mode, but in this validate function only the internal However, since top level Mode is now already checked above, then this Validate function is not necessary because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marwan-at-work you're right. I left a few things out of this
I've made changes to address all this I've fixed all of that |
||
return nil, err | ||
} | ||
return retFile, nil | ||
} | ||
|
||
// parseFile parses an HCL file according to the | ||
|
@@ -93,19 +124,29 @@ func parseFile(file []byte) (*DownloadFile, error) { | |
if dig.HasErrors() { | ||
return nil, errors.E(op, dig.Error()) | ||
} | ||
if err := df.validate(); err != nil { | ||
if err := df.Validate(); err != nil { | ||
return nil, errors.E(op, err) | ||
} | ||
return &df, nil | ||
} | ||
|
||
func (d *DownloadFile) validate() error { | ||
const op errors.Op = "downloadMode.validate" | ||
// Validate validates the download file. It implements the | ||
// config.Validator interface | ||
func (d *DownloadFile) Validate() error { | ||
const op errors.Op = "DownloadFile.Validate" | ||
for _, p := range d.Paths { | ||
if err := p.Mode.Validate(); err != nil { | ||
return err | ||
} | ||
switch p.Mode { | ||
case Sync, Async, Redirect, AsyncRedirect, None: | ||
default: | ||
return errors.E(op, fmt.Errorf("unrecognized mode for %v: %v", p.Pattern, p.Mode)) | ||
return errors.Config( | ||
op, | ||
fmt.Sprintf("mode (in pattern %v", p.Pattern), | ||
fmt.Sprintf("%s is unrecognized", p.Mode), | ||
"https://docs.gomods.io/configuration/download/", | ||
) | ||
} | ||
} | ||
return nil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package errors | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// Config returns an error specifically tailored for reporting errors with configuration | ||
// values. You can check for these errors by calling errors.Is(err, KindConfigError) | ||
// (from the github.com/gomods/athens/pkg/errors package). | ||
// | ||
// Generally these kinds of errors should make Athens crash because it was configured | ||
// improperly | ||
func Config(op Op, field, helpText, url string) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will this ever be called from outside the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope. I put it here just because I think it makes more sense to group a configuration error under the |
||
slc := []string{ | ||
fmt.Sprintf("There was a configuration error with %s. %s", field, helpText), | ||
} | ||
if url != "" { | ||
slc = append(slc, fmt.Sprintf("Please see %s for more information.", url)) | ||
} | ||
return E(op, KindConfigError, strings.Join(slc, " ")) | ||
marwan-at-work marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will break people who are binding
0.0.0.0:<port>
-- plus our code inensurePortFormat
will always add the ":", so I think it's best we remove this validation and if the PORT configuration that the user put is wrong, Go will tell them when the server doesn't boot up.