Skip to content

Commit

Permalink
config: implement and use validator interface
Browse files Browse the repository at this point in the history
Signed-off-by: Hank Donnay <[email protected]>
  • Loading branch information
hdonnay committed Nov 4, 2021
1 parent 63c26ab commit 9033bc9
Show file tree
Hide file tree
Showing 21 changed files with 443 additions and 247 deletions.
2 changes: 1 addition & 1 deletion cmd/clair/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func main() {
golog.Fatalf("failed to decode yaml config: %v", err)
}
conf.Mode = runMode.String()
err = config.Validate(&conf)
_, err = config.Validate(&conf)
if err != nil {
golog.Fatalf("failed to validate config: %v", err)
}
Expand Down
8 changes: 4 additions & 4 deletions cmd/clair/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ func (v *ConfMode) String() string {
}
switch *v {
case ModeCombo:
return config.ComboMode
return config.ComboMode.String()
case ModeIndexer:
return config.IndexerMode
return config.IndexerMode.String()
case ModeMatcher:
return config.MatcherMode
return config.MatcherMode.String()
case ModeNotifier:
return config.NotifierMode
return config.NotifierMode.String()
default:
}
return "invalid"
Expand Down
15 changes: 7 additions & 8 deletions config/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,17 @@ type AuthPSK struct {
Issuer []string `yaml:"iss" json:"iss"`
}

func (a *AuthPSK) lint() (ws []Warning, err error) {
func (a *AuthPSK) validate(_ Mode) ([]Warning, error) {
if len(a.Key) == 0 {
ws = append(ws, Warning{
path: ".key",
msg: "key is empty",
})
return nil, &Warning{
msg: "key is empty",
}
}
if len(a.Issuer) == 0 {
ws = append(ws, Warning{
return nil, &Warning{
path: ".iss",
msg: "no issuers defined",
})
}
}
return ws, nil
return nil, nil
}
59 changes: 14 additions & 45 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,7 @@ package config

import (
"fmt"
"strings"
)

// Clair Modes
const (
// Run this mode to create receive Manifests and create IndexReports.
IndexerMode = "indexer"
// Run this mode to retrieve IndexReports and create VulnerabilityReports.
MatcherMode = "matcher"
// Run this mode to run all modes in a single Clair instance.
ComboMode = "combo"
// Run this mode to listen for Updates and send notifications when they occur.
NotifierMode = "notifier"
"net"
)

// DefaultAddress is used if an http_listen_addr is not provided in the config.
Expand Down Expand Up @@ -60,42 +48,23 @@ type Config struct {
Metrics Metrics `yaml:"metrics" json:"metrics"`
}

// Validate confirms the necessary values to support
// the desired Clair mode exist.
func Validate(conf *Config) error {
if conf.HTTPListenAddr == "" {
conf.HTTPListenAddr = DefaultAddress
func (c *Config) validate(mode Mode) ([]Warning, error) {
if c.HTTPListenAddr == "" {
c.HTTPListenAddr = DefaultAddress
}
if conf.Matcher.DisableUpdaters {
conf.Updaters.Sets = []string{}
if c.Matcher.DisableUpdaters {
c.Updaters.Sets = []string{}
}
switch strings.ToLower(conf.Mode) {
case ComboMode:
if err := conf.Indexer.Validate(true); err != nil {
return err
}
if err := conf.Matcher.Validate(true); err != nil {
return err
}
if err := conf.Notifier.Validate(true); err != nil {
return err
}
case IndexerMode:
if err := conf.Indexer.Validate(false); err != nil {
return err
}
case MatcherMode:
if err := conf.Matcher.Validate(false); err != nil {
return err
}
case NotifierMode:
if err := conf.Notifier.Validate(false); err != nil {
return err
}
switch mode {
case ComboMode, IndexerMode, MatcherMode, NotifierMode:
// OK
default:
return fmt.Errorf("unknown mode received: %v", conf.Mode)
return nil, fmt.Errorf("unknown mode: %q", mode)
}
if _, _, err := net.SplitHostPort(c.HTTPListenAddr); err != nil {
return nil, err
}
return nil
return c.lint()
}

func (c *Config) lint() (ws []Warning, err error) {
Expand Down
42 changes: 11 additions & 31 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,14 @@ func TestConfigValidateFailure(t *testing.T) {
{
name: "ComboMode, Malformed Global HTTP Listen Addr",
conf: config.Config{
Mode: config.ComboMode,
Mode: config.ComboMode.String(),
HTTPListenAddr: "xyz",
},
},
{
name: "IndexerMode, No ConnString",
conf: config.Config{
Mode: config.IndexerMode,
HTTPListenAddr: "localhost:8080",
Indexer: config.Indexer{
ConnString: "",
},
},
},
{
name: "MatcherMode, No ConnString",
conf: config.Config{
Mode: config.MatcherMode,
HTTPListenAddr: "localhost:8080",
Matcher: config.Matcher{
ConnString: "",
},
},
},
{
name: "MatcherMode, No IndexerAddr",
conf: config.Config{
Mode: config.MatcherMode,
Mode: config.MatcherMode.String(),
HTTPListenAddr: "localhost:8080",
Matcher: config.Matcher{
ConnString: "example@example/db",
Expand All @@ -62,7 +42,7 @@ func TestConfigValidateFailure(t *testing.T) {

for _, tab := range table {
t.Run(tab.name, func(t *testing.T) {
if err := config.Validate(&tab.conf); err == nil {
if _, err := config.Validate(&tab.conf); err == nil {
t.Fatalf("expected error for test case: %s", tab.name)
}
})
Expand All @@ -79,7 +59,7 @@ func TestConfigUpateRetention(t *testing.T) {
name: "Retention less than 0",
expectedRetention: 0,
conf: config.Config{
Mode: config.ComboMode,
Mode: config.ComboMode.String(),
HTTPListenAddr: "localhost:8080",
Indexer: config.Indexer{
ConnString: "example@example/db",
Expand All @@ -98,7 +78,7 @@ func TestConfigUpateRetention(t *testing.T) {
name: "Retention of 0",
expectedRetention: 10,
conf: config.Config{
Mode: config.ComboMode,
Mode: config.ComboMode.String(),
HTTPListenAddr: "localhost:8080",
Indexer: config.Indexer{
ConnString: "example@example/db",
Expand All @@ -117,7 +97,7 @@ func TestConfigUpateRetention(t *testing.T) {
name: "Retention less than 2",
expectedRetention: 10,
conf: config.Config{
Mode: config.ComboMode,
Mode: config.ComboMode.String(),
HTTPListenAddr: "localhost:8080",
Indexer: config.Indexer{
ConnString: "example@example/db",
Expand All @@ -136,7 +116,7 @@ func TestConfigUpateRetention(t *testing.T) {
name: "Retention of 2",
expectedRetention: 2,
conf: config.Config{
Mode: config.ComboMode,
Mode: config.ComboMode.String(),
HTTPListenAddr: "localhost:8080",
Indexer: config.Indexer{
ConnString: "example@example/db",
Expand All @@ -154,7 +134,7 @@ func TestConfigUpateRetention(t *testing.T) {
}
for _, tab := range table {
t.Run(tab.name, func(t *testing.T) {
err := config.Validate(&tab.conf)
_, err := config.Validate(&tab.conf)
if err != nil {
t.Fatalf("expected no errors but got: %s, for test case: %s", err, tab.name)
}
Expand All @@ -173,7 +153,7 @@ func TestConfigDisableUpdaters(t *testing.T) {
{
name: "ComboMode, disable updaters",
conf: config.Config{
Mode: config.ComboMode,
Mode: config.ComboMode.String(),
HTTPListenAddr: "localhost:8080",
Indexer: config.Indexer{
ConnString: "example@example/db",
Expand All @@ -197,7 +177,7 @@ func TestConfigDisableUpdaters(t *testing.T) {
{
name: "MatcherMode, disable updaters",
conf: config.Config{
Mode: config.MatcherMode,
Mode: config.MatcherMode.String(),
HTTPListenAddr: "localhost:8080",
Matcher: config.Matcher{
ConnString: "example@example/db",
Expand All @@ -216,7 +196,7 @@ func TestConfigDisableUpdaters(t *testing.T) {

for _, tab := range table {
t.Run(tab.name, func(t *testing.T) {
err := config.Validate(&tab.conf)
_, err := config.Validate(&tab.conf)
if err != nil {
t.Fatalf("expected no errors but got: %s, for test case: %s", err, tab.name)
}
Expand Down
79 changes: 79 additions & 0 deletions config/enums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package config

import (
"encoding"
"fmt"
"strings"
)

//go:generate stringer -type Mode,LogLevel -linecomment -output enums_string.go

// A Mode is an operating mode recognized by Clair.
//
// This is not directly settable by serializing into a Config object.
type Mode int

// Clair modes, with their string representations as the comments.
const (
ComboMode Mode = iota // combo
IndexerMode // indexer
MatcherMode // matcher
NotifierMode // notifier
)

// ParseMode returns a mode for the given string.
//
// The passed string is case-insensitive.
func ParseMode(s string) (Mode, error) {
for i, lim := 0, len(_Mode_index); i < lim; i++ {
m := Mode(i)
if strings.EqualFold(s, m.String()) {
return m, nil
}
}
return Mode(-1), fmt.Errorf(`unknown mode %q`, s)
}

// A LogLevel is a log level recognized by Clair.
//
// The zero value is "info".
type LogLevel int

// The recognized log levels, with their string representations as the comments.
//
// NB "Fatal" and "Panic" are not used in clair or claircore, and will result in
// almost no logging.
const (
InfoLog LogLevel = iota // info
DebugColorLog // debug-color
DebugLog // debug
WarnLog // warn
ErrorLog // error
FatalLog // fatal
PanicLog // panic
)

// ParseLogLevel returns the log lever for the given string.
//
// The passed string is case-insensitive.
func ParseLogLevel(s string) (LogLevel, error) {
for i, lim := 0, len(_LogLevel_index); i < lim; i++ {
l := LogLevel(i)
if strings.EqualFold(s, l.String()) {
return l, nil
}
}
return LogLevel(-1), fmt.Errorf(`unknown log level %q`, s)
}

// UnmarshalText implements encoding.TextUnmarshaler.
func (l *LogLevel) UnmarshalText(b []byte) (err error) {
*l, err = ParseLogLevel(string(b))
if err != nil {
return err
}
return nil
}

// Assert LogLevel implements TextUnmarshaler.
var _ encoding.TextUnmarshaler = (*LogLevel)(nil)
49 changes: 49 additions & 0 deletions config/enums_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions config/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/quay/clair/config

go 1.16

require github.com/google/go-cmp v0.5.6

require golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
require (
github.com/google/go-cmp v0.5.6
golang.org/x/tools v0.1.7
)
Loading

0 comments on commit 9033bc9

Please sign in to comment.