-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Hank Donnay <hdonnay@redhat.com>
- Loading branch information
Showing
10 changed files
with
479 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package config | ||
|
||
import ( | ||
"net/url" | ||
"os" | ||
"strings" | ||
) | ||
|
||
func checkDSN(s string) (w []Warning, err error) { | ||
switch { | ||
case s == "": | ||
// Nothing specified, make sure something's in the environment. | ||
envSet := false | ||
for _, k := range os.Environ() { | ||
if strings.HasPrefix(k, `PG`) { | ||
envSet = true | ||
break | ||
} | ||
} | ||
if !envSet { | ||
w = append(w, Warning{ | ||
msg: "connection string is empty and no relevant environment variables found", | ||
}) | ||
} | ||
case strings.HasPrefix(s, "postgresql://"): | ||
// Looks like a URL | ||
if _, err := url.Parse(s); err != nil { | ||
w = append(w, Warning{inner: err}) | ||
} | ||
case strings.ContainsRune(s, '='): | ||
// Looks like a DSN | ||
case strings.Contains(s, `://`): | ||
w = append(w, Warning{ | ||
msg: "connection string looks like a URL but scheme is unrecognized", | ||
}) | ||
default: | ||
w = append(w, Warning{ | ||
msg: "unable to make sense of connection string", | ||
}) | ||
} | ||
return w, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package config | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
) | ||
|
||
// Lint runs lints on the provided Config. | ||
// | ||
// An error is reported only if an error occurred while running the lints. An | ||
// invalid Config may still report a nil error along with a slice of Warnings. | ||
func Lint(c *Config) ([]Warning, error) { | ||
// The linter does a teeny bit of reflection with an internal interface to | ||
// check different values. | ||
ws := []Warning{} | ||
v := reflect.ValueOf(c) | ||
path := "$" | ||
var walk func(string, reflect.Value) error | ||
walk = func(path string, v reflect.Value) error { | ||
t := v.Type() | ||
var vi interface{} | ||
// Figure out if we should take the address to do the interface | ||
// assertion, or if the value is already a pointer. | ||
switch { | ||
case t.Kind() != reflect.Ptr && v.CanAddr(): | ||
vi = v.Addr().Interface() | ||
case v.CanInterface() && v.IsValid() && !v.IsZero(): | ||
vi = v.Interface() | ||
} | ||
|
||
if l, ok := vi.(linter); ok { | ||
w, err := l.lint() | ||
if err != nil { | ||
return err | ||
} | ||
for i := range w { | ||
// Adjust the path here, so that the lint method doesn't need to | ||
// know where it is. | ||
w[i].path = path + w[i].path | ||
} | ||
ws = append(ws, w...) | ||
} | ||
|
||
// Dereference the pointer, if this is a pointer. | ||
if t.Kind() == reflect.Ptr { | ||
t = t.Elem() | ||
v = v.Elem() | ||
} | ||
if !v.IsValid() { | ||
return nil | ||
} | ||
switch t.Kind() { | ||
case reflect.Struct: | ||
for i, lim := 0, t.NumField(); i < lim; i++ { | ||
f := t.Field(i) | ||
n := f.Name | ||
if t := f.Tag.Get("json"); t != "" && t != "-" { | ||
// Handle the comma options. | ||
if i := strings.IndexByte(t, ','); i != -1 { | ||
t = t[:i] | ||
} | ||
n = t | ||
} | ||
p := fmt.Sprintf(`%s.%s`, path, n) | ||
if err := walk(p, v.Field(i)); err != nil { | ||
return err | ||
} | ||
} | ||
case reflect.Map: | ||
i := v.MapRange() | ||
for i.Next() { | ||
p := fmt.Sprintf(`%s.[%s]`, path, i.Key().String()) | ||
if err := walk(p, i.Value()); err != nil { | ||
return err | ||
} | ||
|
||
} | ||
case reflect.Slice: | ||
for i, lim := 0, v.Len(); i < lim; i++ { | ||
p := fmt.Sprintf(`%s.[%d]`, path, i) | ||
if err := walk(p, v.Index(i)); err != nil { | ||
return err | ||
} | ||
} | ||
default: | ||
// everything else, just pass | ||
} | ||
return nil | ||
} | ||
return ws, walk(path, v) | ||
} | ||
|
||
// Types in this package can implement this interface to report common issues or | ||
// deprecation warnings. | ||
type linter interface { | ||
lint() ([]Warning, error) | ||
} | ||
|
||
// Warning is a linter warning. | ||
// | ||
// Users can treat them like errors and use the sentinel values exported by this | ||
// package. | ||
type Warning struct { | ||
inner error | ||
path string // json-schema style path | ||
msg string | ||
} | ||
|
||
// Should have inner xor msg | ||
|
||
func (w *Warning) Error() string { | ||
var b strings.Builder | ||
if w.inner != nil { | ||
b.WriteString(w.inner.Error()) | ||
} else { | ||
b.WriteString(w.msg) | ||
} | ||
b.WriteString(" (at ") | ||
b.WriteString(w.path) | ||
b.WriteRune(')') | ||
return b.String() | ||
} | ||
|
||
func (w *Warning) Unwrap() error { return w.inner } | ||
|
||
// These are some common kinds of Warnings. | ||
var ( | ||
ErrDeprecated = errors.New("setting will be removed in a future release") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package config | ||
|
||
import "fmt" | ||
|
||
func ExampleLint() { | ||
var c Config | ||
c.Auth.Keyserver = &AuthKeyserver{} | ||
c.Auth.PSK = &AuthPSK{} | ||
ws, err := Lint(&c) | ||
fmt.Println("error:", err) | ||
for _, w := range ws { | ||
fmt.Printf("warning: %v\n", &w) | ||
} | ||
// Output: | ||
// error: <nil> | ||
// warning: http listen address not provided, default will be used (at $.http_listen_addr) | ||
// warning: introspection address not provided, default will be used (at $.introspection_addr) | ||
// warning: connection string is empty and no relevant environment variables found (at $.indexer.connstring) | ||
// warning: unlimited concurrent requests may exceed resource quotas (at $.indexer.index_report_request_concurrency) | ||
// warning: connection string is empty and no relevant environment variables found (at $.matcher.connstring) | ||
// warning: updater period is very aggressive: most sources are updated daily (at $.matcher.period) | ||
// warning: update garbage collection is off (at $.matcher.update_retention) | ||
// warning: connection string is empty and no relevant environment variables found (at $.notifier.connstring) | ||
// warning: interval is very fast: may result in increased workload (at $.notifier.poll_interval) | ||
// warning: interval is very fast: may result in increased workload (at $.notifier.delivery_interval) | ||
// warning: both "PSK" and "Keyserver" authentication methods are defined (at $.auth) | ||
// warning: key is empty (at $.auth.psk.key) | ||
// warning: no issuers defined (at $.auth.psk.iss) | ||
// warning: authentication method deprecated: setting will be removed in a future release (at $.auth.keyserver) | ||
} |
Oops, something went wrong.