github.com/containerd/Containerd@v1.4.13/pkg/oom/v2/v2.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package v2 20 21 import ( 22 "context" 23 24 cgroupsv2 "github.com/containerd/cgroups/v2" 25 eventstypes "github.com/containerd/containerd/api/events" 26 "github.com/containerd/containerd/pkg/oom" 27 "github.com/containerd/containerd/runtime" 28 "github.com/containerd/containerd/runtime/v2/shim" 29 "github.com/pkg/errors" 30 "github.com/sirupsen/logrus" 31 ) 32 33 // New returns an implementation that listens to OOM events 34 // from a container's cgroups. 35 func New(publisher shim.Publisher) (oom.Watcher, error) { 36 return &watcher{ 37 itemCh: make(chan item), 38 publisher: publisher, 39 }, nil 40 } 41 42 // watcher implementation for handling OOM events from a container's cgroup 43 type watcher struct { 44 itemCh chan item 45 publisher shim.Publisher 46 } 47 48 type item struct { 49 id string 50 ev cgroupsv2.Event 51 err error 52 } 53 54 // Close closes the watcher 55 func (w *watcher) Close() error { 56 return nil 57 } 58 59 // Run the loop 60 func (w *watcher) Run(ctx context.Context) { 61 lastOOMMap := make(map[string]uint64) // key: id, value: ev.OOM 62 for { 63 select { 64 case <-ctx.Done(): 65 w.Close() 66 return 67 case i := <-w.itemCh: 68 if i.err != nil { 69 delete(lastOOMMap, i.id) 70 continue 71 } 72 lastOOM := lastOOMMap[i.id] 73 if i.ev.OOM > lastOOM { 74 if err := w.publisher.Publish(ctx, runtime.TaskOOMEventTopic, &eventstypes.TaskOOM{ 75 ContainerID: i.id, 76 }); err != nil { 77 logrus.WithError(err).Error("publish OOM event") 78 } 79 } 80 if i.ev.OOM > 0 { 81 lastOOMMap[i.id] = i.ev.OOM 82 } 83 } 84 } 85 } 86 87 // Add cgroups.Cgroup to the epoll monitor 88 func (w *watcher) Add(id string, cgx interface{}) error { 89 cg, ok := cgx.(*cgroupsv2.Manager) 90 if !ok { 91 return errors.Errorf("expected *cgroupsv2.Manager, got: %T", cgx) 92 } 93 // FIXME: cgroupsv2.Manager does not support closing eventCh routine currently. 94 // The routine shuts down when an error happens, mostly when the cgroup is deleted. 95 eventCh, errCh := cg.EventChan() 96 go func() { 97 for { 98 i := item{id: id} 99 select { 100 case ev := <-eventCh: 101 i.ev = ev 102 w.itemCh <- i 103 case err := <-errCh: 104 i.err = err 105 w.itemCh <- i 106 // we no longer get any event/err when we got an err 107 logrus.WithError(err).Warn("error from *cgroupsv2.Manager.EventChan") 108 return 109 } 110 } 111 }() 112 return nil 113 }