Skip to content

Commit

Permalink
Introduce checks for not supported annotations for Hints based autodi…
Browse files Browse the repository at this point in the history
…scover of Elastic Agent (#81)

This PR relates to elastic/elastic-agent#3064
and provides the checks for the supported hints.
  • Loading branch information
gizas authored Mar 13, 2024
1 parent b3b1fa7 commit 7490558
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 9 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).


[0.6.7]: https://github.com/elastic/elastic-agent-autodiscover/compare/v0.6.2...v0.6.7

## [0.6.9]

### Changed

- Update GenerateHints function to check supported list of hints


[0.6.9]: https://github.com/elastic/elastic-agent-autodiscover/compare/v0.6.8...v0.6.9
27 changes: 25 additions & 2 deletions utils/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,16 +202,22 @@ func IsDisabled(hints mapstr.M, key string) bool {
}

// GenerateHints parses annotations based on a prefix and sets up hints that can be picked up by individual Beats.
func GenerateHints(annotations mapstr.M, container, prefix string) mapstr.M {
func GenerateHints(annotations mapstr.M, container, prefix string, allSupportedHints []string) (mapstr.M, []string) {
hints := mapstr.M{}
var incorrecthints []string
found := false
if rawEntries, err := annotations.GetValue(prefix); err == nil {
if entries, ok := rawEntries.(mapstr.M); ok {
for key, rawValue := range entries {

// 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])
//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)

// Insert only if there is no entry already. container level annotations take
// higher priority.
if _, err := hints.GetValue(hintKey); err != nil {
Expand All @@ -233,6 +239,8 @@ func GenerateHints(annotations mapstr.M, container, prefix string) mapstr.M {
if strings.HasPrefix(hintKey, container) {
// Split the key to get part[1] to be the hint
parts := strings.Split(hintKey, "/")
//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)
if len(parts) == 2 {
// key will be the hint type
hintKey := fmt.Sprintf("%s.%s", key, parts[1])
Expand All @@ -244,11 +252,14 @@ func GenerateHints(annotations mapstr.M, container, prefix string) mapstr.M {
}
}
}
if !found {
incorrecthints = append(incorrecthints, key)
}
}
}
}

return hints
return hints, incorrecthints
}

// GetHintsAsList gets a set of hints and tries to convert them into a list of hints
Expand Down Expand Up @@ -289,3 +300,15 @@ func GetHintsAsList(hints mapstr.M, key string) []mapstr.M {
}
return configs
}

// checkSupportedHints gets a specific hint annotation and compares it with the supported list of hints
func checkSupportedHints(actualannotation string, allSupportedHints []string) bool {
found := false
for _, checksupported := range allSupportedHints {
if actualannotation == checksupported {
found = true
break
}
}
return found
}
34 changes: 27 additions & 7 deletions utils/hints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,21 @@ 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"}

tests := []struct {
annotations map[string]string
result mapstr.M
name string
annotations map[string]string
result mapstr.M
expectedIncorrectHints int // We set the number of hints that will be marked as incorrect and wont be included in the acceptable supported list
}{
// Empty annotations should return empty hints
//Empty annotations should return empty hints
{
annotations: map[string]string{},
result: mapstr.M{},
name: "Empty_Annotations",
annotations: map[string]string{},
result: mapstr.M{},
expectedIncorrectHints: 0,
},

// Scenarios being tested:
Expand All @@ -68,6 +75,7 @@ func TestGenerateHints(t *testing.T) {
// not.to.include must not be part of hints
// period is annotated at both container and pod level. Container level value must be in hints
{
name: "Logs_multiline_and_metrics",
annotations: map[string]string{
"co.elastic.logs/multiline.pattern": "^test",
"co.elastic.logs/json.keys_under_root": "true",
Expand All @@ -91,13 +99,15 @@ func TestGenerateHints(t *testing.T) {
"period": "15s",
},
},
expectedIncorrectHints: 0,
},
// Scenarios being tested:
// logs/multiline.pattern must be a nested mapstr.M under hints.logs
// metrics/module must be found in hints.metrics
// not.to.include must not be part of hints
// metrics/metrics_path must be found in hints.metrics
{
name: "Logs_multiline_and_metrics_with_metrics_path",
annotations: map[string]string{
"co.elastic.logs/multiline.pattern": "^test",
"co.elastic.metrics/module": "prometheus",
Expand All @@ -107,6 +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
"not.to.include": "true",
},
result: mapstr.M{
Expand All @@ -115,6 +126,7 @@ func TestGenerateHints(t *testing.T) {
"pattern": "^test",
},
},
"hints": mapstr.M{"steam": "stdout"},
"metrics": mapstr.M{
"module": "prometheus",
"period": "15s",
Expand All @@ -123,14 +135,15 @@ func TestGenerateHints(t *testing.T) {
"password": "pass",
},
},
expectedIncorrectHints: 1, // Due to co.elastic.hints/steam and not co.elastic.hints/stream
},
// Scenarios being tested:
// have co.elastic.logs/disable set to false.
// logs/multiline.pattern must be a nested mapstr.M under hints.logs
// metrics/module must be found in hints.metrics
// not.to.include must not be part of hints
// period is annotated at both container and pod level. Container level value must be in hints
{
name: "Logs_multiline_and_metrics",
annotations: map[string]string{
"co.elastic.logs/multiline.pattern": "^test",
"co.elastic.metrics/module": "prometheus",
Expand All @@ -150,6 +163,7 @@ func TestGenerateHints(t *testing.T) {
"period": "15s",
},
},
expectedIncorrectHints: 0,
},
// Scenarios being tested:
// have co.elastic.logs/disable set to false.
Expand All @@ -158,6 +172,7 @@ func TestGenerateHints(t *testing.T) {
// not.to.include must not be part of hints
// period is annotated at both container and pod level. Container level value must be in hints
{
name: "Logs_disabled_false_and_metrics",
annotations: map[string]string{
"co.elastic.logs/disable": "false",
"co.elastic.logs/multiline.pattern": "^test",
Expand All @@ -179,6 +194,7 @@ func TestGenerateHints(t *testing.T) {
"period": "15s",
},
},
expectedIncorrectHints: 0,
},
// Scenarios being tested:
// have co.elastic.logs/disable set to true.
Expand All @@ -187,6 +203,7 @@ func TestGenerateHints(t *testing.T) {
// not.to.include must not be part of hints
// period is annotated at both container and pod level. Container level value must be in hints
{
name: "Logs_disabled_true_and_metrics",
annotations: map[string]string{
"co.elastic.logs/disable": "true",
"co.elastic.logs/multiline.pattern": "^test",
Expand All @@ -208,6 +225,7 @@ func TestGenerateHints(t *testing.T) {
"period": "15s",
},
},
expectedIncorrectHints: 0,
},
}

Expand All @@ -219,7 +237,9 @@ func TestGenerateHints(t *testing.T) {
continue
}
}
assert.Equal(t, test.result, GenerateHints(annMap, "foobar", "co.elastic"))
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)
}
}
func TestGetHintsAsList(t *testing.T) {
Expand Down

0 comments on commit 7490558

Please sign in to comment.