github.com/argoproj/argo-events@v1.9.1/sensors/dependencies/filter.go (about) 1 /* 2 Copyright 2018 BlackRock, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package dependencies 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "regexp" 25 "strconv" 26 "strings" 27 "text/template" 28 "time" 29 30 "github.com/Knetic/govaluate" 31 "github.com/Masterminds/sprig/v3" 32 "github.com/argoproj/argo-events/common" 33 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 34 "github.com/tidwall/gjson" 35 lua "github.com/yuin/gopher-lua" 36 ) 37 38 const ( 39 errMsgListSeparator = " / " 40 errMsgTemplate = "%s filter error (%s)" 41 multiErrMsgTemplate = "%s filter errors [%s]" 42 ) 43 44 // Filter filters the event with dependency's defined filters 45 func Filter(event *v1alpha1.Event, filter *v1alpha1.EventDependencyFilter, filtersLogicalOperator v1alpha1.LogicalOperator) (bool, error) { 46 if filter == nil { 47 return true, nil 48 } 49 50 ok, err := filterEvent(filter, filtersLogicalOperator, event) 51 if err != nil { 52 return false, err 53 } 54 55 return ok, nil 56 } 57 58 // filterEvent applies the filters to an Event 59 func filterEvent(filter *v1alpha1.EventDependencyFilter, operator v1alpha1.LogicalOperator, event *v1alpha1.Event) (bool, error) { 60 var errMessages []string 61 if operator == v1alpha1.OrLogicalOperator { 62 errMessages = make([]string, 0) 63 } 64 65 exprFilter, exprErr := filterExpr(filter.Exprs, filter.ExprLogicalOperator, event) 66 if exprErr != nil { 67 if operator != v1alpha1.OrLogicalOperator { 68 return false, exprErr 69 } 70 errMessages = append(errMessages, exprErr.Error()) 71 } 72 73 dataFilter, dataErr := filterData(filter.Data, filter.DataLogicalOperator, event) 74 if dataErr != nil { 75 if operator != v1alpha1.OrLogicalOperator { 76 return false, dataErr 77 } 78 errMessages = append(errMessages, dataErr.Error()) 79 } 80 81 ctxFilter := filterContext(filter.Context, event.Context) 82 83 timeFilter, timeErr := filterTime(filter.Time, event.Context.Time.Time) 84 if timeErr != nil { 85 if operator != v1alpha1.OrLogicalOperator { 86 return false, timeErr 87 } 88 errMessages = append(errMessages, timeErr.Error()) 89 } 90 91 scriptFilter, err := filterScript(filter.Script, event) 92 if err != nil { 93 return false, err 94 } 95 96 if operator == v1alpha1.OrLogicalOperator { 97 pass := (filter.Exprs != nil && exprFilter) || 98 (filter.Data != nil && dataFilter) || 99 (filter.Context != nil && ctxFilter) || 100 (filter.Time != nil && timeFilter) || 101 (filter.Script != "" && scriptFilter) 102 103 if len(errMessages) > 0 { 104 return pass, errors.New(strings.Join(errMessages, errMsgListSeparator)) 105 } 106 return pass, nil 107 } 108 return exprFilter && dataFilter && ctxFilter && timeFilter && scriptFilter, nil 109 } 110 111 // filterExpr applies expression based filters against event data 112 // expression evaluation is based on https://github.com/Knetic/govaluate 113 // in case "operator input" is equal to v1alpha1.OrLogicalOperator, filters are evaluated as mutual exclusive 114 func filterExpr(filters []v1alpha1.ExprFilter, operator v1alpha1.LogicalOperator, event *v1alpha1.Event) (bool, error) { 115 if filters == nil { 116 return true, nil 117 } 118 if event == nil { 119 return false, fmt.Errorf(errMsgTemplate, "expr", "nil event") 120 } 121 payload := event.Data 122 if payload == nil { 123 return true, nil 124 } 125 if !gjson.Valid(string(payload)) { 126 return false, fmt.Errorf(errMsgTemplate, "expr", "event data not valid JSON") 127 } 128 129 var errMessages []string 130 if operator == v1alpha1.OrLogicalOperator { 131 errMessages = make([]string, 0) 132 } 133 filterExpr: 134 for _, filter := range filters { 135 parameters := map[string]interface{}{} 136 for _, field := range filter.Fields { 137 pathResult := gjson.GetBytes(payload, field.Path) 138 if !pathResult.Exists() { 139 errMsg := "path '%s' does not exist" 140 if operator == v1alpha1.OrLogicalOperator { 141 errMessages = append(errMessages, fmt.Sprintf(errMsg, field.Path)) 142 continue filterExpr 143 } else { 144 return false, fmt.Errorf(errMsgTemplate, "expr", fmt.Sprintf(errMsg, field.Path)) 145 } 146 } 147 parameters[field.Name] = pathResult.Value() 148 } 149 150 if len(parameters) == 0 { 151 continue 152 } 153 154 expr, exprErr := govaluate.NewEvaluableExpression(filter.Expr) 155 if exprErr != nil { 156 if operator == v1alpha1.OrLogicalOperator { 157 errMessages = append(errMessages, exprErr.Error()) 158 continue 159 } else { 160 return false, fmt.Errorf(errMsgTemplate, "expr", exprErr.Error()) 161 } 162 } 163 164 result, resErr := expr.Evaluate(parameters) 165 if resErr != nil { 166 if operator == v1alpha1.OrLogicalOperator { 167 errMessages = append(errMessages, resErr.Error()) 168 continue 169 } else { 170 return false, fmt.Errorf(errMsgTemplate, "expr", resErr.Error()) 171 } 172 } 173 174 if result == true { 175 if operator == v1alpha1.OrLogicalOperator { 176 return true, nil 177 } 178 } else { 179 if operator != v1alpha1.OrLogicalOperator { 180 return false, nil 181 } 182 } 183 } 184 185 if operator == v1alpha1.OrLogicalOperator { 186 if len(errMessages) > 0 { 187 return false, fmt.Errorf(multiErrMsgTemplate, "expr", strings.Join(errMessages, errMsgListSeparator)) 188 } 189 return false, nil 190 } else { 191 return true, nil 192 } 193 } 194 195 // filterData runs the dataFilter against the Event's data 196 // returns (true, nil) when data passes filters, false otherwise 197 // in case "operator input" is equal to v1alpha1.OrLogicalOperator, filters are evaluated as mutual exclusive 198 func filterData(filters []v1alpha1.DataFilter, operator v1alpha1.LogicalOperator, event *v1alpha1.Event) (bool, error) { 199 if len(filters) == 0 { 200 return true, nil 201 } 202 if event == nil { 203 return false, fmt.Errorf(errMsgTemplate, "data", "nil Event") 204 } 205 payload := event.Data 206 if payload == nil { 207 return true, nil 208 } 209 if !gjson.Valid(string(payload)) { 210 return false, fmt.Errorf(errMsgTemplate, "data", "event data not valid JSON") 211 } 212 213 var errMessages []string 214 if operator == v1alpha1.OrLogicalOperator { 215 errMessages = make([]string, 0) 216 } 217 filterData: 218 for _, f := range filters { 219 pathResult := gjson.GetBytes(payload, f.Path) 220 if !pathResult.Exists() { 221 errMsg := "path '%s' does not exist" 222 if operator == v1alpha1.OrLogicalOperator { 223 errMessages = append(errMessages, fmt.Sprintf(errMsg, f.Path)) 224 continue 225 } else { 226 return false, fmt.Errorf(errMsgTemplate, "data", fmt.Sprintf(errMsg, f.Path)) 227 } 228 } 229 230 if f.Value == nil || len(f.Value) == 0 { 231 errMsg := "no values specified" 232 if operator == v1alpha1.OrLogicalOperator { 233 errMessages = append(errMessages, errMsg) 234 continue 235 } else { 236 return false, fmt.Errorf(errMsgTemplate, "data", errMsg) 237 } 238 } 239 240 if f.Template != "" { 241 tpl, tplErr := template.New("param").Funcs(sprig.FuncMap()).Parse(f.Template) 242 if tplErr != nil { 243 if operator == v1alpha1.OrLogicalOperator { 244 errMessages = append(errMessages, tplErr.Error()) 245 continue 246 } else { 247 return false, fmt.Errorf(errMsgTemplate, "data", tplErr.Error()) 248 } 249 } 250 251 var buf bytes.Buffer 252 execErr := tpl.Execute(&buf, map[string]interface{}{ 253 "Input": pathResult.String(), 254 }) 255 if execErr != nil { 256 if operator == v1alpha1.OrLogicalOperator { 257 errMessages = append(errMessages, execErr.Error()) 258 continue 259 } else { 260 return false, fmt.Errorf(errMsgTemplate, "data", execErr.Error()) 261 } 262 } 263 264 out := buf.String() 265 if out == "" || out == "<no value>" { 266 if operator == v1alpha1.OrLogicalOperator { 267 errMessages = append(errMessages, fmt.Sprintf("template evaluated to empty string or no value: '%s'", f.Template)) 268 continue 269 } else { 270 return false, fmt.Errorf(errMsgTemplate, "data", 271 fmt.Sprintf("template '%s' evaluated to empty string or no value", f.Template)) 272 } 273 } 274 275 pathResult = gjson.Parse(strconv.Quote(out)) 276 } 277 278 switch f.Type { 279 case v1alpha1.JSONTypeBool: 280 for _, value := range f.Value { 281 val, err := strconv.ParseBool(value) 282 if err != nil { 283 if operator == v1alpha1.OrLogicalOperator { 284 errMessages = append(errMessages, err.Error()) 285 continue filterData 286 } else { 287 return false, fmt.Errorf(errMsgTemplate, "data", err.Error()) 288 } 289 } 290 291 if val == pathResult.Bool() { 292 if operator == v1alpha1.OrLogicalOperator { 293 return true, nil 294 } else { 295 continue filterData 296 } 297 } 298 } 299 300 if operator == v1alpha1.OrLogicalOperator { 301 continue filterData 302 } else { 303 return false, nil 304 } 305 306 case v1alpha1.JSONTypeNumber: 307 for _, value := range f.Value { 308 filterVal, err := strconv.ParseFloat(value, 64) 309 eventVal := pathResult.Float() 310 if err != nil { 311 if operator == v1alpha1.OrLogicalOperator { 312 errMessages = append(errMessages, err.Error()) 313 continue filterData 314 } else { 315 return false, fmt.Errorf(errMsgTemplate, "data", err.Error()) 316 } 317 } 318 319 compareResult := false 320 switch f.Comparator { 321 case v1alpha1.GreaterThanOrEqualTo: 322 if eventVal >= filterVal { 323 compareResult = true 324 } 325 case v1alpha1.GreaterThan: 326 if eventVal > filterVal { 327 compareResult = true 328 } 329 case v1alpha1.LessThan: 330 if eventVal < filterVal { 331 compareResult = true 332 } 333 case v1alpha1.LessThanOrEqualTo: 334 if eventVal <= filterVal { 335 compareResult = true 336 } 337 case v1alpha1.NotEqualTo: 338 if eventVal != filterVal { 339 compareResult = true 340 } 341 case v1alpha1.EqualTo, v1alpha1.EmptyComparator: 342 if eventVal == filterVal { 343 compareResult = true 344 } 345 } 346 347 if compareResult { 348 if operator == v1alpha1.OrLogicalOperator { 349 return true, nil 350 } else { 351 continue filterData 352 } 353 } 354 } 355 if operator == v1alpha1.OrLogicalOperator { 356 continue filterData 357 } else { 358 return false, nil 359 } 360 361 case v1alpha1.JSONTypeString: 362 for _, value := range f.Value { 363 exp, err := regexp.Compile(value) 364 if err != nil { 365 if operator == v1alpha1.OrLogicalOperator { 366 errMessages = append(errMessages, err.Error()) 367 continue filterData 368 } else { 369 return false, fmt.Errorf(errMsgTemplate, "data", err.Error()) 370 } 371 } 372 373 matchResult := false 374 match := exp.Match([]byte(pathResult.String())) 375 switch f.Comparator { 376 case v1alpha1.EqualTo, v1alpha1.EmptyComparator: 377 if match { 378 matchResult = true 379 } 380 case v1alpha1.NotEqualTo: 381 if !match { 382 matchResult = true 383 } 384 } 385 386 if matchResult { 387 if operator == v1alpha1.OrLogicalOperator { 388 return true, nil 389 } else { 390 continue filterData 391 } 392 } 393 } 394 395 if operator == v1alpha1.OrLogicalOperator { 396 continue filterData 397 } else { 398 return false, nil 399 } 400 401 default: 402 errMsg := "unsupported JSON type '%s'" 403 if operator == v1alpha1.OrLogicalOperator { 404 errMessages = append(errMessages, fmt.Sprintf(errMsg, f.Type)) 405 continue filterData 406 } else { 407 return false, fmt.Errorf(errMsgTemplate, "data", fmt.Sprintf(errMsg, f.Type)) 408 } 409 } 410 } 411 412 if operator == v1alpha1.OrLogicalOperator { 413 if len(errMessages) > 0 { 414 return false, fmt.Errorf(multiErrMsgTemplate, "data", strings.Join(errMessages, errMsgListSeparator)) 415 } 416 return false, nil 417 } else { 418 return true, nil 419 } 420 } 421 422 // filterContext checks the expectedResult EventContext against the actual EventContext 423 // values are only enforced if they are non-zero values 424 // map types check that the expectedResult map is a subset of the actual map 425 func filterContext(expected *v1alpha1.EventContext, actual *v1alpha1.EventContext) bool { 426 if expected == nil { 427 return true 428 } 429 if actual == nil { 430 return false 431 } 432 433 res := true 434 if expected.Type != "" { 435 res = res && expected.Type == actual.Type 436 } 437 if expected.Subject != "" { 438 res = res && expected.Subject == actual.Subject 439 } 440 if expected.Source != "" { 441 res = res && expected.Source == actual.Source 442 } 443 if expected.DataContentType != "" { 444 res = res && expected.DataContentType == actual.DataContentType 445 } 446 return res 447 } 448 449 // filterTime checks the eventTime falls into time range specified by the timeFilter. 450 // Start is inclusive, and Stop is exclusive. 451 // 452 // if Start < Stop: eventTime must be in [Start, Stop) 453 // 454 // 0:00 Start Stop 0:00 455 // ├───────────●───────────○───────────┤ 456 // └─── OK ────┘ 457 // 458 // if Stop < Start: eventTime must be in [Start, Stop@Next day) 459 // 460 // this is equivalent to: eventTime must be in [0:00, Stop) or [Start, 0:00@Next day) 461 // 462 // 0:00 Start 0:00 Stop 0:00 463 // ├───────────○───────────●───────────┼───────────○───────────●───────────┤ 464 // └───────── OK ──────────┘ 465 // 466 // 0:00 Stop Start 0:00 467 // ●───────────○───────────●───────────○ 468 // └─── OK ────┘ └─── OK ────┘ 469 func filterTime(timeFilter *v1alpha1.TimeFilter, eventTime time.Time) (bool, error) { 470 if timeFilter == nil { 471 return true, nil 472 } 473 474 // Parse start and stop 475 startTime, startErr := common.ParseTime(timeFilter.Start, eventTime) 476 if startErr != nil { 477 return false, fmt.Errorf(errMsgTemplate, "time", startErr.Error()) 478 } 479 stopTime, stopErr := common.ParseTime(timeFilter.Stop, eventTime) 480 if stopErr != nil { 481 return false, fmt.Errorf(errMsgTemplate, "time", stopErr.Error()) 482 } 483 484 // Filtering logic 485 if startTime.Before(stopTime) { 486 return (eventTime.After(startTime) || eventTime.Equal(startTime)) && eventTime.Before(stopTime), nil 487 } else { 488 return (eventTime.After(startTime) || eventTime.Equal(startTime)) || eventTime.Before(stopTime), nil 489 } 490 } 491 492 func filterScript(script string, event *v1alpha1.Event) (bool, error) { 493 if script == "" { 494 return true, nil 495 } 496 if event == nil { 497 return false, fmt.Errorf("nil event") 498 } 499 payload := event.Data 500 if payload == nil { 501 return true, nil 502 } 503 var js *json.RawMessage 504 if err := json.Unmarshal(payload, &js); err != nil { 505 return false, err 506 } 507 var jsData []byte 508 jsData, err := json.Marshal(js) 509 if err != nil { 510 return false, err 511 } 512 l := lua.NewState() 513 defer l.Close() 514 var payloadJson map[string]interface{} 515 if err = json.Unmarshal(jsData, &payloadJson); err != nil { 516 return false, err 517 } 518 lEvent := mapToTable(payloadJson) 519 l.SetGlobal("event", lEvent) 520 if err = l.DoString(script); err != nil { 521 return false, err 522 } 523 lv := l.Get(-1) 524 return lv == lua.LTrue, nil 525 }