diff --git a/validate/labels.go b/validate/labels.go index e8acedb..38bc767 100644 --- a/validate/labels.go +++ b/validate/labels.go @@ -3,6 +3,7 @@ package validate import ( "fmt" "regexp" + "strings" "go.uber.org/multierr" ) @@ -12,6 +13,10 @@ type regexKV struct { V *regexp.Regexp } +func (r regexKV) String() string { + return fmt.Sprintf("%s=%s", r.K, r.V) +} + // AllowedLabels allows labels to be validated against a set of allowed labels. // The zero value is ready to use and denies all labels. type AllowedLabels struct { @@ -46,7 +51,11 @@ func (l *AllowedLabels) Validate(lbls map[string]string) error { violations = append(violations, l.ValidateLabel(k, v)) } - return multierr.Combine(violations...) + if err := multierr.Combine(violations...); err != nil { + return fmt.Errorf("label validation failed: %w, allowed labels: %s", err, l.formatLabels()) + } + + return nil } func anchor(s string) string { @@ -63,3 +72,16 @@ func (l *AllowedLabels) ValidateLabel(key, value string) error { return fmt.Errorf("label %s=%s is not allowed", key, value) } + +func (l *AllowedLabels) String() string { + return fmt.Sprintf("allowed %s", l.formatLabels()) +} + +func (l *AllowedLabels) formatLabels() string { + s := make([]string, 0, len(l.allowed)) + for _, allowed := range l.allowed { + s = append(s, allowed.String()) + } + + return fmt.Sprintf("{ %s }", strings.Join(s, ", ")) +} diff --git a/validate/labels_test.go b/validate/labels_test.go index 787c1ed..d605be9 100644 --- a/validate/labels_test.go +++ b/validate/labels_test.go @@ -46,3 +46,12 @@ func Test_AllowedLabels_Validate(t *testing.T) { assert.ErrorContains(t, err, "some.other.io=a") assert.ErrorContains(t, err, "some.other.io/2=a") } + +func Test_AllowedLabels_String(t *testing.T) { + v := validate.AllowedLabels{} + + v.Add("my.ns.io/node-class", "flex|plus") + v.Add("app", ".+") + + assert.Equal(t, "allowed { ^(?:my.ns.io/node-class)$=^(?:flex|plus)$, ^(?:app)$=^(?:.+)$ }", v.String()) +}