@cryptotaxi247 / netdata-1 / commits / 1293c2a26

fix positive and negative matches on labels (#17290)

* fix positive and negative matches * fix typo * fix unit tests * unittest for rrdlabels pattern lists

Costa Tsaousis committed Mar 29, 2024 at 16:35 UTC 1293c2a2626b63c6eb59fb812952f894e9ffd327
6 files changed +115 -35
src/database/contexts/api_v1.c
+4 -4
@@ -131,13 +131,13 @@ static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void
131 if(before && (!ri->first_time_s || before < ri->first_time_s))
132 return 0;
133
134 - if(t_parent->chart_label_key && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_label_key,
135 - '\0', NULL))
134 + if(t_parent->chart_label_key && rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_label_key,
135 + '\0', NULL) != SP_MATCHED_POSITIVE)
136 return 0;
137
138 - if(t_parent->chart_labels_filter && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels,
138 + if(t_parent->chart_labels_filter && rrdlabels_match_simple_pattern_parsed(ri->rrdlabels,
139 t_parent->chart_labels_filter, ':',
140 - NULL))
140 + NULL) != SP_MATCHED_POSITIVE)
141 return 0;
142
143 time_t first_time_s = ri->first_time_s;
src/database/contexts/api_v2.c
+1 -1
@@ -473,7 +473,7 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso
473
474 size_t label_searches = 0;
475 if(unlikely(ri->rrdlabels && rrdlabels_entries(ri->rrdlabels) &&
476 - rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, q, ':', &label_searches))) {
476 + rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, q, ':', &label_searches) == SP_MATCHED_POSITIVE)) {
477 ctl->q.fts.searches += label_searches;
478 ctl->q.fts.char_searches += label_searches;
479 matched = FTS_MATCHED_LABEL;
src/database/contexts/query_target.c
+2 -2
@@ -732,12 +732,12 @@ static inline bool query_instance_matches_labels(
732 SIMPLE_PATTERN *labels_sp)
733 {
734
735 - if (chart_label_key_sp && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, chart_label_key_sp, '\0', NULL))
735 + if (chart_label_key_sp && rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, chart_label_key_sp, '\0', NULL) != SP_MATCHED_POSITIVE)
736 return false;
737
738 if (labels_sp) {
739 struct pattern_array *pa = pattern_array_add_simple_pattern(NULL, labels_sp, ':');
740 - bool found = pattern_array_label_match(pa, ri->rrdlabels, ':', NULL, rrdlabels_match_simple_pattern_parsed);
740 + bool found = pattern_array_label_match(pa, ri->rrdlabels, ':', NULL);
741 pattern_array_free(pa);
742 return found;
743 }
src/database/rrdlabels.c
+103 -22
@@ -977,7 +977,7 @@ int rrdlabels_walkthrough_read(RRDLABELS *labels, int (*callback)(const char *na
977 lfe_start_read(labels, lb, ls)
978 {
979 ret = callback(string2str(lb->index.key), string2str(lb->index.value), ls, data);
980 - if (ret < 0)
980 + if (ret != 0)
981 break;
982 }
983 lfe_done(labels);
@@ -1125,9 +1125,12 @@ static int simple_pattern_match_name_only_callback(const char *name, const char
1125
1126 // we return -1 to stop the walkthrough on first match
1127 t->searches++;
1128 - if(simple_pattern_matches(t->pattern, name)) return -1;
1128 + SIMPLE_PATTERN_RESULT rc = simple_pattern_matches_extract(t->pattern, name, NULL, 0);
1129
1130 - return 0;
1130 + if(rc == SP_MATCHED_NEGATIVE)
1131 + return -1;
1132 +
1133 + return rc == SP_MATCHED_POSITIVE;
1134 }
1135
1136 static int simple_pattern_match_name_and_value_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) {
@@ -1154,13 +1157,15 @@ static int simple_pattern_match_name_and_value_callback(const char *name, const
1157 *dst = '\0';
1158
1159 t->searches++;
1157 - if(simple_pattern_matches_length_extract(t->pattern, tmp, dst - tmp, NULL, 0) == SP_MATCHED_POSITIVE)
1160 + SIMPLE_PATTERN_RESULT rc = simple_pattern_matches_length_extract(t->pattern, tmp, dst - tmp, NULL, 0);
1161 +
1162 + if(rc == SP_MATCHED_NEGATIVE)
1163 return -1;
1164
1160 - return 0;
1165 + return rc == SP_MATCHED_POSITIVE ? 1 : 0;
1166 }
1167
1163 -bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches) {
1168 +SIMPLE_PATTERN_RESULT rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches) {
1169 if (!labels) return false;
1170
1171 struct simple_pattern_match_name_value t = {
@@ -1174,7 +1179,10 @@ bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pa
1179 if(searches)
1180 *searches = t.searches;
1181
1177 - return (ret == -1)?true:false;
1182 + if(ret < 0)
1183 + return SP_MATCHED_NEGATIVE;
1184 +
1185 + return (ret > 0)?SP_MATCHED_POSITIVE:SP_NOT_MATCHED;
1186 }
1187
1188 bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_pattern_txt) {
@@ -1191,11 +1199,11 @@ bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_patter
1199 }
1200 }
1201
1194 - bool ret = rrdlabels_match_simple_pattern_parsed(labels, pattern, equal, NULL);
1202 + SIMPLE_PATTERN_RESULT ret = rrdlabels_match_simple_pattern_parsed(labels, pattern, equal, NULL);
1203
1204 simple_pattern_free(pattern);
1205
1198 - return ret;
1206 + return ret == SP_MATCHED_POSITIVE;
1207 }
1208
1209
@@ -1388,8 +1396,7 @@ bool pattern_array_label_match(
1396 struct pattern_array *pa,
1397 RRDLABELS *labels,
1398 char eq,
1391 - size_t *searches,
1392 - bool (*callback_function)(RRDLABELS *, SIMPLE_PATTERN *, char, size_t *))
1399 + size_t *searches)
1400 {
1401 if (!pa || !labels)
1402 return true;
@@ -1398,14 +1405,23 @@ bool pattern_array_label_match(
1405 Word_t Index = 0;
1406 bool first_then_next = true;
1407 while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) {
1408 + // for each label key in the patterns array
1409 +
1410 struct pattern_array_item *pai = *Pvalue;
1402 - bool match = false;
1403 - for (Word_t i = 1; !match && i <= pai->size; i++) {
1411 + SIMPLE_PATTERN_RESULT match = SP_NOT_MATCHED ;
1412 + for (Word_t i = 1; i <= pai->size; i++) {
1413 + // for each pattern in the label key pattern list
1414 +
1415 if (!(Pvalue = JudyLGet(pai->JudyL, i, PJE0)) || !*Pvalue)
1416 continue;
1406 - match = callback_function(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches);
1417 +
1418 + match = rrdlabels_match_simple_pattern_parsed(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches);
1419 +
1420 + if(match != SP_NOT_MATCHED)
1421 + break;
1422 }
1408 - if (!match)
1423 +
1424 + if (match != SP_MATCHED_POSITIVE)
1425 return false;
1426 }
1427 return true;
@@ -1481,6 +1497,7 @@ void pattern_array_free(struct pattern_array *pa)
1497
1498 string_freez((STRING *)Index);
1499 (void) JudyLDel(&(pa->JudyL), Index, PJE0);
1500 + freez(pai);
1501 Index = 0;
1502 }
1503 freez(pa);
@@ -1669,7 +1686,7 @@ static int rrdlabels_walkthrough_index_read(RRDLABELS *labels, int (*callback)(c
1686 lfe_start_read(labels, lb, ls)
1687 {
1688 ret = callback(string2str(lb->index.key), string2str(lb->index.value), ls, index, data);
1672 - if (ret < 0)
1689 + if (ret != 0)
1690 break;
1691 index++;
1692 }
@@ -1705,25 +1722,25 @@ static int rrdlabels_unittest_pattern_check()
1722
1723 bool match;
1724 struct pattern_array *pa = pattern_array_add_key_value(NULL, "_module", "wrong_module", '=');
1708 - match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1725 + match = pattern_array_label_match(pa, labels, '=', NULL);
1726 // This should not match: _module in ("wrong_module")
1727 if (match)
1728 rc++;
1729
1730 pattern_array_add_key_value(pa, "_module", "disk_detection", '=');
1714 - match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1731 + match = pattern_array_label_match(pa, labels, '=', NULL);
1732 // This should match: _module in ("wrong_module","disk_detection")
1733 if (!match)
1734 rc++;
1735
1736 pattern_array_add_key_value(pa, "key1", "wrong_key1_value", '=');
1720 - match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1737 + match = pattern_array_label_match(pa, labels, '=', NULL);
1738 // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value")
1739 if (match)
1740 rc++;
1741
1742 pattern_array_add_key_value(pa, "key1", "value1", '=');
1726 - match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1743 + match = pattern_array_label_match(pa, labels, '=', NULL);
1744 // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1")
1745 if (!match)
1746 rc++;
@@ -1734,13 +1751,13 @@ static int rrdlabels_unittest_pattern_check()
1751 sp = simple_pattern_create("key3=*phant", SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true);
1752 pattern_array_add_lblkey_with_sp(pa, "key3", sp);
1753
1737 - match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1754 + match = pattern_array_label_match(pa, labels, '=', NULL);
1755 // This should match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") AND key2 in ("cat* !d*") AND key3 in ("*phant")
1756 if (!match)
1757 rc++;
1758
1759 rrdlabels_add(labels, "key3", "now_fail", RRDLABEL_SRC_CONFIG);
1743 - match = pattern_array_label_match(pa, labels, '=', NULL, rrdlabels_match_simple_pattern_parsed);
1760 + match = pattern_array_label_match(pa, labels, '=', NULL);
1761 // This should not match: _module in ("wrong_module","disk_detection") AND key1 in ("wrong_key1_value", "value1") AND key2 in ("cat* !d*") AND key3 in ("*phant")
1762 if (match)
1763 rc++;
@@ -1832,6 +1849,69 @@ static int rrdlabels_unittest_migrate_check()
1849 return rc;
1850 }
1851
1852 +struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input);
1853 +static int rrdlabels_unittest_check_pattern_list(RRDLABELS *labels, const char *pattern, bool expected) {
1854 + fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern);
1855 +
1856 + STRING *str = string_strdupz(pattern);
1857 + struct pattern_array *pa = trim_and_add_key_to_values(NULL, NULL, str);
1858 +
1859 + bool ret = pattern_array_label_match(pa, labels, '=', NULL);
1860 +
1861 + fprintf(stderr, "%s, got %s expected %s\n", (ret == expected)?"OK":"FAILED", ret?"true":"false", expected?"true":"false");
1862 +
1863 + string_freez(str);
1864 + pattern_array_free(pa);
1865 +
1866 + return (ret == expected)?0:1;
1867 +}
1868 +
1869 +static int rrdlabels_unittest_host_chart_labels() {
1870 + fprintf(stderr, "\n%s() tests\n", __FUNCTION__);
1871 +
1872 + int errors = 0;
1873 +
1874 + RRDLABELS *labels = rrdlabels_create();
1875 + rrdlabels_add(labels, "_hostname", "hostname1", RRDLABEL_SRC_CONFIG);
1876 + rrdlabels_add(labels, "_os", "linux", RRDLABEL_SRC_CONFIG);
1877 + rrdlabels_add(labels, "_distro", "ubuntu", RRDLABEL_SRC_CONFIG);
1878 +
1879 + // match a single key
1880 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*", true);
1881 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*", false);
1882 +
1883 + // conflicting keys (some positive, some negative)
1884 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* _os=!*", false);
1885 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!* _os=*", false);
1886 +
1887 + // the user uses a key that is not there
1888 + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=*", false);
1889 + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=!*", false);
1890 + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=* _hostname=* _os=*", false);
1891 + errors += rrdlabels_unittest_check_pattern_list(labels, "_not_a_key=!* _hostname=* _os=*", false);
1892 +
1893 + // positive and negative matches on the same key
1894 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*bad* *name*", true);
1895 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* !*invalid* !*bad*", true);
1896 +
1897 + // positive and negative matches on the same key with catch all
1898 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*bad* *", true);
1899 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* !*invalid* !*bad*", true);
1900 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*invalid* !*name* *", false);
1901 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=* !*invalid* !*name*", true);
1902 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* !*", true);
1903 +
1904 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=!*name* _os=l*", false);
1905 + errors += rrdlabels_unittest_check_pattern_list(labels, "_os=l* hostname=!*name*", false);
1906 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* _hostname=*", true);
1907 + errors += rrdlabels_unittest_check_pattern_list(labels, "_hostname=*name* _os=l*", true);
1908 + errors += rrdlabels_unittest_check_pattern_list(labels, "_os=l* _hostname=*name*", true);
1909 +
1910 + rrdlabels_destroy(labels);
1911 +
1912 + return errors;
1913 +}
1914 +
1915 static int rrdlabels_unittest_check_simple_pattern(RRDLABELS *labels, const char *pattern, bool expected) {
1916 fprintf(stderr, "rrdlabels_match_simple_pattern(labels, \"%s\") ... ", pattern);
1917
@@ -1924,6 +2004,7 @@ int rrdlabels_unittest(void) {
2004 errors += rrdlabels_unittest_sanitization();
2005 errors += rrdlabels_unittest_add_pairs();
2006 errors += rrdlabels_unittest_simple_pattern();
2007 + errors += rrdlabels_unittest_host_chart_labels();
2008 errors += rrdlabels_unittest_double_check();
2009 errors += rrdlabels_unittest_migrate_check();
2010 errors += rrdlabels_unittest_pattern_check();
src/database/rrdlabels.h
+2 -3
@@ -51,7 +51,7 @@ int rrdlabels_walkthrough_read(RRDLABELS *labels, int (*callback)(const char *na
51 void rrdlabels_log_to_buffer(RRDLABELS *labels, BUFFER *wb);
52 bool rrdlabels_match_simple_pattern(RRDLABELS *labels, const char *simple_pattern_txt);
53
54 -bool rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches);
54 +SIMPLE_PATTERN_RESULT rrdlabels_match_simple_pattern_parsed(RRDLABELS *labels, SIMPLE_PATTERN *pattern, char equal, size_t *searches);
55 int rrdlabels_to_buffer(RRDLABELS *labels, BUFFER *wb, const char *before_each, const char *equal, const char *quote, const char *between_them,
56 bool (*filter_callback)(const char *name, const char *value, RRDLABEL_SRC ls, void *data), void *filter_data,
57 void (*name_sanitizer)(char *dst, const char *src, size_t dst_size),
@@ -69,8 +69,7 @@ bool pattern_array_label_match(
69 struct pattern_array *pa,
70 RRDLABELS *labels,
71 char eq,
72 - size_t *searches,
73 - bool (*callback_function)(RRDLABELS *, SIMPLE_PATTERN *, char, size_t *));
72 + size_t *searches);
73 struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep);
74 struct pattern_array *
75 pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern);
src/health/health_prototypes.c
+3 -3
@@ -350,7 +350,7 @@ static char *simple_pattern_trim_around_equal(const char *src) {
350 return store;
351 }
352
353 -static struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input) {
353 +struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input) {
354 char *tmp = simple_pattern_trim_around_equal(string2str(input));
355 pa = health_config_add_key_to_values(pa, key, tmp);
356 freez(tmp);
@@ -483,7 +483,7 @@ static bool prototype_matches_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap) {
483 return false;
484
485 if (host->rrdlabels && ap->match.host_labels_pattern &&
486 - !pattern_array_label_match(ap->match.host_labels_pattern, host->rrdlabels, '=', NULL, rrdlabels_match_simple_pattern_parsed))
486 + !pattern_array_label_match(ap->match.host_labels_pattern, host->rrdlabels, '=', NULL))
487 return false;
488
489 return true;
@@ -501,7 +501,7 @@ static bool prototype_matches_rrdset(RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
501 return false;
502
503 if (st->rrdlabels && ap->match.chart_labels_pattern &&
504 - !pattern_array_label_match(ap->match.chart_labels_pattern, st->rrdlabels, '=', NULL, rrdlabels_match_simple_pattern_parsed))
504 + !pattern_array_label_match(ap->match.chart_labels_pattern, st->rrdlabels, '=', NULL))
505 return false;
506
507 return true;