You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-09-16 09:26:22 +02:00
[ANS] logrus hook (#3671)
* Add ans implementation * Remove todo comment * Rename test function Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com> * Better wording Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com> * Add reading of response body function * Use http pkg ReadResponseBody * Check read error * Better test case description * Fix formatting * Create own package for read response body * Omit empty nested resource struct * Separate Resource struct from Event struct * Merge and unmarshall instead of only unmarshalling * Improve status code error message * Remove unchangeable event fields * Separate event parts * Change log level setter function * Restructure ans send test * Revert exporting readResponseBody function Instead the code is duplicated in the xsuaa and ans package * Add check correct ans setup request * Add set options function for mocking * Review fixes * Correct function name * Use strict unmarshalling * Validate event * Move functions * Add documentation comments * improve test * Validate event * Add logrus hook for ans * Set defaults on new hook creation * Fix log level on error * Don't alter entry log level * Set severity fatal on 'fatal error' log message * Ensure that log entries don't affect each other * Remove unnecessary correlationID * Use file path instead of event template string * Improve warning messages * Add empty log message check * Allow configuration from file and string * Add sourceEventId to tags * Change resourceType to Pipeline * Use structured config approach * Use new log level set function * Check correct setup and return error * Mock http requests * Only send log level warning or higher * Use new function name * One-liner ifs * Improve test name * Fix tests * Prevent double firing * Reduce Fire test size * Add error message to test * Reduce newANSHook test size * Further check error * Rename to defaultEvent in hook struct * Reduce ifs further * Fix set error category test The ansHook Fire test cannot run in parallel, as it would affect the other tests that use the error category. * Change function name to SetServiceKey * Validate event * Rename to eventTemplate in hook struct * Move copy to event.go * Fix function mix * Remove unnecessary cleanup * Remove parallel test The translation fails now and again when parallel is on. * Remove prefix test * Remove unused copyEvent function * Fix ifs * Add docu comment * Register ans hook from pkg * register hook and setup event template seperately * Exclusively read eventTemplate from environment * setupEventTemplate tests * adjust hook levels test * sync tests- wlill still fail * migrate TestANSHook_registerANSHook test * fixes * review - cleanup, reuse poke * Apply suggestions from code review * Change subject * Review fixes * Set stepName 'n/a' if not available * Fix fire tests Co-authored-by: Linda Siebert <39100394+LindaSieb@users.noreply.github.com> Co-authored-by: Roland Stengel <r.stengel@sap.com>
This commit is contained in:
@@ -75,6 +75,16 @@ func (event *Event) Validate() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Copy will copy an ANS Event
|
||||
func (event *Event) Copy() (destination Event, err error) {
|
||||
var sourceJSON []byte
|
||||
if sourceJSON, err = json.Marshal(event); err != nil {
|
||||
return
|
||||
}
|
||||
err = destination.MergeWithJSON(sourceJSON)
|
||||
return
|
||||
}
|
||||
|
||||
// SetSeverityAndCategory takes the logrus log level and sets the corresponding ANS severity and category string
|
||||
func (event *Event) SetSeverityAndCategory(level logrus.Level) {
|
||||
switch level {
|
||||
|
@@ -179,3 +179,15 @@ func defaultEvent() Event {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_Copy(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("good", func(t *testing.T) {
|
||||
originalEvent := defaultEvent()
|
||||
newEvent, err := originalEvent.Copy()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, originalEvent, newEvent, "Events should be the same after copying.")
|
||||
newEvent.Resource.ResourceType = "different"
|
||||
assert.NotEqual(t, originalEvent, newEvent, "Events should not affect each other after copying")
|
||||
})
|
||||
}
|
||||
|
141
pkg/log/ansHook.go
Normal file
141
pkg/log/ansHook.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/SAP/jenkins-library/pkg/ans"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ANSHook is used to set the hook features for the logrus hook
|
||||
type ANSHook struct {
|
||||
client ans.Client
|
||||
eventTemplate ans.Event
|
||||
firing bool
|
||||
}
|
||||
|
||||
// Levels returns the supported log level of the hook.
|
||||
func (ansHook *ANSHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{logrus.WarnLevel, logrus.ErrorLevel, logrus.PanicLevel, logrus.FatalLevel}
|
||||
}
|
||||
|
||||
// Fire creates a new event from the logrus and sends an event to the ANS backend
|
||||
func (ansHook *ANSHook) Fire(entry *logrus.Entry) (err error) {
|
||||
if ansHook.firing {
|
||||
return fmt.Errorf("ANS hook has already been fired")
|
||||
}
|
||||
ansHook.firing = true
|
||||
defer func() { ansHook.firing = false }()
|
||||
|
||||
if len(strings.TrimSpace(entry.Message)) == 0 {
|
||||
return
|
||||
}
|
||||
var event ans.Event
|
||||
if event, err = ansHook.eventTemplate.Copy(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logLevel := entry.Level
|
||||
event.SetSeverityAndCategory(logLevel)
|
||||
var stepName string
|
||||
if entry.Data["stepName"] != nil {
|
||||
stepName = fmt.Sprint(entry.Data["stepName"])
|
||||
} else {
|
||||
stepName = "n/a"
|
||||
}
|
||||
event.Tags["pipeline:stepName"] = stepName
|
||||
if errorCategory := GetErrorCategory().String(); errorCategory != "undefined" {
|
||||
event.Tags["pipeline:errorCategory"] = errorCategory
|
||||
}
|
||||
|
||||
event.EventTimestamp = entry.Time.Unix()
|
||||
if event.Subject == "" {
|
||||
event.Subject = fmt.Sprintf("Pipeline step '%s' sends '%s'", stepName, event.Severity)
|
||||
}
|
||||
event.Body = entry.Message
|
||||
event.Tags["pipeline:logLevel"] = logLevel.String()
|
||||
|
||||
return ansHook.client.Send(event)
|
||||
}
|
||||
|
||||
type registrationUtil interface {
|
||||
ans.Client
|
||||
registerHook(hook *ANSHook)
|
||||
}
|
||||
|
||||
type registrationUtilImpl struct {
|
||||
ans.Client
|
||||
}
|
||||
|
||||
func (u *registrationUtilImpl) registerHook(hook *ANSHook) {
|
||||
RegisterHook(hook)
|
||||
}
|
||||
|
||||
func (u *registrationUtilImpl) registerSecret(secret string) {
|
||||
RegisterSecret(secret)
|
||||
}
|
||||
|
||||
// RegisterANSHookIfConfigured creates a new ANS hook for logrus if it is configured and registers it
|
||||
func RegisterANSHookIfConfigured(correlationID string) error {
|
||||
return registerANSHookIfConfigured(correlationID, ®istrationUtilImpl{Client: &ans.ANS{}})
|
||||
}
|
||||
|
||||
func registerANSHookIfConfigured(correlationID string, util registrationUtil) error {
|
||||
ansServiceKeyJSON := os.Getenv("PIPER_ansHookServiceKey")
|
||||
if len(ansServiceKeyJSON) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ansServiceKey, err := ans.UnmarshallServiceKeyJSON(ansServiceKeyJSON)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot initialize SAP Alert Notification Service due to faulty serviceKey json")
|
||||
}
|
||||
RegisterSecret(ansServiceKey.ClientSecret)
|
||||
|
||||
util.SetServiceKey(ansServiceKey)
|
||||
if err = util.CheckCorrectSetup(); err != nil {
|
||||
return errors.Wrap(err, "check http request to SAP Alert Notification Service failed; not setting up the ANS hook")
|
||||
}
|
||||
|
||||
eventTemplate, err := setupEventTemplate(os.Getenv("PIPER_ansEventTemplate"), correlationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
util.registerHook(&ANSHook{
|
||||
client: util,
|
||||
eventTemplate: eventTemplate,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupEventTemplate(customerEventTemplate, correlationID string) (ans.Event, error) {
|
||||
event := ans.Event{
|
||||
EventType: "Piper",
|
||||
Tags: map[string]interface{}{"ans:correlationId": correlationID, "ans:sourceEventId": correlationID},
|
||||
Resource: &ans.Resource{
|
||||
ResourceType: "Pipeline",
|
||||
ResourceName: "Pipeline",
|
||||
},
|
||||
}
|
||||
|
||||
if len(customerEventTemplate) > 0 {
|
||||
if err := event.MergeWithJSON([]byte(customerEventTemplate)); err != nil {
|
||||
Entry().WithField("stepName", "ANS").Warnf("provided SAP Alert Notification Service event template '%s' could not be unmarshalled: %v", customerEventTemplate, err)
|
||||
return ans.Event{}, errors.Wrapf(err, "provided SAP Alert Notification Service event template '%s' could not be unmarshalled", customerEventTemplate)
|
||||
}
|
||||
}
|
||||
if len(event.Severity) > 0 {
|
||||
Entry().WithField("stepName", "ANS").Warnf("event severity set to '%s' will be overwritten according to the log level", event.Severity)
|
||||
event.Severity = ""
|
||||
}
|
||||
if len(event.Category) > 0 {
|
||||
Entry().WithField("stepName", "ANS").Warnf("event category set to '%s' will be overwritten according to the log level", event.Category)
|
||||
event.Category = ""
|
||||
}
|
||||
if err := event.Validate(); err != nil {
|
||||
return ans.Event{}, errors.Wrap(err, "did not initialize SAP Alert Notification Service due to faulty event template json")
|
||||
}
|
||||
return event, nil
|
||||
}
|
428
pkg/log/ansHook_test.go
Normal file
428
pkg/log/ansHook_test.go
Normal file
@@ -0,0 +1,428 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/SAP/jenkins-library/pkg/ans"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestANSHook_Levels(t *testing.T) {
|
||||
|
||||
registrationUtil := createRegUtil()
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
t.Run("default hook levels", func(t *testing.T) {
|
||||
registerANSHookIfConfigured(testCorrelationID, registrationUtil)
|
||||
assert.Equal(t, []logrus.Level{logrus.WarnLevel, logrus.ErrorLevel, logrus.PanicLevel, logrus.FatalLevel}, registrationUtil.Hook.Levels())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestANSHook_setupEventTemplate(t *testing.T) {
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
t.Run("setup event without customer template", func(t *testing.T) {
|
||||
event, _ := setupEventTemplate("", defaultCorrelationID())
|
||||
assert.Equal(t, defaultEvent(), event, "unexpected event data")
|
||||
})
|
||||
t.Run("setup event from default customer template", func(t *testing.T) {
|
||||
event, _ := setupEventTemplate(customerEventString(), defaultCorrelationID())
|
||||
assert.Equal(t, defaultEvent(), event, "unexpected event data")
|
||||
})
|
||||
t.Run("setup event with category", func(t *testing.T) {
|
||||
event, _ := setupEventTemplate(customerEventString(map[string]interface{}{"Category": "ALERT"}), defaultCorrelationID())
|
||||
assert.Equal(t, "", event.Category, "unexpected category data")
|
||||
})
|
||||
t.Run("setup event with severity", func(t *testing.T) {
|
||||
event, _ := setupEventTemplate(customerEventString(map[string]interface{}{"Severity": "WARNING"}), defaultCorrelationID())
|
||||
assert.Equal(t, "", event.Severity, "unexpected severity data")
|
||||
})
|
||||
t.Run("setup event with invalid category", func(t *testing.T) {
|
||||
event, _ := setupEventTemplate(customerEventString(map[string]interface{}{"Category": "invalid"}), defaultCorrelationID())
|
||||
assert.Equal(t, "", event.Category, "unexpected category data")
|
||||
})
|
||||
t.Run("setup event with priority", func(t *testing.T) {
|
||||
event, _ := setupEventTemplate(customerEventString(map[string]interface{}{"Priority": "1"}), defaultCorrelationID())
|
||||
assert.Equal(t, 1, event.Priority, "unexpected priority data")
|
||||
})
|
||||
t.Run("setup event with omitted priority 0", func(t *testing.T) {
|
||||
event, err := setupEventTemplate(customerEventString(map[string]interface{}{"Priority": "0"}), defaultCorrelationID())
|
||||
assert.Equal(t, nil, err, "priority 0 must not fail")
|
||||
assert.Equal(t, 0, event.Priority, "unexpected priority data")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("bad", func(t *testing.T) {
|
||||
t.Run("setup event with invalid priority", func(t *testing.T) {
|
||||
_, err := setupEventTemplate(customerEventString(map[string]interface{}{"Priority": "-1"}), defaultCorrelationID())
|
||||
assert.Contains(t, err.Error(), "Priority must be 1 or greater", "unexpected error text")
|
||||
})
|
||||
t.Run("setup event with invalid variable name", func(t *testing.T) {
|
||||
_, err := setupEventTemplate(customerEventString(map[string]interface{}{"Invalid": "invalid"}), defaultCorrelationID())
|
||||
assert.Contains(t, err.Error(), "could not be unmarshalled", "unexpected error text")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestANSHook_registerANSHook(t *testing.T) {
|
||||
|
||||
os.Setenv("PIPER_ansHookServiceKey", defaultServiceKeyJSON)
|
||||
|
||||
t.Run("good", func(t *testing.T) {
|
||||
t.Run("No service key skips registration", func(t *testing.T) {
|
||||
util := createRegUtil()
|
||||
os.Setenv("PIPER_ansHookServiceKey", "")
|
||||
assert.Nil(t, registerANSHookIfConfigured(testCorrelationID, util), "registration did not nil")
|
||||
assert.Nil(t, util.Hook, "registration registered hook")
|
||||
os.Setenv("PIPER_ansHookServiceKey", defaultServiceKeyJSON)
|
||||
})
|
||||
t.Run("Default registration registers hook and secret", func(t *testing.T) {
|
||||
util := createRegUtil()
|
||||
assert.Nil(t, registerANSHookIfConfigured(testCorrelationID, util), "registration did not nil")
|
||||
assert.NotNil(t, util.Hook, "registration didnt register hook")
|
||||
assert.NotNil(t, util.Secret, "registration didnt register secret")
|
||||
})
|
||||
t.Run("Registration with default template", func(t *testing.T) {
|
||||
util := createRegUtil()
|
||||
os.Setenv("PIPER_ansEventTemplate", customerEventString())
|
||||
assert.Nil(t, registerANSHookIfConfigured(testCorrelationID, util), "registration did not return nil")
|
||||
assert.Equal(t, customerEvent(), util.Hook.eventTemplate, "unexpected event template data")
|
||||
os.Setenv("PIPER_ansEventTemplate", "")
|
||||
})
|
||||
t.Run("Registration with customized template", func(t *testing.T) {
|
||||
util := createRegUtil()
|
||||
os.Setenv("PIPER_ansEventTemplate", customerEventString(map[string]interface{}{"Priority": "123"}))
|
||||
assert.Nil(t, registerANSHookIfConfigured(testCorrelationID, util), "registration did not return nil")
|
||||
assert.Equal(t, 123, util.Hook.eventTemplate.Priority, "unexpected event template data")
|
||||
os.Setenv("PIPER_ansEventTemplate", "")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("bad", func(t *testing.T) {
|
||||
t.Run("Fails on check error", func(t *testing.T) {
|
||||
util := createRegUtil(map[string]interface{}{"CheckErr": fmt.Errorf("check failed")})
|
||||
err := registerANSHookIfConfigured(testCorrelationID, util)
|
||||
assert.Contains(t, err.Error(), "check failed", "unexpected error text")
|
||||
})
|
||||
|
||||
t.Run("Fails on validation error", func(t *testing.T) {
|
||||
os.Setenv("PIPER_ansEventTemplate", customerEventString(map[string]interface{}{"Priority": "-1"}))
|
||||
err := registerANSHookIfConfigured(testCorrelationID, createRegUtil())
|
||||
assert.Contains(t, err.Error(), "Priority must be 1 or greater", "unexpected error text")
|
||||
os.Setenv("PIPER_ansEventTemplate", "")
|
||||
})
|
||||
|
||||
})
|
||||
os.Setenv("PIPER_ansHookServiceKey", "")
|
||||
}
|
||||
|
||||
func TestANSHook_Fire(t *testing.T) {
|
||||
registrationUtil := createRegUtil()
|
||||
ansHook := &ANSHook{
|
||||
client: registrationUtil,
|
||||
}
|
||||
|
||||
t.Run("Straight forward test", func(t *testing.T) {
|
||||
ansHook.eventTemplate = defaultEvent()
|
||||
require.NoError(t, ansHook.Fire(defaultLogrusEntry()), "error is not nil")
|
||||
assert.Equal(t, defaultResultingEvent(), registrationUtil.Event, "error category tag is not as expected")
|
||||
registrationUtil.clearEventTemplate()
|
||||
})
|
||||
t.Run("Set error category", func(t *testing.T) {
|
||||
SetErrorCategory(ErrorTest)
|
||||
ansHook.eventTemplate = defaultEvent()
|
||||
require.NoError(t, ansHook.Fire(defaultLogrusEntry()), "error is not nil")
|
||||
assert.Equal(t, "test", registrationUtil.Event.Tags["pipeline:errorCategory"], "error category tag is not as expected")
|
||||
SetErrorCategory(ErrorUndefined)
|
||||
registrationUtil.clearEventTemplate()
|
||||
})
|
||||
t.Run("Event already set", func(t *testing.T) {
|
||||
alreadySetEvent := ans.Event{EventType: "My event type", Subject: "My subject line", Tags: map[string]interface{}{"Some": 1.0, "Additional": "a string", "Tags": true}}
|
||||
ansHook.eventTemplate = mergeEvents(t, defaultEvent(), alreadySetEvent)
|
||||
require.NoError(t, ansHook.Fire(defaultLogrusEntry()), "error is not nil")
|
||||
assert.Equal(t, mergeEvents(t, defaultResultingEvent(), alreadySetEvent), registrationUtil.Event, "event is not as expected")
|
||||
registrationUtil.clearEventTemplate()
|
||||
})
|
||||
t.Run("Log entries should not affect each other", func(t *testing.T) {
|
||||
ansHook.eventTemplate = defaultEvent()
|
||||
SetErrorCategory(ErrorTest)
|
||||
require.NoError(t, ansHook.Fire(defaultLogrusEntry()), "error is not nil")
|
||||
assert.Equal(t, "test", registrationUtil.Event.Tags["pipeline:errorCategory"], "error category tag is not as expected")
|
||||
SetErrorCategory(ErrorUndefined)
|
||||
require.NoError(t, ansHook.Fire(defaultLogrusEntry()), "error is not nil")
|
||||
assert.Nil(t, registrationUtil.Event.Tags["pipeline:errorCategory"], "error category tag is not nil")
|
||||
registrationUtil.clearEventTemplate()
|
||||
})
|
||||
t.Run("White space messages should not send", func(t *testing.T) {
|
||||
ansHook.eventTemplate = defaultEvent()
|
||||
entryWithSpaceMessage := defaultLogrusEntry()
|
||||
entryWithSpaceMessage.Message = " "
|
||||
require.NoError(t, ansHook.Fire(entryWithSpaceMessage), "error is not nil")
|
||||
assert.Equal(t, ans.Event{}, registrationUtil.Event, "event is not empty")
|
||||
})
|
||||
t.Run("Should not fire twice", func(t *testing.T) {
|
||||
ansHook.eventTemplate = defaultEvent()
|
||||
ansHook.firing = true
|
||||
require.EqualError(t, ansHook.Fire(defaultLogrusEntry()), "ANS hook has already been fired", "error message is not as expected")
|
||||
ansHook.firing = false
|
||||
})
|
||||
t.Run("No stepName set", func(t *testing.T) {
|
||||
ansHook.eventTemplate = defaultEvent()
|
||||
logrusEntryWithoutStepName := defaultLogrusEntry()
|
||||
logrusEntryWithoutStepName.Data = map[string]interface{}{}
|
||||
require.NoError(t, ansHook.Fire(logrusEntryWithoutStepName), "error is not nil")
|
||||
assert.Equal(t, "n/a", registrationUtil.Event.Tags["pipeline:stepName"], "event step name tag is not as expected.")
|
||||
assert.Equal(t, "Pipeline step 'n/a' sends 'WARNING'", registrationUtil.Event.Subject, "event subject is not as expected")
|
||||
registrationUtil.clearEventTemplate()
|
||||
})
|
||||
}
|
||||
|
||||
const testCorrelationID = "1234"
|
||||
const defaultServiceKeyJSON = `{"url": "https://my.test.backend", "client_id": "myTestClientID", "client_secret": "super secret", "oauth_url": "https://my.test.oauth.provider"}`
|
||||
|
||||
var defaultTime = time.Date(2001, 2, 3, 4, 5, 6, 7, time.UTC)
|
||||
|
||||
func defaultCorrelationID() string {
|
||||
return testCorrelationID
|
||||
}
|
||||
|
||||
func merge(base, overlay map[string]interface{}) map[string]interface{} {
|
||||
|
||||
result := map[string]interface{}{}
|
||||
|
||||
if base == nil {
|
||||
base = map[string]interface{}{}
|
||||
}
|
||||
|
||||
for key, value := range base {
|
||||
result[key] = value
|
||||
}
|
||||
|
||||
for key, value := range overlay {
|
||||
if val, ok := value.(map[string]interface{}); ok {
|
||||
if valBaseKey, ok := base[key].(map[string]interface{}); !ok {
|
||||
result[key] = merge(map[string]interface{}{}, val)
|
||||
} else {
|
||||
result[key] = merge(valBaseKey, val)
|
||||
}
|
||||
} else {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func customerEvent(params ...interface{}) ans.Event {
|
||||
event := ans.Event{}
|
||||
json.Unmarshal([]byte(customerEventString(params)), &event)
|
||||
return event
|
||||
}
|
||||
|
||||
func customerEventString(params ...interface{}) string {
|
||||
event := defaultEvent()
|
||||
|
||||
additionalFields := make(map[string]interface{})
|
||||
if len(params) > 0 {
|
||||
for i := 0; i < len(params); i++ {
|
||||
additionalFields = merge(additionalFields, pokeObject(&event, params[i]))
|
||||
}
|
||||
}
|
||||
|
||||
// create json string from Event
|
||||
marshaled, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot marshal customer event: %v", err))
|
||||
}
|
||||
|
||||
// add non Event members to json string
|
||||
if len(additionalFields) > 0 {
|
||||
closingBraceIdx := bytes.LastIndexByte(marshaled, '}')
|
||||
for key, value := range additionalFields {
|
||||
var entry string
|
||||
switch value.(type) {
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid key value type: %v", key))
|
||||
case string:
|
||||
entry = `, "` + key + `": "` + value.(string) + `"`
|
||||
case int:
|
||||
entry = `, "` + key + `": "` + strconv.Itoa(value.(int)) + `"`
|
||||
}
|
||||
|
||||
add := []byte(entry)
|
||||
marshaled = append(marshaled[:closingBraceIdx], add...)
|
||||
}
|
||||
marshaled = append(marshaled, '}')
|
||||
}
|
||||
|
||||
return string(marshaled)
|
||||
}
|
||||
|
||||
type registrationUtilMock struct {
|
||||
ans.Client
|
||||
Event ans.Event
|
||||
ServiceKey ans.ServiceKey
|
||||
SendErr error
|
||||
CheckErr error
|
||||
Hook *ANSHook
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (m *registrationUtilMock) Send(event ans.Event) error {
|
||||
m.Event = event
|
||||
return m.SendErr
|
||||
}
|
||||
|
||||
func (m *registrationUtilMock) CheckCorrectSetup() error {
|
||||
return m.CheckErr
|
||||
}
|
||||
|
||||
func (m *registrationUtilMock) SetServiceKey(serviceKey ans.ServiceKey) {
|
||||
m.ServiceKey = serviceKey
|
||||
|
||||
}
|
||||
func (m *registrationUtilMock) registerHook(hook *ANSHook) {
|
||||
m.Hook = hook
|
||||
}
|
||||
|
||||
func (m *registrationUtilMock) registerSecret(secret string) {
|
||||
m.Secret = secret
|
||||
}
|
||||
|
||||
func (m *registrationUtilMock) clearEventTemplate() {
|
||||
m.Event = ans.Event{}
|
||||
}
|
||||
|
||||
func createRegUtil(params ...interface{}) *registrationUtilMock {
|
||||
|
||||
mock := registrationUtilMock{}
|
||||
if len(params) > 0 {
|
||||
for i := 0; i < len(params); i++ {
|
||||
pokeObject(&mock, params[i])
|
||||
}
|
||||
}
|
||||
return &mock
|
||||
}
|
||||
|
||||
func pokeObject(obj interface{}, param interface{}) map[string]interface{} {
|
||||
|
||||
additionalFields := make(map[string]interface{})
|
||||
|
||||
switch t := param.(type) {
|
||||
case map[string]interface{}:
|
||||
{
|
||||
m := param.(map[string]interface{})
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
for key, value := range m {
|
||||
f := v.FieldByName(key)
|
||||
|
||||
if f != (reflect.Value{}) {
|
||||
switch f.Kind() {
|
||||
case reflect.String:
|
||||
f.SetString(value.(string))
|
||||
case reflect.Int, reflect.Int64:
|
||||
switch t := value.(type) {
|
||||
case string:
|
||||
v, _ := strconv.Atoi(value.(string))
|
||||
f.SetInt(int64(v))
|
||||
case int:
|
||||
f.SetInt(int64((value).(int)))
|
||||
case int64:
|
||||
f.SetInt(value.(int64))
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type: %v of key:%v value:%v\n", t, key, value))
|
||||
}
|
||||
case reflect.Map:
|
||||
switch value.(type) {
|
||||
case map[string]string, map[string]interface{}:
|
||||
if value != nil {
|
||||
val := reflect.ValueOf(value)
|
||||
f.Set(val)
|
||||
} else {
|
||||
f.Set(reflect.Zero(f.Type()))
|
||||
}
|
||||
}
|
||||
case reflect.Interface:
|
||||
if value != nil {
|
||||
val := reflect.ValueOf(value)
|
||||
f.Set(val)
|
||||
} else {
|
||||
f.Set(reflect.Zero(f.Type()))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported field type: %v of key:%v value:%v\n", f.Kind(), key, value))
|
||||
}
|
||||
} else {
|
||||
additionalFields[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
p := param.([]interface{})
|
||||
for i := 0; i < len(p); i++ {
|
||||
pokeObject(obj, p[i])
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported paramter type: %v", t))
|
||||
}
|
||||
return additionalFields
|
||||
}
|
||||
|
||||
func defaultEvent() ans.Event {
|
||||
event := ans.Event{
|
||||
EventType: "Piper",
|
||||
Tags: map[string]interface{}{
|
||||
"ans:correlationId": testCorrelationID,
|
||||
"ans:sourceEventId": testCorrelationID,
|
||||
},
|
||||
Resource: &ans.Resource{
|
||||
ResourceType: "Pipeline",
|
||||
ResourceName: "Pipeline",
|
||||
},
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func defaultResultingEvent() ans.Event {
|
||||
return customerEvent(map[string]interface{}{
|
||||
"EventTimestamp": defaultTime.Unix(),
|
||||
"Severity": "WARNING",
|
||||
"Category": "ALERT",
|
||||
"Subject": "Pipeline step 'testStep' sends 'WARNING'",
|
||||
"Body": "my log message",
|
||||
"Tags": map[string]interface{}{
|
||||
"ans:correlationId": "1234",
|
||||
"ans:sourceEventId": "1234",
|
||||
"pipeline:stepName": "testStep",
|
||||
"pipeline:logLevel": "warning",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func defaultLogrusEntry() *logrus.Entry {
|
||||
return &logrus.Entry{
|
||||
Level: logrus.WarnLevel,
|
||||
Time: defaultTime,
|
||||
Message: "my log message",
|
||||
Data: map[string]interface{}{"stepName": "testStep"},
|
||||
}
|
||||
}
|
||||
|
||||
func mergeEvents(t *testing.T, event1, event2 ans.Event) ans.Event {
|
||||
event2JSON, err := json.Marshal(event2)
|
||||
require.NoError(t, err)
|
||||
err = event1.MergeWithJSON(event2JSON)
|
||||
require.NoError(t, err)
|
||||
return event1
|
||||
}
|
Reference in New Issue
Block a user