tetragon

Форк
0
204 строки · 5.6 Кб
1
// SPDX-License-Identifier: Apache-2.0
2
// Copyright Authors of Tetragon
3

4
package fsscan
5

6
import (
7
	"errors"
8
	"fmt"
9
	"io/fs"
10
	"os"
11
	"path/filepath"
12
	"strings"
13

14
	"github.com/cilium/tetragon/pkg/cgroups"
15
	"github.com/cilium/tetragon/pkg/logger"
16
	"github.com/google/uuid"
17
)
18

19
var (
20
	// special error to inidicate that fileystem scanning found a file that
21
	// matches the container id, without, however, matching the pod id.
22
	ErrContainerPathWithoutMatchingPodID = errors.New("found cgroup file that matches the container id, but not the pod id")
23
)
24

25
// FsScanner is a utility for scanning the filesystem to find container cgroup directories.
26
type FsScanner interface {
27
	// FindContainer returns:
28
	//   path, nil: if the container cgroup was found
29
	//   path, ErrContainerPathWithoutMatchingPodID: if the container cgroup was found, but the pod id did not match the parent directory
30
	//   "", err: if the container cgroup was not found.
31
	//
32
	// NB: FindContainerPath returns ErrContainerPathWithoutMatchingPodID only if the directory
33
	// matching the pod id did not existed in the fs.
34
	//
35
	// Callers need to serialize concurrent access to this function on their own.
36
	FindContainerPath(podID uuid.UUID, containerID string) (string, error)
37
}
38

39
func New() FsScanner {
40
	return &fsScannerState{}
41
}
42

43
type fsScannerState struct {
44
	knownPodDirs []string
45
	root         string
46
	rootErr      error
47
}
48

49
// We 've seen some cases, where the container cgroup file is not in a
50
// directory that mathces the pod id. To handle these cases, do a full scan of
51
// the root to find a directory with the container id.
52
func fsFullContainerScan(root string, containerID string) string {
53
	found := errors.New("Found")
54
	retPath := ""
55
	// NB: there might be more efficient ways of searching the file-system than WalkDir, but
56
	// use that for now and we can improve later as needed.
57
	filepath.WalkDir(root, func(path string, dentry fs.DirEntry, err error) error {
58
		if err != nil || !dentry.IsDir() {
59
			return nil
60
		}
61
		base := filepath.Base(path)
62
		if strings.Contains(base, containerID) {
63
			retPath = path
64
			return found
65
		}
66
		return nil
67
	})
68
	return retPath
69
}
70

71
// FindContainer implements FindContainer method from FsScanner
72
func (fs *fsScannerState) FindContainerPath(podID uuid.UUID, containerID string) (string, error) {
73

74
	// first, check the known (cached) locations
75
	for _, loc := range fs.knownPodDirs {
76
		podDir, containerDir := findPodAndContainerDirectory(loc, podID, containerID)
77
		if podDir == "" {
78
			continue
79
		}
80

81
		if containerDir != "" {
82
			return filepath.Join(podDir, containerDir), nil
83
		}
84

85
		// found the pod dir, but not the container dir. Return an error
86
		return "", fmt.Errorf("found pod dir=%s but failed to find container for id=%s", podDir, containerID)
87
	}
88

89
	if fs.root == "" && fs.rootErr == nil {
90
		fs.root, fs.rootErr = cgroups.HostCgroupRoot()
91
		if fs.rootErr != nil {
92
			logger.GetLogger().WithError(fs.rootErr).Warnf("failed to retrieve host cgroup root")
93
		}
94
	}
95
	if fs.rootErr != nil {
96
		return "", fmt.Errorf("no cgroup root")
97
	}
98

99
	podPath := fsFindPodPath(fs.root, podID)
100
	if podPath == "" {
101
		contPath := fsFullContainerScan(fs.root, containerID)
102
		if contPath == "" {
103
			return "", fmt.Errorf("no directory found that matches container-id '%s'", containerID)
104
		}
105
		return contPath, ErrContainerPathWithoutMatchingPodID
106
	}
107
	podDir := filepath.Dir(podPath)
108
	// found a new pod directory, added it to the cached locations
109
	fs.knownPodDirs = append(fs.knownPodDirs, podDir)
110
	logger.GetLogger().Infof("adding %s to cgroup pod directories", podDir)
111
	containerDir := findContainerDirectory(podPath, containerID)
112
	if containerDir == "" {
113
		return "", fmt.Errorf("found pod dir=%s but failed to find container for id=%s", podPath, containerID)
114
	}
115
	return filepath.Join(podPath, containerDir), nil
116
}
117

118
func podDirMatcher(podID uuid.UUID) func(p string) bool {
119
	s1 := podID.String()
120
	// replace '-' with '_' in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
121
	s2 := strings.Replace(s1, "-", "_", 4)
122
	return func(p string) bool {
123
		if strings.Contains(p, s1) {
124
			return true
125
		}
126
		if strings.Contains(p, s2) {
127
			return true
128
		}
129
		return false
130
	}
131
}
132

133
func findPodAndContainerDirectory(poddir string, podID uuid.UUID, containerID string) (string, string) {
134
	podpath := findPodDirectory(poddir, podID)
135
	if podpath == "" {
136
		return "", ""
137
	}
138
	podpath = filepath.Join(poddir, podpath)
139
	contpath := findContainerDirectory(podpath, containerID)
140
	return podpath, contpath
141
}
142

143
func findContainerDirectory(podpath string, containerID string) string {
144
	entries, err := os.ReadDir(podpath)
145
	if err != nil {
146
		return ""
147
	}
148
	for _, dentry := range entries {
149
		if !dentry.IsDir() {
150
			continue
151
		}
152

153
		name := dentry.Name()
154
		// skip crio's conmon container
155
		if strings.Contains(name, "crio-conmon") {
156
			continue
157
		}
158
		if strings.Contains(name, containerID) {
159
			return name
160
		}
161
	}
162

163
	return ""
164
}
165

166
func findPodDirectory(poddir string, podID uuid.UUID) string {
167
	podMatcher := podDirMatcher(podID)
168
	entries, err := os.ReadDir(poddir)
169
	if err != nil {
170
		return ""
171
	}
172
	for _, dentry := range entries {
173
		if !dentry.IsDir() {
174
			continue
175
		}
176

177
		name := dentry.Name()
178
		if podMatcher(name) {
179
			return name
180
		}
181
	}
182

183
	return ""
184
}
185

186
func fsFindPodPath(root string, podID uuid.UUID) string {
187
	found := errors.New("Found")
188
	podMatcher := podDirMatcher(podID)
189
	retPath := ""
190
	// NB: there might be more efficient ways of searching the file-system than WalkDir, but
191
	// use that for now and we can improve later as needed.
192
	filepath.WalkDir(root, func(path string, dentry fs.DirEntry, err error) error {
193
		if err != nil || !dentry.IsDir() {
194
			return nil
195
		}
196
		base := filepath.Base(path)
197
		if podMatcher(base) {
198
			retPath = path
199
			return found
200
		}
201
		return nil
202
	})
203
	return retPath
204
}
205

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.