package utils import ( "encoding/json" "errors" "sync" "time" oclib "cloud.o-forge.io/core/oc-lib" "cloud.o-forge.io/core/oc-lib/models/resources" "cloud.o-forge.io/core/oc-lib/models/workflow" "cloud.o-forge.io/core/oc-lib/tools" ) type BookingResource struct { ID string // resource MongoDB _id PeerPID string // peer public PeerID (PID) — PlannerCache key InstanceID string // resolved from WorkflowSchedule.SelectedInstances } // collectBookingResources returns unique storage and compute resources from the // workflow graph. For each resource the selected instance ID is resolved from // selectedInstances (the scheduler's SelectedInstances ConfigItem) so the planner // check targets the exact instance chosen by the user. func CollectBookingResources(wf *workflow.Workflow, selectedInstances workflow.ConfigItem) map[string]BookingResource { if wf.Graph == nil { return nil } seen := map[string]bool{} result := map[string]BookingResource{} // Resolve MongoDB peer _id (DID) → public PeerID (PID) used as PlannerCache key. peerAccess := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil) didToPID := map[string]string{} resolvePID := func(did string) string { if pid, ok := didToPID[did]; ok { return pid } if data := peerAccess.LoadOne(did); data.Data != nil { if p := data.ToPeer(); p != nil { didToPID[did] = p.PeerID return p.PeerID } } return "" } resolveInstanceID := func(res interface { GetID() string GetCreatorID() string }) string { idx := selectedInstances.Get(res.GetID()) switch r := res.(type) { case *resources.StorageResource: if inst := r.GetSelectedInstance(idx); inst != nil { return inst.GetID() } case *resources.ComputeResource: if inst := r.GetSelectedInstance(idx); inst != nil { return inst.GetID() } } return "" } for _, item := range wf.GetGraphItems(wf.Graph.IsStorage) { _, res := item.GetResource() if res == nil { continue } id := res.GetID() if seen[id] { continue } pid := resolvePID(res.GetCreatorID()) if pid == "" { continue } seen[id] = true result[pid] = BookingResource{ ID: id, PeerPID: pid, InstanceID: resolveInstanceID(res), } } for _, item := range wf.GetGraphItems(wf.Graph.IsCompute) { _, res := item.GetResource() if res == nil { continue } id := res.GetID() if seen[id] { continue } pid := resolvePID(res.GetCreatorID()) if pid == "" { continue } seen[id] = true result[pid] = BookingResource{ ID: id, PeerPID: pid, InstanceID: resolveInstanceID(res), } } return result } // GetWorkflowPeerIDs loads the workflow and returns the deduplicated list of // creator peer IDs for all its storage and compute resources. // These are the peers whose planners must be watched by a check stream. func GetWorkflowPeerIDs(wfID string, request *tools.APIRequest) ([]string, error) { obj, code, err := workflow.NewAccessor(request).LoadOne(wfID) if code != 200 || err != nil { msg := "could not load workflow " + wfID if err != nil { msg += ": " + err.Error() } return nil, errors.New(msg) } wf := obj.(*workflow.Workflow) if wf.Graph == nil { return nil, nil } seen := map[string]bool{} var peerIDs []string for _, item := range wf.GetGraphItems(wf.Graph.IsStorage) { _, res := item.GetResource() if res == nil { continue } if id := res.GetCreatorID(); id != "" && !seen[id] { seen[id] = true peerIDs = append(peerIDs, id) } } for _, item := range wf.GetGraphItems(wf.Graph.IsCompute) { _, res := item.GetResource() if res == nil { continue } if id := res.GetCreatorID(); id != "" && !seen[id] { seen[id] = true peerIDs = append(peerIDs, id) } } realPeersID := []string{} access := oclib.NewRequestAdmin(oclib.LibDataEnum(tools.PEER), nil) for _, id := range peerIDs { if data := access.LoadOne(id); data.Data != nil { realPeersID = append(realPeersID, data.ToPeer().PeerID) } } return realPeersID, nil } func FormatOptTime(t *time.Time) string { if t == nil { return "open" } return t.Format(time.RFC3339) } func Notify[T interface{}](mu *sync.RWMutex, registry map[string][]chan T, key string, toAdd T) { mu.RLock() subs := registry[key] mu.RUnlock() for _, ch := range subs { select { case ch <- toAdd: default: } } } func Propalgate(peerID string, message tools.PropalgationMessage) { b, _ := json.Marshal(message) tools.NewNATSCaller().SetNATSPub(tools.PROPALGATION_EVENT, tools.NATSResponse{ FromApp: "oc-scheduler", Datatype: -1, Method: int(tools.PROPALGATION_EVENT), Payload: b, }) }