package nats import ( "encoding/json" "oc-scheduler/infrastructure/execution" "oc-scheduler/infrastructure/planner" "oc-scheduler/infrastructure/scheduling_resources" oclib "cloud.o-forge.io/core/oc-lib" "cloud.o-forge.io/core/oc-lib/models/booking" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/resources/purchase_resource" libutils "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/models/workflow" "cloud.o-forge.io/core/oc-lib/models/workflow_execution" "cloud.o-forge.io/core/oc-lib/tools" ) // handleConfirm processes a CONFIRM_EVENT: sets IsDraft=false on the resource. func handleConfirm(resp tools.NATSResponse) { scheduling_resources.Confirm(string(resp.Payload), resp.Datatype) } // handleConsidersEvent routes CONSIDERS_EVENT to the execution service. func handleConsidersEvent(resp tools.NATSResponse) { switch resp.Datatype { case tools.BOOKING, tools.PURCHASE_RESOURCE: execution.UpdateExecutionState(resp.Payload, resp.Datatype) case tools.WORKFLOW_EXECUTION: execution.ConfirmExecutionDrafts(resp.Payload) } } // handleRemoveResource routes REMOVE_RESOURCE to the appropriate service. func handleRemoveResource(resp tools.NATSResponse) { adminReq := &tools.APIRequest{Admin: true} switch resp.Datatype { case tools.WORKFLOW: var wf workflow.Workflow if err := json.Unmarshal(resp.Payload, &wf); err != nil { return } planner.GetPlannerService().NotifyWorkflow(wf.GetID()) case tools.BOOKING: var p scheduling_resources.RemoveResourcePayload if err := json.Unmarshal(resp.Payload, &p); err != nil { return } scheduling_resources.GetService().HandleRemoveBooking(p, adminReq) case tools.PURCHASE_RESOURCE: var p scheduling_resources.RemoveResourcePayload if err := json.Unmarshal(resp.Payload, &p); err != nil { return } scheduling_resources.GetService().HandleRemovePurchase(p, adminReq) case tools.WORKFLOW_EXECUTION: var p scheduling_resources.RemoveResourcePayload if err := json.Unmarshal(resp.Payload, &p); err != nil || p.ID == "" { return } // DeleteOne calls GenericDeleteOne internally which fires NotifyChange. workflow_execution.NewAccessor(adminReq).DeleteOne(p.ID) } } // handleCreateResource routes CREATE_RESOURCE to the appropriate service. func handleCreateResource(resp tools.NATSResponse) { adminReq := &tools.APIRequest{Admin: true} switch resp.Datatype { case tools.WORKFLOW: var wf workflow.Workflow if err := json.Unmarshal(resp.Payload, &wf); err != nil { return } planner.GetPlannerService().Broadcast(&wf) planner.GetPlannerService().NotifyWorkflow(wf.GetID()) case tools.BOOKING: var bk booking.Booking if err := json.Unmarshal(resp.Payload, &bk); err != nil { return } if bk.FromNano != "" { access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil) pp := access.LoadOne(bk.FromNano) if p := pp.ToPeer(); p == nil || p.Relation == peer.NANO { return } access = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.BOOKING), nil) d := access.LoadOne(bk.GetID()) if d.Data == nil { access.StoreOne(bk.Serialize(&bk)) } else { access.UpdateOne(bk.Serialize(&bk), bk.GetID()) } return } needsConsiders := scheduling_resources.GetService().HandleCreateBooking(&bk, adminReq) if needsConsiders { payload, _ := json.Marshal(execution.ConsidersPayload{ID: bk.GetID()}) execution.UpdateExecutionState(payload, tools.BOOKING) } case tools.PURCHASE_RESOURCE: var pr purchase_resource.PurchaseResource if err := json.Unmarshal(resp.Payload, &pr); err != nil { return } if pr.FromNano != "" { access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil) pp := access.LoadOne(pr.FromNano) if p := pp.ToPeer(); p == nil || p.Relation == peer.NANO { return } access = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PURCHASE_RESOURCE), nil) d := access.LoadOne(pr.GetID()) if d.Data == nil { access.StoreOne(pr.Serialize(&pr)) } else { access.UpdateOne(pr.Serialize(&pr), pr.GetID()) } return } needsConsiders := scheduling_resources.GetService().HandleCreatePurchase(&pr, adminReq) if needsConsiders { payload, _ := json.Marshal(execution.ConsidersPayload{ID: pr.GetID()}) execution.UpdateExecutionState(payload, tools.PURCHASE_RESOURCE) } case tools.WORKFLOW_EXECUTION: // Only propagate the state change onto an execution that oc-scheduler // already owns. Never create executions from an external NATS event: // creation is strictly oc-scheduler's responsibility (via the session // flow), and blindly calling StoreOne here would trigger // StoreDraftDefault (IsDraft=true, State=DRAFT), polluting the name- // uniqueness index and breaking the check stream's first draft creation. var update workflow_execution.WorkflowExecution if err := json.Unmarshal(resp.Payload, &update); err != nil || update.GetID() == "" { return } res, _, loadErr := workflow_execution.NewAccessor(adminReq).LoadOne(update.GetID()) if loadErr != nil || res == nil { return } exec := res.(*workflow_execution.WorkflowExecution) exec.State = update.State libutils.GenericRawUpdateOne(exec, exec.GetID(), workflow_execution.NewAccessor(adminReq)) } }