watcher.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  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. // Package container defines types for sub-container events and also
  15. // defines an interface for container operation handlers.
  16. package raw
  17. import (
  18. "fmt"
  19. "io/ioutil"
  20. "os"
  21. "path"
  22. "strings"
  23. inotify "k8s.io/utils/inotify"
  24. "github.com/google/cadvisor/container/common"
  25. "github.com/google/cadvisor/container/libcontainer"
  26. "github.com/google/cadvisor/watcher"
  27. "k8s.io/klog/v2"
  28. )
  29. type rawContainerWatcher struct {
  30. // Absolute path to the root of the cgroup hierarchies
  31. cgroupPaths map[string]string
  32. // Inotify event watcher.
  33. watcher *common.InotifyWatcher
  34. // Signal for watcher thread to stop.
  35. stopWatcher chan error
  36. }
  37. func NewRawContainerWatcher() (watcher.ContainerWatcher, error) {
  38. cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(nil)
  39. if err != nil {
  40. return nil, fmt.Errorf("failed to get cgroup subsystems: %v", err)
  41. }
  42. if len(cgroupSubsystems) == 0 {
  43. return nil, fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
  44. }
  45. watcher, err := common.NewInotifyWatcher()
  46. if err != nil {
  47. return nil, err
  48. }
  49. rawWatcher := &rawContainerWatcher{
  50. cgroupPaths: cgroupSubsystems,
  51. watcher: watcher,
  52. stopWatcher: make(chan error),
  53. }
  54. return rawWatcher, nil
  55. }
  56. func (w *rawContainerWatcher) Start(events chan watcher.ContainerEvent) error {
  57. // Watch this container (all its cgroups) and all subdirectories.
  58. watched := make([]string, 0)
  59. for _, cgroupPath := range w.cgroupPaths {
  60. _, err := w.watchDirectory(events, cgroupPath, "/")
  61. if err != nil {
  62. for _, watchedCgroupPath := range watched {
  63. _, removeErr := w.watcher.RemoveWatch("/", watchedCgroupPath)
  64. if removeErr != nil {
  65. klog.Warningf("Failed to remove inotify watch for %q with error: %v", watchedCgroupPath, removeErr)
  66. }
  67. }
  68. return err
  69. }
  70. watched = append(watched, cgroupPath)
  71. }
  72. // Process the events received from the kernel.
  73. go func() {
  74. for {
  75. select {
  76. case event := <-w.watcher.Event():
  77. err := w.processEvent(event, events)
  78. if err != nil {
  79. klog.Warningf("Error while processing event (%+v): %v", event, err)
  80. }
  81. case err := <-w.watcher.Error():
  82. klog.Warningf("Error while watching %q: %v", "/", err)
  83. case <-w.stopWatcher:
  84. err := w.watcher.Close()
  85. if err == nil {
  86. w.stopWatcher <- err
  87. return
  88. }
  89. }
  90. }
  91. }()
  92. return nil
  93. }
  94. func (w *rawContainerWatcher) Stop() error {
  95. // Rendezvous with the watcher thread.
  96. w.stopWatcher <- nil
  97. return <-w.stopWatcher
  98. }
  99. // Watches the specified directory and all subdirectories. Returns whether the path was
  100. // already being watched and an error (if any).
  101. func (w *rawContainerWatcher) watchDirectory(events chan watcher.ContainerEvent, dir string, containerName string) (bool, error) {
  102. // Don't watch .mount cgroups because they never have containers as sub-cgroups. A single container
  103. // can have many .mount cgroups associated with it which can quickly exhaust the inotify watches on a node.
  104. if strings.HasSuffix(containerName, ".mount") {
  105. return false, nil
  106. }
  107. alreadyWatching, err := w.watcher.AddWatch(containerName, dir)
  108. if err != nil {
  109. return alreadyWatching, err
  110. }
  111. // Remove the watch if further operations failed.
  112. cleanup := true
  113. defer func() {
  114. if cleanup {
  115. _, err := w.watcher.RemoveWatch(containerName, dir)
  116. if err != nil {
  117. klog.Warningf("Failed to remove inotify watch for %q: %v", dir, err)
  118. }
  119. }
  120. }()
  121. // TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime.
  122. // Watch subdirectories as well.
  123. entries, err := ioutil.ReadDir(dir)
  124. if err != nil {
  125. return alreadyWatching, err
  126. }
  127. for _, entry := range entries {
  128. if entry.IsDir() {
  129. entryPath := path.Join(dir, entry.Name())
  130. subcontainerName := path.Join(containerName, entry.Name())
  131. alreadyWatchingSubDir, err := w.watchDirectory(events, entryPath, subcontainerName)
  132. if err != nil {
  133. klog.Errorf("Failed to watch directory %q: %v", entryPath, err)
  134. if os.IsNotExist(err) {
  135. // The directory may have been removed before watching. Try to watch the other
  136. // subdirectories. (https://github.com/kubernetes/kubernetes/issues/28997)
  137. continue
  138. }
  139. return alreadyWatching, err
  140. }
  141. // since we already missed the creation event for this directory, publish an event here.
  142. if !alreadyWatchingSubDir {
  143. go func() {
  144. events <- watcher.ContainerEvent{
  145. EventType: watcher.ContainerAdd,
  146. Name: subcontainerName,
  147. WatchSource: watcher.Raw,
  148. }
  149. }()
  150. }
  151. }
  152. }
  153. cleanup = false
  154. return alreadyWatching, nil
  155. }
  156. func (w *rawContainerWatcher) processEvent(event *inotify.Event, events chan watcher.ContainerEvent) error {
  157. // Convert the inotify event type to a container create or delete.
  158. var eventType watcher.ContainerEventType
  159. switch {
  160. case (event.Mask & inotify.InCreate) > 0:
  161. eventType = watcher.ContainerAdd
  162. case (event.Mask & inotify.InDelete) > 0:
  163. eventType = watcher.ContainerDelete
  164. case (event.Mask & inotify.InMovedFrom) > 0:
  165. eventType = watcher.ContainerDelete
  166. case (event.Mask & inotify.InMovedTo) > 0:
  167. eventType = watcher.ContainerAdd
  168. default:
  169. // Ignore other events.
  170. return nil
  171. }
  172. // Derive the container name from the path name.
  173. var containerName string
  174. for _, mount := range w.cgroupPaths {
  175. mountLocation := path.Clean(mount) + "/"
  176. if strings.HasPrefix(event.Name, mountLocation) {
  177. containerName = event.Name[len(mountLocation)-1:]
  178. break
  179. }
  180. }
  181. if containerName == "" {
  182. return fmt.Errorf("unable to detect container from watch event on directory %q", event.Name)
  183. }
  184. // Maintain the watch for the new or deleted container.
  185. switch eventType {
  186. case watcher.ContainerAdd:
  187. // New container was created, watch it.
  188. alreadyWatched, err := w.watchDirectory(events, event.Name, containerName)
  189. if err != nil {
  190. return err
  191. }
  192. // Only report container creation once.
  193. if alreadyWatched {
  194. return nil
  195. }
  196. case watcher.ContainerDelete:
  197. // Container was deleted, stop watching for it.
  198. lastWatched, err := w.watcher.RemoveWatch(containerName, event.Name)
  199. if err != nil {
  200. return err
  201. }
  202. // Only report container deletion once.
  203. if !lastWatched {
  204. return nil
  205. }
  206. default:
  207. return fmt.Errorf("unknown event type %v", eventType)
  208. }
  209. // Deliver the event.
  210. events <- watcher.ContainerEvent{
  211. EventType: eventType,
  212. Name: containerName,
  213. WatchSource: watcher.Raw,
  214. }
  215. return nil
  216. }