Skip to content
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

Omit empty config fields and show regex upon re-marshalling to elide secrets #864

Merged
merged 2 commits into from
Jun 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,19 +303,19 @@ type GlobalConfig struct {
// if it has not been updated.
ResolveTimeout model.Duration `yaml:"resolve_timeout" json:"resolve_timeout"`

SMTPFrom string `yaml:"smtp_from" json:"smtp_from"`
SMTPSmarthost string `yaml:"smtp_smarthost" json:"smtp_smarthost"`
SMTPAuthUsername string `yaml:"smtp_auth_username" json:"smtp_auth_username"`
SMTPAuthPassword Secret `yaml:"smtp_auth_password" json:"smtp_auth_password"`
SMTPAuthSecret Secret `yaml:"smtp_auth_secret" json:"smtp_auth_secret"`
SMTPAuthIdentity string `yaml:"smtp_auth_identity" json:"smtp_auth_identity"`
SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls"`
SlackAPIURL Secret `yaml:"slack_api_url" json:"slack_api_url"`
PagerdutyURL string `yaml:"pagerduty_url" json:"pagerduty_url"`
HipchatURL string `yaml:"hipchat_url" json:"hipchat_url"`
HipchatAuthToken Secret `yaml:"hipchat_auth_token" json:"hipchat_auth_token"`
OpsGenieAPIHost string `yaml:"opsgenie_api_host" json:"opsgenie_api_host"`
VictorOpsAPIURL string `yaml:"victorops_api_url" json:"victorops_api_url"`
SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
SMTPSmarthost string `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"`
SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"`
SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"`
SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"`
SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"`
SMTPRequireTLS bool `yaml:"smtp_require_tls,omitempty" json:"smtp_require_tls,omitempty"`
SlackAPIURL Secret `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
PagerdutyURL string `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
HipchatURL string `yaml:"hipchat_url,omitempty" json:"hipchat_url,omitempty"`
HipchatAuthToken Secret `yaml:"hipchat_auth_token,omitempty" json:"hipchat_auth_token,omitempty"`
OpsGenieAPIHost string `yaml:"opsgenie_api_host,omitempty" json:"opsgenie_api_host,omitempty"`
VictorOpsAPIURL string `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
Expand Down Expand Up @@ -386,19 +386,19 @@ func (r *Route) UnmarshalYAML(unmarshal func(interface{}) error) error {
type InhibitRule struct {
// SourceMatch defines a set of labels that have to equal the given
// value for source alerts.
SourceMatch map[string]string `yaml:"source_match" json:"source_match"`
SourceMatch map[string]string `yaml:"source_match,omitempty" json:"source_match,omitempty"`
// SourceMatchRE defines pairs like SourceMatch but does regular expression
// matching.
SourceMatchRE map[string]Regexp `yaml:"source_match_re" json:"source_match_re"`
SourceMatchRE map[string]Regexp `yaml:"source_match_re,omitempty" json:"source_match_re,omitempty"`
// TargetMatch defines a set of labels that have to equal the given
// value for target alerts.
TargetMatch map[string]string `yaml:"target_match" json:"target_match"`
TargetMatch map[string]string `yaml:"target_match,omitempty" json:"target_match,omitempty"`
// TargetMatchRE defines pairs like TargetMatch but does regular expression
// matching.
TargetMatchRE map[string]Regexp `yaml:"target_match_re" json:"target_match_re"`
TargetMatchRE map[string]Regexp `yaml:"target_match_re,omitempty" json:"target_match_re,omitempty"`
// A set of labels that must be equal between the source and target alert
// for them to be a match.
Equal model.LabelNames `yaml:"equal" json:"equal"`
Equal model.LabelNames `yaml:"equal,omitempty" json:"equal,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
Expand Down Expand Up @@ -488,8 +488,8 @@ func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
}

// MarshalYAML implements the yaml.Marshaler interface.
func (re *Regexp) MarshalYAML() (interface{}, error) {
if re != nil {
func (re Regexp) MarshalYAML() (interface{}, error) {
if re.Regexp != nil {
return re.String(), nil
}
return nil, nil
Expand Down
87 changes: 82 additions & 5 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ package config

import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"
"testing"
"time"

"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -69,14 +71,13 @@ receivers:
func TestHideConfigSecrets(t *testing.T) {
c, _, err := LoadFile("testdata/conf.good.yml")
if err != nil {
t.Errorf("Error parsing %s: %s", "testdata/good.yml", err)
t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err)
}

// String method must not reveal authentication credentials.
s := c.String()
secretRe := regexp.MustCompile("<secret>")
matches := secretRe.FindAllStringIndex(s, -1)
fmt.Println(len(matches))
if len(matches) != 14 || strings.Contains(s, "mysecret") {
t.Fatal("config's String method reveals authentication credentials.")
}
Expand All @@ -85,7 +86,7 @@ func TestHideConfigSecrets(t *testing.T) {
func TestJSONMarshal(t *testing.T) {
c, _, err := LoadFile("testdata/conf.good.yml")
if err != nil {
t.Errorf("Error parsing %s: %s", "testdata/good.yml", err)
t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err)
}

_, err = json.Marshal(c)
Expand Down Expand Up @@ -114,7 +115,7 @@ func TestJSONMarshalSecret(t *testing.T) {
func TestJSONUnmarshalMarshaled(t *testing.T) {
c, _, err := LoadFile("testdata/conf.good.yml")
if err != nil {
t.Errorf("Error parsing %s: %s", "testdata/good.yml", err)
t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err)
}

plainCfg, err := json.Marshal(c)
Expand All @@ -128,3 +129,79 @@ func TestJSONUnmarshalMarshaled(t *testing.T) {
t.Fatal("JSON Unmarshaling failed:", err)
}
}

func TestEmptyFieldsAndRegex(t *testing.T) {

boolFoo := true
var regexpFoo Regexp
regexpFoo.Regexp, _ = regexp.Compile("^(?:^(foo1|foo2|baz)$)$")

var expectedConf = Config{

Global: &GlobalConfig{
ResolveTimeout: model.Duration(5 * time.Minute),
SMTPSmarthost: "localhost:25",
SMTPFrom: "[email protected]",
HipchatAuthToken: "mysecret",
HipchatURL: "https://hipchat.foobar.org/",
SlackAPIURL: "mysecret",
SMTPRequireTLS: true,
PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json",
OpsGenieAPIHost: "https://api.opsgenie.com/",
VictorOpsAPIURL: "https://alert.victorops.com/integrations/generic/20131114/alert/",
},

Templates: []string{
"/etc/alertmanager/template/*.tmpl",
},
Route: &Route{
Receiver: "team-X-mails",
GroupBy: []model.LabelName{
"alertname",
"cluster",
"service",
},
Routes: []*Route{
{
Receiver: "team-X-mails",
MatchRE: map[string]Regexp{
"service": regexpFoo,
},
},
},
},
Receivers: []*Receiver{
{
Name: "team-X-mails",
EmailConfigs: []*EmailConfig{
{
To: "[email protected]",
From: "[email protected]",
Smarthost: "localhost:25",
HTML: "{{ template \"email.default.html\" . }}",
RequireTLS: &boolFoo,
},
},
},
},
}

config, _, err := LoadFile("testdata/conf.empty-fields.yml")
if err != nil {
t.Errorf("Error parsing %s: %s", "testdata/conf.empty-fields.yml", err)
}

configGot, err := yaml.Marshal(config)
if err != nil {
t.Fatal("YAML Marshaling failed:", err)
}

configExp, err := yaml.Marshal(expectedConf)
if err != nil {
t.Fatalf("%s", err)
}

if !reflect.DeepEqual(configGot, configExp) {
t.Fatalf("%s: unexpected config result: \n\n%s\n expected\n\n%s", "testdata/conf.empty-fields.yml", configGot, configExp)
}
}
116 changes: 58 additions & 58 deletions config/notifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,15 @@ type EmailConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

// Email address to notify.
To string `yaml:"to" json:"to"`
From string `yaml:"from" json:"from"`
To string `yaml:"to,omitempty" json:"to,omitempty"`
From string `yaml:"from,omitempty" json:"from,omitempty"`
Smarthost string `yaml:"smarthost,omitempty" json:"smarthost,omitempty"`
AuthUsername string `yaml:"auth_username" json:"auth_username"`
AuthPassword Secret `yaml:"auth_password" json:"auth_password"`
AuthSecret Secret `yaml:"auth_secret" json:"auth_secret"`
AuthIdentity string `yaml:"auth_identity" json:"auth_identity"`
Headers map[string]string `yaml:"headers" json:"headers"`
HTML string `yaml:"html" json:"html"`
AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"`
AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"`
AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"`
AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"`
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
HTML string `yaml:"html,omitempty" json:"html,omitempty"`
RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"`

// Catches all undefined fields and must be empty after parsing.
Expand Down Expand Up @@ -174,12 +174,12 @@ func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
type PagerdutyConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

ServiceKey Secret `yaml:"service_key" json:"service_key"`
URL string `yaml:"url" json:"url"`
Client string `yaml:"client" json:"client"`
ClientURL string `yaml:"client_url" json:"client_url"`
Description string `yaml:"description" json:"description"`
Details map[string]string `yaml:"details" json:"details"`
ServiceKey Secret `yaml:"service_key,omitempty" json:"service_key,omitempty"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Client string `yaml:"client,omitempty" json:"client,omitempty"`
ClientURL string `yaml:"client_url,omitempty" json:"client_url,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
Expand All @@ -202,20 +202,20 @@ func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
type SlackConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

APIURL Secret `yaml:"api_url" json:"api_url"`
APIURL Secret `yaml:"api_url,omitempty" json:"api_url,omitempty"`

// Slack channel override, (like #other-channel or @username).
Channel string `yaml:"channel" json:"channel"`
Username string `yaml:"username" json:"username"`
Color string `yaml:"color" json:"color"`

Title string `yaml:"title" json:"title"`
TitleLink string `yaml:"title_link" json:"title_link"`
Pretext string `yaml:"pretext" json:"pretext"`
Text string `yaml:"text" json:"text"`
Fallback string `yaml:"fallback" json:"fallback"`
IconEmoji string `yaml:"icon_emoji" json:"icon_emoji"`
IconURL string `yaml:"icon_url" json:"icon_url"`
Channel string `yaml:"channel,omitempty" json:"channel,omitempty"`
Username string `yaml:"username,omitempty" json:"username,omitempty"`
Color string `yaml:"color,omitempty" json:"color,omitempty"`

Title string `yaml:"title,omitempty" json:"title,omitempty"`
TitleLink string `yaml:"title_link,omitempty" json:"title_link,omitempty"`
Pretext string `yaml:"pretext,omitempty" json:"pretext,omitempty"`
Text string `yaml:"text,omitempty" json:"text,omitempty"`
Fallback string `yaml:"fallback,omitempty" json:"fallback,omitempty"`
IconEmoji string `yaml:"icon_emoji,omitempty" json:"icon_emoji,omitempty"`
IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
Expand All @@ -235,17 +235,17 @@ func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
type HipchatConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

APIURL string `yaml:"api_url" json:"api_url"`
AuthToken Secret `yaml:"auth_token" json:"auth_token"`
RoomID string `yaml:"room_id" json:"room_id"`
From string `yaml:"from" json:"from"`
Notify bool `yaml:"notify" json:"notify"`
Message string `yaml:"message" json:"message"`
MessageFormat string `yaml:"message_format" json:"message_format"`
Color string `yaml:"color" json:"color"`
APIURL string `yaml:"api_url,omitempty" json:"api_url,omitempty"`
AuthToken Secret `yaml:"auth_token,omitempty" json:"auth_token,omitempty"`
RoomID string `yaml:"room_id,omitempty" json:"room_id,omitempty"`
From string `yaml:"from,omitempty" json:"from,omitempty"`
Notify bool `yaml:"notify,omitempty" json:"notify,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
MessageFormat string `yaml:"message_format,omitempty" json:"message_format,omitempty"`
Color string `yaml:"color,omitempty" json:"color,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
XXX map[string]interface{} `yaml:",inline" ,json:"-"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
Expand Down Expand Up @@ -290,15 +290,15 @@ func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
type OpsGenieConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

APIKey Secret `yaml:"api_key" json:"api_key"`
APIHost string `yaml:"api_host" json:"api_host"`
Message string `yaml:"message" json:"message"`
Description string `yaml:"description" json:"description"`
Source string `yaml:"source" json:"source"`
Details map[string]string `yaml:"details" json:"details"`
Teams string `yaml:"teams" json:"teams"`
Tags string `yaml:"tags" json:"tags"`
Note string `yaml:"note" json:"note"`
APIKey Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"`
APIHost string `yaml:"api_host,omitempty" json:"api_host,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Source string `yaml:"source,omitempty" json:"source,omitempty"`
Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
Teams string `yaml:"teams,omitempty" json:"teams,omitempty"`
Tags string `yaml:"tags,omitempty" json:"tags,omitempty"`
Note string `yaml:"note,omitempty" json:"note,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
Expand All @@ -321,12 +321,12 @@ func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
type VictorOpsConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

APIKey Secret `yaml:"api_key" json:"api_key"`
APIURL string `yaml:"api_url" json:"api_url"`
RoutingKey string `yaml:"routing_key" json:"routing_key"`
MessageType string `yaml:"message_type" json:"message_type"`
StateMessage string `yaml:"state_message" json:"state_message"`
MonitoringTool string `yaml:"monitoring_tool" json:"monitoring_tool"`
APIKey Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"`
APIURL string `yaml:"api_url,omitempty" json:"api_url,omitempty"`
RoutingKey string `yaml:"routing_key,omitempty" json:"routing_key,omitempty"`
MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"`
StateMessage string `yaml:"state_message,omitempty" json:"state_message,omitempty"`
MonitoringTool string `yaml:"monitoring_tool,omitempty" json:"monitoring_tool,omitempty"`

XXX map[string]interface{} `yaml:",inline" json:"-"`
}
Expand Down Expand Up @@ -364,14 +364,14 @@ func (d duration) MarshalText() ([]byte, error) {
type PushoverConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

UserKey Secret `yaml:"user_key" json:"user_key"`
Token Secret `yaml:"token" json:"token"`
Title string `yaml:"title" json:"title"`
Message string `yaml:"message" json:"message"`
URL string `yaml:"url" json:"url"`
Priority string `yaml:"priority" json:"priority"`
Retry duration `yaml:"retry" json:"retry"`
Expire duration `yaml:"expire" json:"expire"`
UserKey Secret `yaml:"user_key,omitempty" json:"user_key,omitempty"`
Token Secret `yaml:"token,omitempty" json:"token,omitempty"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
Retry duration `yaml:"retry,omitempty" json:"retry,omitempty"`
Expire duration `yaml:"expire,omitempty" json:"expire,omitempty"`

// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline" json:"-"`
Expand Down
27 changes: 27 additions & 0 deletions config/testdata/conf.empty-fields.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
global:
smtp_smarthost: 'localhost:25'
smtp_from: '[email protected]'
smtp_auth_username: ''
smtp_auth_password: ''
hipchat_auth_token: 'mysecret'
hipchat_url: 'https://hipchat.foobar.org/'
slack_api_url: 'mysecret'



templates:
- '/etc/alertmanager/template/*.tmpl'

route:
group_by: ['alertname', 'cluster', 'service']

receiver: team-X-mails
routes:
- match_re:
service: ^(foo1|foo2|baz)$
receiver: team-X-mails

receivers:
- name: 'team-X-mails'
email_configs:
- to: '[email protected]'