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

Logshints #83

Merged
merged 28 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9bd3cb6
first commit with checks for hints vocabulary
gizas Mar 6, 2024
d922ef0
adding checks also to containers
gizas Mar 6, 2024
1795076
returning list with unsupported hints
gizas Mar 7, 2024
9306516
returning list with unsupported hints and fixing test to check those
gizas Mar 7, 2024
98d9808
adding function for duplicate code
gizas Mar 7, 2024
60286d4
udpating comment
gizas Mar 7, 2024
f22d479
fixing test error
gizas Mar 7, 2024
3c1e817
fixing test error
gizas Mar 7, 2024
516584a
fixing test error
gizas Mar 7, 2024
72a0cc5
Updating test definitions
gizas Mar 7, 2024
cea860a
adding expectedIncorrectHints on test cases for more clarity
gizas Mar 12, 2024
3c291a5
adding expectedIncorrectHints on test cases for more clarity
gizas Mar 12, 2024
95ed1dd
removing const from tests and added only array
gizas Mar 13, 2024
30f0e92
updating library to support cheks for multiple data_streams and metrc…
gizas Mar 14, 2024
d83ee12
updating chaneglog
gizas Mar 14, 2024
4c20caf
merging with main
gizas Mar 14, 2024
dac26f6
correcting typos
gizas Mar 14, 2024
0f606d7
Adding tests for metricsets and for enumerations
gizas Mar 27, 2024
da071f3
Merge branch 'main' of github.com:elastic/elastic-agent-autodiscover …
gizas Mar 27, 2024
7470002
Updating Changelog
gizas Mar 27, 2024
5ed7de7
Adding test for merricset enumeration
gizas Mar 27, 2024
5574401
Adding comments
gizas Mar 27, 2024
c68f69f
Correcting tests
gizas Mar 27, 2024
b651a15
correcting lint errors
gizas Mar 27, 2024
aa4dbf3
Adding more obvious errors in tests
gizas Apr 4, 2024
96897fd
removing uneeded if checks
gizas Apr 9, 2024
2735dd1
removing uneeded if checks
gizas Apr 9, 2024
5c28313
adding comment
gizas Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).


[0.6.9]: https://github.com/elastic/elastic-agent-autodiscover/compare/v0.6.8...v0.6.9

## [0.6.11]

### Changed

- Enhance GenerateHints function to check supported list of hints for multiple datastreams and metricsets


[0.6.10]: https://github.com/elastic/elastic-agent-autodiscover/compare/v0.6.10...v0.6.11
116 changes: 108 additions & 8 deletions utils/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package utils
import (
"encoding/json"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -205,18 +206,63 @@ func IsDisabled(hints mapstr.M, key string) bool {
func GenerateHints(annotations mapstr.M, container, prefix string, allSupportedHints []string) (mapstr.M, []string) {
hints := mapstr.M{}
var incorrecthints []string
found := false
var incorrecthint string
var digitCheck = regexp.MustCompile(`^[0-9]+$`)

if rawEntries, err := annotations.GetValue(prefix); err == nil {
if entries, ok := rawEntries.(mapstr.M); ok {
for key, rawValue := range entries {

//Start of Annotation Check: whether the annotation follows the supported format and vocabulary. The check happens for annotations that have prefix co.elastic
datastreamlist := GetHintAsList(entries, logName+"/"+"data_streams", "")
// We check if multiple data_streams are defined and we retrieve the hints per data_stream. Only applicable in elastic-agent
// See Metrics_apache_package_and_specific_config_per_datastream test case in hints_test.go
for _, stream := range datastreamlist {
allSupportedHints = append(allSupportedHints, stream)
incorrecthints = checkSupportedHintsSets(annotations, prefix, stream, logName, allSupportedHints, incorrecthints)
}
metricsetlist := GetHintAsList(entries, "metrics"+"/"+"metricsets", "")
// We check if multiple metrcisets are defined and we retrieve the hints per metricset. Only applicable in beats
//See Metrics_istio_module_and_specific_config_per_metricset test case in hints_test.go
for _, metric := range metricsetlist {
allSupportedHints = append(allSupportedHints, metric)
incorrecthints = checkSupportedHintsSets(annotations, prefix, metric, "metrics", allSupportedHints, incorrecthints)
}
//End of Annotation Check

for key, rawValue := range entries {
enumeratedmodules := []string{}
// If there are top level hints like co.elastic.logs/ then just add the values after the /
// Only consider namespaced annotations
parts := strings.Split(key, "/")
if len(parts) == 2 {
hintKey := fmt.Sprintf("%s.%s", parts[0], parts[1])

checkdigit := digitCheck.MatchString(parts[1]) // With this regex we check if enumeration for modules is provided
if checkdigit {
allSupportedHints = append(allSupportedHints, parts[1])

specificlist, _ := entries.GetValue(key)
if specificentries, ok := specificlist.(mapstr.M); ok {
for keyspec := range specificentries {
// enumeratedmodules will be populated only in cases we have module enumeration, like:
// "co.elastic.metrics/1.module": "prometheus",
// "co.elastic.metrics/2.module": "istiod",
enumeratedmodules = append(enumeratedmodules, keyspec)
}
}
}

// We check if multiple metrcisets are defined and we retrieve the hints per metricset. Only applicable in beats
// See Metrics_multiple_modules_and_specific_config_per_module test case in hints_test.go
for _, metric := range enumeratedmodules {
_, incorrecthint = checkSupportedHints(metric, fmt.Sprintf("%s.%s", key, metric), allSupportedHints)
if incorrecthint != "" {
incorrecthints = append(incorrecthints, incorrecthint)
}

}
//We check whether the provided annotation follows the supported format and vocabulary. The check happens for annotations that have prefix co.elastic
found = checkSupportedHints(parts[1], allSupportedHints)
_, incorrecthint = checkSupportedHints(parts[1], key, allSupportedHints)

// Insert only if there is no entry already. container level annotations take
// higher priority.
Expand All @@ -239,8 +285,36 @@ func GenerateHints(annotations mapstr.M, container, prefix string, allSupportedH
if strings.HasPrefix(hintKey, container) {
// Split the key to get part[1] to be the hint
parts := strings.Split(hintKey, "/")

checkdigit := digitCheck.MatchString(parts[1]) // With this regex we check if enumeration for modules is provided
if checkdigit {
allSupportedHints = append(allSupportedHints, parts[1])

specificlist, _ := entries.GetValue(key)
if specificentries, ok := specificlist.(mapstr.M); ok {
for keyspec := range specificentries {
// enumeratedmodules will be populated only in cases we have module enumeration, like:
// "co.elastic.metrics/1.module": "prometheus",
// "co.elastic.metrics/2.module": "istiod",
enumeratedmodules = append(enumeratedmodules, keyspec)
}
}
}

// We check if multiple metrcisets are defined and we retrieve the hints per metricset. Only applicable in beats
// See Metrics_multiple_modules_and_specific_config_per_module test case in hints_test.go
for _, metric := range enumeratedmodules {
_, incorrecthint = checkSupportedHints(metric, fmt.Sprintf("%s.%s", key, metric), allSupportedHints)
if incorrecthint != "" {
incorrecthints = append(incorrecthints, incorrecthint)
}

}
//We check whether the provided annotation follows the supported format and vocabulary. The check happens for annotations that have prefix co.elastic
found = checkSupportedHints(parts[1], allSupportedHints)
_, incorrecthint = checkSupportedHints(parts[1], key, allSupportedHints)

//end of check

if len(parts) == 2 {
// key will be the hint type
hintKey := fmt.Sprintf("%s.%s", key, parts[1])
Expand All @@ -252,8 +326,8 @@ func GenerateHints(annotations mapstr.M, container, prefix string, allSupportedH
}
}
}
if !found {
incorrecthints = append(incorrecthints, key)
if incorrecthint != "" {
incorrecthints = append(incorrecthints, incorrecthint)
}
}
}
Expand Down Expand Up @@ -302,13 +376,39 @@ func GetHintsAsList(hints mapstr.M, key string) []mapstr.M {
}

// checkSupportedHints gets a specific hint annotation and compares it with the supported list of hints
func checkSupportedHints(actualannotation string, allSupportedHints []string) bool {
func checkSupportedHints(actualannotation, key string, allSupportedHints []string) (bool, string) {
found := false
var incorrecthint string

for _, checksupported := range allSupportedHints {
if actualannotation == checksupported {
found = true
break
}

}
if !found {
incorrecthint = key
}
return found
return found, incorrecthint
}

// checkSupportedHintsSets gest the data_streams or metricset lists that are defined. Searches inside specific list and returns the unsupported list of hints found
// This function will merge the incorrect hints found in metricsets of data_streams with rest incorrect hints
func checkSupportedHintsSets(annotations mapstr.M, prefix, stream, kind string, allSupportedHints, incorrecthints []string) []string {
var incorrecthint string

if hintsindatastream, err := annotations.GetValue(prefix + "." + kind + "/" + stream); err == nil {
if hintsentries, ok := hintsindatastream.(mapstr.M); ok {
for hintkey := range hintsentries {
_, incorrecthint = checkSupportedHints(hintkey, kind+"/"+stream+"."+hintkey, allSupportedHints)
if incorrecthint != "" {
incorrecthints = append(incorrecthints, incorrecthint)
}
}

}
}

return incorrecthints
}
82 changes: 78 additions & 4 deletions utils/hints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestGetProcessors(t *testing.T) {

func TestGenerateHints(t *testing.T) {

var allSupportedHints = []string{"enabled", "module", "integration", "datas_treams", "host", "period", "timeout", "metrics_path", "username", "password", "stream", "processors", "multiline", "json", "disable"}
var allSupportedHints = []string{"enabled", "package", "module", "integration", "data_streams", "metricsets", "host", "period", "timeout", "metrics_path", "username", "password", "stream", "processors", "multiline", "json", "disable"}

tests := []struct {
name string
Expand Down Expand Up @@ -117,7 +117,7 @@ func TestGenerateHints(t *testing.T) {
"co.elastic.metrics/password": "pass",
"co.elastic.metrics.foobar/period": "15s",
"co.elastic.metrics.foobar1/period": "15s",
"co.elastic.hints/steam": "stdout", // On purpose this added with typo
"co.elastic.hints/streamssssssssss": "stdout", // On purpose this added with typo
"not.to.include": "true",
},
result: mapstr.M{
Expand All @@ -126,7 +126,7 @@ func TestGenerateHints(t *testing.T) {
"pattern": "^test",
},
},
"hints": mapstr.M{"steam": "stdout"},
"hints": mapstr.M{"streamssssssssss": "stdout"},
"metrics": mapstr.M{
"module": "prometheus",
"period": "15s",
Expand All @@ -135,7 +135,7 @@ func TestGenerateHints(t *testing.T) {
"password": "pass",
},
},
expectedIncorrectHints: 1, // Due to co.elastic.hints/steam and not co.elastic.hints/stream
expectedIncorrectHints: 1, // Due to co.elastic.hints/streamsteamssssssssss
},
// Scenarios being tested:
// logs/multiline.pattern must be a nested mapstr.M under hints.logs
Expand Down Expand Up @@ -227,6 +227,79 @@ func TestGenerateHints(t *testing.T) {
},
expectedIncorrectHints: 0,
},
// Scenarios being tested:
// have co.elastic.hints/package set.
// Define multiple co.elastic.hints/data_streams and also specific configuration for each one
// Typo errors introduced for "co.elastic.hints/access.streams" and "co.elastic.hints/error.streams"
{
name: "Metrics_apache_package_and_specific_config_per_datastream",
annotations: map[string]string{
"co.elastic.hints/package": "apache",
"co.elastic.hints/data_streams": "access,error",
"co.elastic.hints/access.period": "5m",
"co.elastic.hints/access.streamssssssssss": "stdout", // On purpose this added with typo
"co.elastic.hints/error.period": "5m",
"co.elastic.hints/error.streamssssssssss": "stderr", // On purpose this added with typo
},
result: mapstr.M{
"hints": mapstr.M{
"data_streams": "access,error",
"access": mapstr.M{"period": "5m", "streamssssssssss": "stdout"},
"error": mapstr.M{"period": "5m", "streamssssssssss": "stderr"},
"package": "apache",
}},
expectedIncorrectHints: 2, // Due to co.elastic.hints/access.streamssssssssss and co.elastic.hints/error.streamssssssssss typo errors
},
// Scenarios being tested:
// have co.elastic.metrics/module set.
// Define multiple co.elastic.hints/data_streams and also specific configuration for each one
// A typo error introduced for "co.elastic.metrics/istiod.streams"
{
name: "Metrics_istio_module_and_specific_config_per_metricset",
annotations: map[string]string{
"co.elastic.metrics/module": "istio",
"co.elastic.metrics/metricsets": "istiod,proxy",
"co.elastic.metrics/istiod.period": "5m",
"co.elastic.metrics/istiod.streamssssssssss": "stdout", // On purpose this added with typo
"co.elastic.metrics/proxy.period": "5m",
"co.elastic.metrics/proxy.stream": "stderr",
},
result: mapstr.M{
"metrics": mapstr.M{
"metricsets": "istiod,proxy",
"istiod": mapstr.M{"period": "5m", "streamssssssssss": "stdout"},
"proxy": mapstr.M{"period": "5m", "stream": "stderr"},
"module": "istio",
}},
expectedIncorrectHints: 1, // Due to co.elastic.metrics/istiod.streamssssssssss
},
// Scenarios being tested:
// have co.elastic.metrics/module set for multiple enumerations.
// Define different hints for each one enumeration
// A typo error introduced for "co.elastic.metrics/1.periods" and "co.elastic.metrics/2.streams"
{
name: "Metrics_multiple_modules_and_specific_config_per_module",
annotations: map[string]string{
"co.elastic.metrics/1.module": "prometheus",
"co.elastic.metrics/1.periodssssssssss": "15s", // On purpose this added with typo
"co.elastic.metrics/2.module": "istiod",
"co.elastic.metrics/2.period": "15s",
"co.elastic.metrics/2.streamssssssssss": "stderr", // On purpose this added with typo
},
result: mapstr.M{
"metrics": mapstr.M{
"1": mapstr.M{
"module": "prometheus",
"periodssssssssss": "15s",
},
"2": mapstr.M{
"module": "istiod",
"period": "15s",
"streamssssssssss": "stderr",
},
}},
expectedIncorrectHints: 2, // Due to co.elastic.metrics/1.periodssssssssss and co.elastic.metrics/2.streamssssssssss typo errors
},
}

for _, test := range tests {
Expand All @@ -237,6 +310,7 @@ func TestGenerateHints(t *testing.T) {
continue
}
}

generateHints, incorrectHints := GenerateHints(annMap, "foobar", "co.elastic", allSupportedHints)
assert.Equal(t, test.expectedIncorrectHints, len(incorrectHints)) // We validate how many incorrect hints are provided per test case.
assert.Equal(t, test.result, generateHints)
Expand Down