1
2
3
4
19
20 package nsenter
21
22 import (
23 "context"
24 "errors"
25 "fmt"
26 "os"
27 "path/filepath"
28 "strings"
29
30 "k8s.io/klog/v2"
31 "k8s.io/utils/exec"
32 )
33
34 const (
35
36
37 DefaultHostRootFsPath = "/rootfs"
38
39 mountNsPath = "/proc/1/ns/mnt"
40
41 nsenterPath = "nsenter"
42 )
43
44
45 type Nsenter = NSEnter
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 type NSEnter struct {
75
76 paths map[string]string
77
78
79 hostRootFsPath string
80
81
82 executor exec.Interface
83 }
84
85
86 func NewNsenter(hostRootFsPath string, executor exec.Interface) (*NSEnter, error) {
87 ne := &NSEnter{
88 hostRootFsPath: hostRootFsPath,
89 executor: executor,
90 }
91 if err := ne.initPaths(); err != nil {
92 return nil, err
93 }
94 return ne, nil
95 }
96
97 func (ne *NSEnter) initPaths() error {
98 ne.paths = map[string]string{}
99 binaries := []string{
100 "mount",
101 "findmnt",
102 "umount",
103 "systemd-run",
104 "stat",
105 "touch",
106 "mkdir",
107 "sh",
108 "chmod",
109 "realpath",
110 }
111
112 for _, binary := range binaries {
113
114 for _, path := range []string{"/", "/bin", "/usr/sbin", "/usr/bin"} {
115 binPath := filepath.Join(path, binary)
116 if _, err := os.Stat(filepath.Join(ne.hostRootFsPath, binPath)); err != nil {
117 continue
118 }
119 ne.paths[binary] = binPath
120 break
121 }
122
123 if ne.paths[binary] == "" && binary != "systemd-run" {
124 return fmt.Errorf("unable to find %v", binary)
125 }
126 }
127 return nil
128 }
129
130
131 func (ne *NSEnter) Exec(cmd string, args []string) exec.Cmd {
132 hostProcMountNsPath := filepath.Join(ne.hostRootFsPath, mountNsPath)
133 fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"},
134 append([]string{ne.AbsHostPath(cmd)}, args...)...)
135 klog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs)
136 return ne.executor.Command(nsenterPath, fullArgs...)
137 }
138
139
140 func (ne *NSEnter) Command(cmd string, args ...string) exec.Cmd {
141 return ne.Exec(cmd, args)
142 }
143
144
145 func (ne *NSEnter) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
146 hostProcMountNsPath := filepath.Join(ne.hostRootFsPath, mountNsPath)
147 fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"},
148 append([]string{ne.AbsHostPath(cmd)}, args...)...)
149 klog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs)
150 return ne.executor.CommandContext(ctx, nsenterPath, fullArgs...)
151 }
152
153
154 func (ne *NSEnter) LookPath(file string) (string, error) {
155 return "", fmt.Errorf("not implemented, error looking up : %s", file)
156 }
157
158
159 func (ne *NSEnter) AbsHostPath(command string) string {
160 path, ok := ne.paths[command]
161 if !ok {
162 return command
163 }
164 return path
165 }
166
167
168 func (ne *NSEnter) SupportsSystemd() (string, bool) {
169 systemdRunPath, ok := ne.paths["systemd-run"]
170 return systemdRunPath, ok && systemdRunPath != ""
171 }
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 func (ne *NSEnter) EvalSymlinks(pathname string, mustExist bool) (string, error) {
190 var args []string
191 if mustExist {
192
193 args = []string{"-e", pathname}
194 } else {
195
196 args = []string{"-m", pathname}
197 }
198 outBytes, err := ne.Exec("realpath", args).CombinedOutput()
199 if err != nil {
200 klog.Infof("failed to resolve symbolic links on %s: %v", pathname, err)
201 return "", err
202 }
203 return strings.TrimSpace(string(outBytes)), nil
204 }
205
206
207
208
209 func (ne *NSEnter) KubeletPath(pathname string) string {
210 return filepath.Join(ne.hostRootFsPath, pathname)
211 }
212
213
214
215
216
217
218 func NewFakeNsenter(rootfsPath string) (*NSEnter, error) {
219 executor := &fakeExec{
220 rootfsPath: rootfsPath,
221 }
222
223 bin := filepath.Join(rootfsPath, "bin")
224 if err := os.Symlink("/bin", bin); err != nil {
225 return nil, err
226 }
227
228 usr := filepath.Join(rootfsPath, "usr")
229 if err := os.Mkdir(usr, 0755); err != nil {
230 return nil, err
231 }
232 usrbin := filepath.Join(usr, "bin")
233 if err := os.Symlink("/usr/bin", usrbin); err != nil {
234 return nil, err
235 }
236 usrsbin := filepath.Join(usr, "sbin")
237 if err := os.Symlink("/usr/sbin", usrsbin); err != nil {
238 return nil, err
239 }
240
241 return NewNsenter(rootfsPath, executor)
242 }
243
244 type fakeExec struct {
245 rootfsPath string
246 }
247
248 func (f fakeExec) Command(cmd string, args ...string) exec.Cmd {
249
250 realCmd := args[2]
251 realArgs := args[3:]
252 return exec.New().Command(realCmd, realArgs...)
253 }
254
255 func (fakeExec) LookPath(file string) (string, error) {
256 return "", errors.New("not implemented")
257 }
258
259 func (fakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
260 return nil
261 }
262
263 var _ exec.Interface = fakeExec{}
264 var _ exec.Interface = &NSEnter{}
265
View as plain text