github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-collection/pubsub.go (about) 1 // Copyright 2019-2022 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package containercollection 16 17 import ( 18 "sync" 19 "time" 20 ) 21 22 type EventType int 23 24 type FuncNotify func(event PubSubEvent) 25 26 const ( 27 EventTypeAddContainer EventType = iota 28 EventTypeRemoveContainer 29 ) 30 31 func (e *EventType) String() string { 32 switch *e { 33 case EventTypeRemoveContainer: 34 return "DELETED" 35 case EventTypeAddContainer: 36 fallthrough 37 default: 38 return "CREATED" 39 } 40 } 41 42 func EventTypeFromString(s string) EventType { 43 switch s { 44 case "DELETED": 45 return EventTypeRemoveContainer 46 case "CREATED": 47 fallthrough 48 default: 49 return EventTypeAddContainer 50 } 51 } 52 53 func (e *EventType) MarshalText() (text []byte, err error) { 54 return []byte(e.String()), nil 55 } 56 57 func (e *EventType) UnmarshalText(bytes []byte) error { 58 *e = EventTypeFromString(string(bytes)) 59 return nil 60 } 61 62 type PubSubEvent struct { 63 Timestamp string `json:"timestamp,omitempty" column:"timestamp,maxWidth:30" columnTags:"runtime"` 64 Type EventType `json:"event" column:"event,maxWidth:10" columnTags:"runtime"` 65 Container *Container `json:"container"` 66 } 67 68 // GadgetPubSub provides a synchronous publish subscribe mechanism for gadgets 69 // to be informed of container creation and deletion. It needs to be 70 // synchronous so that gadgets have time to attach their tracer before the 71 // container is started. 72 type GadgetPubSub struct { 73 mu sync.RWMutex 74 75 // subs is the set of subscribers 76 subs map[interface{}]FuncNotify 77 } 78 79 func NewGadgetPubSub() *GadgetPubSub { 80 return &GadgetPubSub{ 81 subs: make(map[interface{}]FuncNotify), 82 } 83 } 84 85 // Subscribe registers the callback to be called for every container event 86 // published with Publish(). Optionally, the caller can pass an initializer() 87 // function that is guaranteed to be called before any new container events are 88 // published. 89 func (g *GadgetPubSub) Subscribe(key interface{}, callback FuncNotify, initializer func()) { 90 g.mu.Lock() 91 defer g.mu.Unlock() 92 93 g.subs[key] = callback 94 95 if initializer != nil { 96 initializer() 97 } 98 } 99 100 func (g *GadgetPubSub) Unsubscribe(key interface{}) { 101 g.mu.Lock() 102 defer g.mu.Unlock() 103 104 delete(g.subs, key) 105 } 106 107 func (g *GadgetPubSub) Publish(eventType EventType, container *Container) { 108 // Make a copy so we don't keep the lock while actually publishing 109 g.mu.RLock() 110 copiedSubs := []FuncNotify{} 111 for _, callback := range g.subs { 112 copiedSubs = append(copiedSubs, callback) 113 } 114 g.mu.RUnlock() 115 116 var wg sync.WaitGroup 117 for _, callback := range copiedSubs { 118 wg.Add(1) 119 go func(callback FuncNotify) { 120 event := PubSubEvent{ 121 Timestamp: time.Now().Format(time.RFC3339), 122 Type: eventType, 123 Container: container, 124 } 125 callback(event) 126 wg.Done() 127 }(callback) 128 } 129 130 wg.Wait() 131 }