github.com/argoproj/argo-events@v1.9.1/eventbus/jetstream/sensor/sensor_jetstream.go (about) 1 package sensor 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "time" 8 9 eventbuscommon "github.com/argoproj/argo-events/eventbus/common" 10 eventbusjetstreambase "github.com/argoproj/argo-events/eventbus/jetstream/base" 11 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 12 nats "github.com/nats-io/nats.go" 13 "go.uber.org/zap" 14 15 "encoding/json" 16 17 "github.com/argoproj/argo-events/common" 18 cloudevents "github.com/cloudevents/sdk-go/v2" 19 hashstructure "github.com/mitchellh/hashstructure/v2" 20 ) 21 22 const ( 23 SensorNilError = "sensorSpec == nil??" 24 ) 25 26 type SensorJetstream struct { 27 *eventbusjetstreambase.Jetstream 28 29 sensorName string 30 sensorSpec *v1alpha1.Sensor 31 keyValueStore nats.KeyValue 32 } 33 34 func NewSensorJetstream(url string, sensorSpec *v1alpha1.Sensor, streamConfig string, auth *eventbuscommon.Auth, logger *zap.SugaredLogger) (*SensorJetstream, error) { 35 if sensorSpec == nil { 36 errStr := SensorNilError 37 logger.Errorf(errStr) 38 return nil, fmt.Errorf(errStr) 39 } 40 41 baseJetstream, err := eventbusjetstreambase.NewJetstream(url, streamConfig, auth, logger) 42 if err != nil { 43 return nil, err 44 } 45 return &SensorJetstream{ 46 baseJetstream, 47 sensorSpec.Name, 48 sensorSpec, 49 nil}, nil 50 } 51 52 func (stream *SensorJetstream) Initialize() error { 53 err := stream.Init() // member of jetstreambase.Jetstream 54 if err != nil { 55 return err 56 } 57 58 // see if there's an existing one 59 stream.keyValueStore, _ = stream.MgmtConnection.JSContext.KeyValue(stream.sensorName) 60 if stream.keyValueStore == nil { 61 // create Key/Value store for this Sensor (seems to be okay to call this if it already exists) 62 stream.keyValueStore, err = stream.MgmtConnection.JSContext.CreateKeyValue(&nats.KeyValueConfig{Bucket: stream.sensorName}) 63 if err != nil { 64 errStr := fmt.Sprintf("failed to Create Key/Value Store for sensor %s, err: %v", stream.sensorName, err) 65 stream.Logger.Error(errStr) 66 return err 67 } 68 } else { 69 stream.Logger.Infof("found existing K/V store for sensor %s, using that", stream.sensorName) 70 } 71 stream.Logger.Infof("successfully created/located K/V store for sensor %s", stream.sensorName) 72 73 // Here we can take the sensor specification and clean up the K/V store so as to remove any old 74 // Triggers for this Sensor that no longer exist and any old Dependencies (and also Drain any corresponding Connections) 75 err = stream.setStateToSpec(stream.sensorSpec) 76 return err 77 } 78 79 func (stream *SensorJetstream) Connect(ctx context.Context, triggerName string, dependencyExpression string, deps []eventbuscommon.Dependency, atLeastOnce bool) (eventbuscommon.TriggerConnection, error) { 80 conn, err := stream.MakeConnection() 81 if err != nil { 82 return nil, err 83 } 84 85 return NewJetstreamTriggerConn(conn, stream.sensorName, triggerName, dependencyExpression, deps) 86 } 87 88 // Update the K/V store to reflect the current Spec: 89 // 1. save the current spec, including list of triggers, list of dependencies and how they're defined, and trigger expressions 90 // 2. selectively purge dependencies from the K/V store if either the Trigger no longer exists, 91 // the dependency definition has changed, or the trigger expression has changed 92 // 3. for each dependency purged, delete the associated consumer so no new data is sent there 93 func (stream *SensorJetstream) setStateToSpec(sensorSpec *v1alpha1.Sensor) error { 94 log := stream.Logger 95 if sensorSpec == nil { 96 errStr := SensorNilError 97 log.Error(errStr) 98 return fmt.Errorf(errStr) 99 } 100 101 log.Infof("Comparing previous Spec stored in k/v store for sensor %s to new Spec", sensorSpec.Name) 102 103 changedDeps, removedDeps, validDeps, err := stream.getChangedDeps(sensorSpec) 104 if err != nil { 105 return err 106 } 107 log.Infof("Comparison of previous dependencies definitions to current: changed=%v, removed=%v, still valid=%v", changedDeps, removedDeps, validDeps) 108 109 changedTriggers, removedTriggers, validTriggers, err := stream.getChangedTriggers(sensorSpec) // this looks at the list of triggers as well as the dependency expression for each trigger 110 if err != nil { 111 return err 112 } 113 log.Infof("Comparison of previous trigger list to current: changed=%v, removed=%v, still valid=%v", changedTriggers, removedTriggers, validTriggers) 114 115 // for all valid triggers, determine if changedDeps or removedDeps requires them to be deleted 116 changedPlusRemovedDeps := make([]string, 0, len(changedDeps)+len(removedDeps)) 117 changedPlusRemovedDeps = append(changedPlusRemovedDeps, changedDeps...) 118 changedPlusRemovedDeps = append(changedPlusRemovedDeps, removedDeps...) 119 for _, triggerName := range validTriggers { 120 _ = stream.purgeSelectedDepsForTrigger(triggerName, changedPlusRemovedDeps) 121 } 122 123 // for all changedTriggers (which includes modified and deleted), purge their dependencies 124 changedPlusRemovedTriggers := make([]string, 0, len(changedTriggers)+len(removedTriggers)) 125 changedPlusRemovedTriggers = append(changedPlusRemovedTriggers, changedTriggers...) 126 changedPlusRemovedTriggers = append(changedPlusRemovedTriggers, removedTriggers...) 127 for _, triggerName := range changedPlusRemovedTriggers { 128 _ = stream.purgeAllDepsForTrigger(triggerName) 129 } 130 131 // save new spec 132 err = stream.saveSpec(sensorSpec, removedTriggers) 133 if err != nil { 134 return err 135 } 136 137 return nil 138 } 139 140 // purging dependency means both purging it from the K/V store as well as deleting the associated Consumer so no more messages are sent to it 141 func (stream *SensorJetstream) purgeDependency(triggerName string, depName string) error { 142 // purge from Key/Value store first 143 key := getDependencyKey(triggerName, depName) 144 durableName := getDurableName(stream.sensorName, triggerName, depName) 145 stream.Logger.Debugf("purging dependency, including 1) key %s from the K/V store, and 2) durable consumer %s", key, durableName) 146 err := stream.keyValueStore.Delete(key) 147 if err != nil && err != nats.ErrKeyNotFound { // sometimes we call this on a trigger/dependency combination not sure if it actually exists or not, so 148 // don't need to worry about case of it not existing 149 stream.Logger.Error(err) 150 return err 151 } 152 // then delete consumer 153 stream.Logger.Debugf("durable name for sensor='%s', trigger='%s', dep='%s': '%s'", stream.sensorName, triggerName, depName, durableName) 154 155 _ = stream.MgmtConnection.JSContext.DeleteConsumer("default", durableName) // sometimes we call this on a trigger/dependency combination not sure if it actually exists or not, so 156 // don't need to worry about case of it not existing 157 158 return nil 159 } 160 161 func (stream *SensorJetstream) saveSpec(sensorSpec *v1alpha1.Sensor, removedTriggers []string) error { 162 // remove the old triggers from the K/V store 163 for _, trigger := range removedTriggers { 164 key := getTriggerExpressionKey(trigger) 165 err := stream.keyValueStore.Delete(key) 166 if err != nil { 167 errStr := fmt.Sprintf("error deleting key %s: %v", key, err) 168 stream.Logger.Error(errStr) 169 return fmt.Errorf(errStr) 170 } 171 stream.Logger.Debugf("successfully removed Trigger expression at key %s", key) 172 } 173 174 // save the dependency definitions 175 depMap := make(DependencyDefinitionValue) 176 for _, dep := range sensorSpec.Spec.Dependencies { 177 hash, err := hashstructure.Hash(dep, hashstructure.FormatV2, nil) 178 if err != nil { 179 errStr := fmt.Sprintf("failed to hash dependency %+v", dep) 180 stream.Logger.Errorf(errStr) 181 err = fmt.Errorf(errStr) 182 return err 183 } 184 depMap[dep.Name] = hash 185 } 186 err := stream.storeDependencyDefinitions(depMap) 187 if err != nil { 188 return err 189 } 190 191 // save the list of Triggers 192 triggerList := make(TriggerValue, len(sensorSpec.Spec.Triggers)) 193 for i, trigger := range sensorSpec.Spec.Triggers { 194 triggerList[i] = trigger.Template.Name 195 } 196 err = stream.storeTriggerList(triggerList) 197 if err != nil { 198 return err 199 } 200 201 // for each trigger, save its expression 202 for _, trigger := range sensorSpec.Spec.Triggers { 203 err := stream.storeTriggerExpression(trigger.Template.Name, trigger.Template.Conditions) 204 if err != nil { 205 return err 206 } 207 } 208 209 return nil 210 } 211 212 func (stream *SensorJetstream) getChangedTriggers(sensorSpec *v1alpha1.Sensor) (changedTriggers []string, removedTriggers []string, validTriggers []string, err error) { 213 if sensorSpec == nil { 214 errStr := SensorNilError 215 stream.Logger.Errorf(errStr) 216 err = fmt.Errorf(errStr) 217 return nil, nil, nil, err 218 } 219 220 mappedSpecTriggers := make(map[string]v1alpha1.Trigger, len(sensorSpec.Spec.Triggers)) 221 for _, trigger := range sensorSpec.Spec.Triggers { 222 mappedSpecTriggers[trigger.Template.Name] = trigger 223 } 224 storedTriggers, err := stream.getTriggerList() 225 if err != nil { 226 return nil, nil, nil, err 227 } 228 for _, triggerName := range storedTriggers { 229 currTrigger, found := mappedSpecTriggers[triggerName] 230 if !found { 231 removedTriggers = append(removedTriggers, triggerName) 232 } else { 233 // is the trigger expression the same or different? 234 storedExpression, err := stream.getTriggerExpression(triggerName) 235 if err != nil { 236 return nil, nil, nil, err 237 } 238 if storedExpression == currTrigger.Template.Conditions { 239 validTriggers = append(validTriggers, triggerName) 240 } else { 241 changedTriggers = append(changedTriggers, triggerName) 242 } 243 } 244 } 245 return changedTriggers, removedTriggers, validTriggers, nil 246 } 247 248 func (stream *SensorJetstream) getChangedDeps(sensorSpec *v1alpha1.Sensor) (changedDeps []string, removedDeps []string, validDeps []string, err error) { 249 if sensorSpec == nil { 250 errStr := SensorNilError 251 stream.Logger.Errorf(errStr) 252 err = fmt.Errorf(errStr) 253 return nil, nil, nil, err 254 } 255 256 specDependencies := sensorSpec.Spec.Dependencies 257 mappedSpecDependencies := make(map[string]v1alpha1.EventDependency, len(specDependencies)) 258 for _, dep := range specDependencies { 259 mappedSpecDependencies[dep.Name] = dep 260 } 261 storedDependencies, err := stream.getDependencyDefinitions() 262 if err != nil { 263 return nil, nil, nil, err 264 } 265 for depName, hashedDep := range storedDependencies { 266 currDep, found := mappedSpecDependencies[depName] 267 if !found { 268 removedDeps = append(removedDeps, depName) 269 } else { 270 // is the dependency definition the same or different? 271 hash, err := hashstructure.Hash(currDep, hashstructure.FormatV2, nil) 272 if err != nil { 273 errStr := fmt.Sprintf("failed to hash dependency %+v", currDep) 274 stream.Logger.Errorf(errStr) 275 err = fmt.Errorf(errStr) 276 return nil, nil, nil, err 277 } 278 if hash == hashedDep { 279 validDeps = append(validDeps, depName) 280 } else { 281 changedDeps = append(changedDeps, depName) 282 } 283 } 284 } 285 286 return changedDeps, removedDeps, validDeps, nil 287 } 288 289 func (stream *SensorJetstream) getDependencyDefinitions() (DependencyDefinitionValue, error) { 290 depDefs, err := stream.keyValueStore.Get(DependencyDefsKey) 291 if err != nil { 292 if err == nats.ErrKeyNotFound { 293 return make(DependencyDefinitionValue), nil 294 } 295 errStr := fmt.Sprintf("error getting key %s: %v", DependencyDefsKey, err) 296 stream.Logger.Error(errStr) 297 return nil, fmt.Errorf(errStr) 298 } 299 stream.Logger.Debugf("Value of key %s: %s", DependencyDefsKey, string(depDefs.Value())) 300 301 depDefMap := DependencyDefinitionValue{} 302 err = json.Unmarshal(depDefs.Value(), &depDefMap) 303 if err != nil { 304 errStr := fmt.Sprintf("error unmarshalling value %s of key %s: %v", string(depDefs.Value()), DependencyDefsKey, err) 305 stream.Logger.Error(errStr) 306 return nil, fmt.Errorf(errStr) 307 } 308 309 return depDefMap, nil 310 } 311 312 func (stream *SensorJetstream) storeDependencyDefinitions(depDef DependencyDefinitionValue) error { 313 bytes, err := json.Marshal(depDef) 314 if err != nil { 315 errStr := fmt.Sprintf("error marshalling %+v: %v", depDef, err) 316 stream.Logger.Error(errStr) 317 return fmt.Errorf(errStr) 318 } 319 _, err = stream.keyValueStore.Put(DependencyDefsKey, bytes) 320 if err != nil { 321 errStr := fmt.Sprintf("error storing %s under key %s: %v", string(bytes), DependencyDefsKey, err) 322 stream.Logger.Error(errStr) 323 return fmt.Errorf(errStr) 324 } 325 stream.Logger.Debugf("successfully stored dependency definition under key %s: %s", DependencyDefsKey, string(bytes)) 326 return nil 327 } 328 329 func (stream *SensorJetstream) getTriggerList() (TriggerValue, error) { 330 triggerListJson, err := stream.keyValueStore.Get(TriggersKey) 331 if err != nil { 332 if err == nats.ErrKeyNotFound { 333 return make(TriggerValue, 0), nil 334 } 335 errStr := fmt.Sprintf("error getting key %s: %v", TriggersKey, err) 336 stream.Logger.Error(errStr) 337 return nil, fmt.Errorf(errStr) 338 } 339 stream.Logger.Debugf("Value of key %s: %s", TriggersKey, string(triggerListJson.Value())) 340 341 triggerList := TriggerValue{} 342 err = json.Unmarshal(triggerListJson.Value(), &triggerList) 343 if err != nil { 344 errStr := fmt.Sprintf("error unmarshalling value %s of key %s: %v", string(triggerListJson.Value()), TriggersKey, err) 345 stream.Logger.Error(errStr) 346 return nil, fmt.Errorf(errStr) 347 } 348 349 return triggerList, nil 350 } 351 352 func (stream *SensorJetstream) storeTriggerList(triggerList TriggerValue) error { 353 bytes, err := json.Marshal(triggerList) 354 if err != nil { 355 errStr := fmt.Sprintf("error marshalling %+v: %v", triggerList, err) 356 stream.Logger.Error(errStr) 357 return fmt.Errorf(errStr) 358 } 359 _, err = stream.keyValueStore.Put(TriggersKey, bytes) 360 if err != nil { 361 errStr := fmt.Sprintf("error storing %s under key %s: %v", string(bytes), TriggersKey, err) 362 stream.Logger.Error(errStr) 363 return fmt.Errorf(errStr) 364 } 365 stream.Logger.Debugf("successfully stored trigger list under key %s: %s", TriggersKey, string(bytes)) 366 return nil 367 } 368 369 func (stream *SensorJetstream) getTriggerExpression(triggerName string) (string, error) { 370 key := getTriggerExpressionKey(triggerName) 371 expr, err := stream.keyValueStore.Get(key) 372 if err != nil { 373 if err == nats.ErrKeyNotFound { 374 return "", nil 375 } 376 errStr := fmt.Sprintf("error getting key %s: %v", key, err) 377 stream.Logger.Error(errStr) 378 return "", fmt.Errorf(errStr) 379 } 380 stream.Logger.Debugf("Value of key %s: %s", key, string(expr.Value())) 381 382 return string(expr.Value()), nil 383 } 384 385 func (stream *SensorJetstream) storeTriggerExpression(triggerName string, conditionExpression string) error { 386 key := getTriggerExpressionKey(triggerName) 387 _, err := stream.keyValueStore.PutString(key, conditionExpression) 388 if err != nil { 389 errStr := fmt.Sprintf("error storing %s under key %s: %v", conditionExpression, key, err) 390 stream.Logger.Error(errStr) 391 return fmt.Errorf(errStr) 392 } 393 stream.Logger.Debugf("successfully stored trigger expression under key %s: %s", key, conditionExpression) 394 return nil 395 } 396 397 func (stream *SensorJetstream) purgeSelectedDepsForTrigger(triggerName string, deps []string) error { 398 stream.Logger.Debugf("purging selected dependencies %v for trigger %s", deps, triggerName) 399 for _, dep := range deps { 400 err := stream.purgeDependency(triggerName, dep) // this will attempt a delete even if no such key exists for a particular trigger, but that's okay 401 if err != nil { 402 return err 403 } 404 } 405 return nil 406 } 407 408 func (stream *SensorJetstream) purgeAllDepsForTrigger(triggerName string) error { 409 stream.Logger.Debugf("purging all dependencies for trigger %s", triggerName) 410 // use the stored trigger expression to determine which dependencies need to be purged 411 storedExpression, err := stream.getTriggerExpression(triggerName) 412 if err != nil { 413 return err 414 } 415 416 // get the individual dependencies by removing the special characters 417 modExpr := strings.ReplaceAll(storedExpression, "&&", " ") 418 modExpr = strings.ReplaceAll(modExpr, "||", " ") 419 modExpr = strings.ReplaceAll(modExpr, "(", " ") 420 modExpr = strings.ReplaceAll(modExpr, ")", " ") 421 deps := strings.FieldsFunc(modExpr, func(r rune) bool { return r == ' ' }) 422 423 for _, dep := range deps { 424 err := stream.purgeDependency(triggerName, dep) 425 if err != nil { 426 return err 427 } 428 } 429 return nil 430 } 431 432 // //////////////////////////////////////////////////////////////////////////////////////////////////// 433 // These are the Keys and methods to derive Keys for our K/V store 434 var ( 435 TriggersKey = "Triggers" 436 DependencyDefsKey = "Deps" 437 ) 438 439 func getDependencyKey(triggerName string, depName string) string { 440 return fmt.Sprintf("%s/%s", triggerName, depName) 441 } 442 443 func getTriggerExpressionKey(triggerName string) string { 444 return fmt.Sprintf("%s/Expression", triggerName) 445 } 446 447 // //////////////////////////////////////////////////////////////////////////////////////////////////// 448 // These are the structs representing Values in our K/V store 449 type DependencyDefinitionValue map[string]uint64 // value for DependencyDefsKey 450 type TriggerValue []string // value for TriggersKey 451 452 // value for getDependencyKey() 453 type MsgInfo struct { 454 StreamSeq uint64 455 ConsumerSeq uint64 456 Timestamp time.Time 457 Event *cloudevents.Event 458 } 459 460 func getDurableName(sensorName string, triggerName string, depName string) string { 461 hashKey := fmt.Sprintf("%s-%s-%s", sensorName, triggerName, depName) 462 hashVal := common.Hasher(hashKey) 463 return fmt.Sprintf("group-%s", hashVal) 464 }