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

VictorOps api-key <SECRET> via file feature #2554

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
51 changes: 28 additions & 23 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,12 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c.Global = DefaultGlobalConfig()
}

if c.Global.SlackAPIURL != nil && len(c.Global.SlackAPIURLFile) > 0 {
return fmt.Errorf("at most one of slack_api_url & slack_api_url_file must be configured")
if c.Global.SlackAPIURL != nil {
return fmt.Errorf("at most one of slack_api_url must be configured")
}
Comment on lines +304 to +306
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is about Slack and should not change.


if c.Global.VictorOpsAPIKey != "" && len(c.Global.VictorOpsAPIKeyFile) > 0 {
return fmt.Errorf("at most one of victorops_api_key & victorops_api_key_file must be configured")
}

names := map[string]struct{}{}
Expand Down Expand Up @@ -440,11 +444,12 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
if !strings.HasSuffix(voc.APIURL.Path, "/") {
voc.APIURL.Path += "/"
}
if voc.APIKey == "" {
if c.Global.VictorOpsAPIKey == "" {
return fmt.Errorf("no global VictorOps API Key set")
if voc.APIKey == "" && len(voc.APIKeyFile) == 0 {
if c.Global.VictorOpsAPIKey == "" && len(c.Global.VictorOpsAPIKeyFile) == 0 {
return fmt.Errorf("no global VictorOps API Key set either inline or in a file")
}
voc.APIKey = c.Global.VictorOpsAPIKey
voc.APIKeyFile = c.Global.VictorOpsAPIKeyFile
}
}
names[rcv.Name] = struct{}{}
Expand Down Expand Up @@ -628,24 +633,24 @@ type GlobalConfig struct {

HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`

SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
SMTPSmarthost HostPort `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" json:"smtp_require_tls,omitempty"`
SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you are accidentally removing this line about Slack.

PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"`
OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"`
WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"`
WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"`
WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"`
VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`
VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"`
SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
SMTPSmarthost HostPort `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" json:"smtp_require_tls,omitempty"`
SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"`
OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"`
WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"`
WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"`
WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"`
VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`
VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"`
VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig.
Expand Down
38 changes: 36 additions & 2 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -810,14 +810,48 @@ func TestVictorOpsDefaultAPIKey(t *testing.T) {
t.Errorf("Invalid victorops key: %s\nExpected: %s", conf.Receivers[0].VictorOpsConfigs[0].APIKey, "qwe456")
}
}
func TestVictorOpsBothAPIURLAndFile(t *testing.T) {
_, err := LoadFile("testdata/conf.victorops-api-key-both-file-and-inline.yml")
if err == nil {
t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.victorops-api-key-both-file-and-inline.yml", err)
}
if err.Error() != "at most one of victorops_api_key & victorops_api_key_file must be configured" {
t.Errorf("Expected: %s\nGot: %s", "at most one of victorops_api_key & victorops_api_key_file must be configured", err.Error())
}
}

func TestVictorOpsNoAPIKey(t *testing.T) {
_, err := LoadFile("testdata/conf.victorops-no-apikey.yml")
if err == nil {
t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.victorops-no-apikey.yml", err)
}
if err.Error() != "no global VictorOps API Key set" {
t.Errorf("Expected: %s\nGot: %s", "no global VictorOps API Key set", err.Error())
if err.Error() != "no global VictorOps API Key set or in a file" {
t.Errorf("Expected: %s\nGot: %s", "no global VictorOps API Key set or in a file", err.Error())
}
}

func TestVictorOpsGlobalAPIKEYFile(t *testing.T) {
conf, err := LoadFile("testdata/conf.victorops-default-apikey-file.yml")
if err != nil {
t.Fatalf("Error parsing %s: %s", "testdata/conf.victorops-default-apikey-file.yml", err)
}

// no override
firstConfig := conf.Receivers[0].VictorOpsConfigs[0]
if firstConfig.APIKeyFile != "/global_file" || firstConfig.APIKey != "" {
t.Fatalf("Invalid VictorOps APIKEY file: %s\nExpected: %s", firstConfig.APIKeyFile, "/global_file")
}

// override the file
secondConfig := conf.Receivers[0].VictorOpsConfigs[1]
if secondConfig.APIKeyFile != "/override_file" || secondConfig.APIKey != "" {
t.Fatalf("Invalid VictorOps APIKEY file: %s\nExpected: %s", secondConfig.APIKeyFile, "/override_file")
}

// override the global file with an inline <SECRET>
thirdConfig := conf.Receivers[0].VictorOpsConfigs[2]
if thirdConfig.APIKeyFile != "" {
t.Fatalf("Invalid VictorOps APIKEY: %s\nExpected: %s", thirdConfig.APIKey, "<SECRET>")
}
}

Expand Down
9 changes: 8 additions & 1 deletion config/notifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ type VictorOpsConfig struct {
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`

APIKey Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"`
APIKeyFile Secret `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"`
APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"`
APIURL *URL `yaml:"api_url" json:"api_url"`
RoutingKey string `yaml:"routing_key" json:"routing_key"`
MessageType string `yaml:"message_type" json:"message_type"`
Expand All @@ -517,6 +517,13 @@ func (c *VictorOpsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
if err := unmarshal((*plain)(c)); err != nil {
return err
}

if c.APIKey != "" && len(c.APIKeyFile) > 0 {
return fmt.Errorf("at most one of api_key & api_key_file must be configured")
}

return nil

if c.RoutingKey == "" {
return fmt.Errorf("missing Routing key in VictorOps config")
}
Expand Down
13 changes: 13 additions & 0 deletions config/testdata/conf.victorops-api-key-both-file-and-inline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
global:
victorops_api_key: "<SECRET>"
victorops_api_key_file: '/global_file'

route:
receiver: 'victorops-notifications'
group_by: [alertname, datacenter, app]

receivers:
- name: 'victorops-notifications'
slack_configs:
- channel: '#alerts1'
text: 'test'
21 changes: 21 additions & 0 deletions config/testdata/conf.victorops-default-api-key-file.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
global:
victorops_api_key_file: '/global_file'

route:
receiver: 'victorops-notifications'
group_by: [alertname, datacenter, app]

receivers:
- name: 'victorops-notifications'
slack_configs:
# Use global
- channel: '#alerts1'
text: 'test'
# Override global with other file
- channel: '#alerts2'
text: 'test'
victorops_api_key_file: '/override_file'
# Override global with inline URL
- channel: '#alerts3'
text: 'test'
victorops_api_key: '<SECRET>'
18 changes: 17 additions & 1 deletion notify/victorops/victorops.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/go-kit/kit/log"
Expand Down Expand Up @@ -71,7 +72,22 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
tmpl = notify.TmplText(n.tmpl, data, &err)
apiURL = n.conf.APIURL.Copy()
)
apiURL.Path += fmt.Sprintf("%s/%s", n.conf.APIKey, tmpl(n.conf.RoutingKey))

var api_key_read string

apiKey := tmpl(string(n.conf.APIKey))

if n.conf.APIKey != "" {
api_key_read = apiKey
} else {
content, err := ioutil.ReadFile(n.conf.APIKeyFile)
if err != nil {
return false, err
}
api_key_read = string(content)
}

apiURL.Path += fmt.Sprintf("%s/%s", api_key_read, tmpl(n.conf.RoutingKey))
if err != nil {
return false, fmt.Errorf("templating error: %s", err)
}
Expand Down
1 change: 1 addition & 0 deletions notify/victorops/victorops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// patch

package victorops

Expand Down