-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathturtlefinder_test.go
More file actions
217 lines (192 loc) · 7.33 KB
/
Copy pathturtlefinder_test.go
File metadata and controls
217 lines (192 loc) · 7.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// (c) Siemens AG 2023
//
// SPDX-License-Identifier: MIT
package turtlefinder
import (
"context"
"fmt"
"log/slog"
"os"
"sync"
"time"
"github.com/siemens/turtlefinder/v2/activator/podman"
"github.com/siemens/turtlefinder/v2/internal/testslog"
"github.com/siemens/turtlefinder/v2/matcher"
"github.com/thediveo/lxkns/discover"
"github.com/thediveo/lxkns/model"
"github.com/thediveo/morbyd/v2"
"github.com/thediveo/morbyd/v2/build"
"github.com/thediveo/morbyd/v2/exec"
"github.com/thediveo/morbyd/v2/run"
"github.com/thediveo/morbyd/v2/session"
"github.com/thediveo/morbyd/v2/timestamper"
"github.com/thediveo/whalewatcher/v2/watcher/containerd"
"github.com/thediveo/whalewatcher/v2/watcher/moby"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gleak"
. "github.com/thediveo/fdooze"
. "github.com/thediveo/success"
)
const (
fedoraTag = "39"
pindName = "turtlefinder-pind"
pindImageName = "siemens/turtlefinder-pind"
canaryContainerName = "canary"
canaryImageRef = "docker.io/library/busybox:latest"
spinupTimeout = 30 * time.Second
spinupPolling = 500 * time.Millisecond
)
var _ = Describe("turtle finder", Ordered, Serial, func() {
var pindCntr *morbyd.Container
BeforeAll(func(ctx context.Context) {
if os.Getuid() != 0 {
Skip("needs root")
}
goodfds := Filedescriptors()
goodgos := Goroutines() // avoid other failed goroutine tests to spill over
DeferCleanup(func() {
Eventually(Goroutines).WithTimeout(goroutinesUnwindTimeout).WithPolling(goroutinesUnwindPolling).
ShouldNot(HaveLeaked(goodgos))
Expect(Filedescriptors()).NotTo(HaveLeakedFds(goodfds))
})
By("creating a new Docker session for testing")
sess := Successful(morbyd.NewSession(ctx,
session.WithAutoCleaning("test.turtlefinder=turtlefinder")))
DeferCleanup(func(ctx context.Context) {
By("auto-cleaning the session")
sess.Close(ctx)
})
By("spinning up a Docker container with a podman system demon^Wservice")
// The necessary container start arguments loosely base on
// https://www.redhat.com/sysadmin/podman-inside-container but had to be
// heavily modified because they didn't work out as is, for whatever
// reasons. This is now a mash-up of the args used to get the KinD
// base-based images correctly working and some "spirit" of the before
// mentioned RedHat blog post.
//
// Lesson learnt: podman in Docker is much more fragile than the podmen
// want us to believe.
//
// docker run -it --rm --name pind
// --privileged \
// --cgroupns=private \
// --tmpfs /tmp \
// --tmpfs /run \
// --volume /var \
// --device=/dev/fuse \
// pind
//
// Please note that the initial build of the podman-in-Docker image is
// really slow, as fedora installs lots of things.
Expect(sess.BuildImage(ctx, "./_test/pind",
build.WithTag(pindImageName),
build.WithBuildArg("FEDORA_TAG="+fedoraTag),
build.WithOutput(timestamper.New(GinkgoWriter)))).
Error().NotTo(HaveOccurred())
pindCntr = Successful(sess.Run(ctx, pindImageName,
run.WithName(pindName),
run.WithAutoRemove(),
run.WithPrivileged(),
run.WithSecurityOpt("label=disable"),
run.WithCgroupnsMode("private"),
run.WithVolume("/var"),
run.WithTmpfs("/tmp"),
run.WithTmpfs("/run"),
run.WithDevice("/dev/fuse"),
run.WithCombinedOutput(timestamper.New(GinkgoWriter))))
})
BeforeEach(clearCachedDetectorPlugins)
BeforeEach(func() {
goodfds := Filedescriptors()
goodgos := Goroutines() // avoid other failed goroutine tests to spill over
DeferCleanup(func() {
Eventually(Goroutines).WithTimeout(goroutinesUnwindTimeout).WithPolling(goroutinesUnwindPolling).
ShouldNot(HaveLeaked(goodgos))
Expect(Filedescriptors()).NotTo(HaveLeakedFds(goodfds))
})
})
BeforeEach(func() {
oldDefault := slog.Default()
DeferCleanup(func() { slog.SetDefault(oldDefault) })
_ = testslog.SetDefault(slog.LevelInfo, GinkgoWriter)
})
It("it finds and updates socket activators", func(ctx context.Context) {
By("setting up a socket activator object for our systemd PID 1")
ctx, cancel := context.WithCancel(ctx)
defer cancel()
tf := New(
func() context.Context { return ctx },
WithGettingOnlineWait(5*time.Second))
pidmap := model.NewProcessTable(false)
var wg sync.WaitGroup
tf.updateActivators(pidmap, &wg)
done := make(chan struct{})
go func() {
defer close(done)
wg.Wait()
}()
By("waiting for the update to be done")
Eventually(done).Within(5 * time.Second).ProbeEvery(250 * time.Millisecond).
Should(BeClosed())
By("winding down")
cancel()
})
// And Now ... All Together! This is becoming more extreme by the version
// tag :D
//
// In order to avoid all the "nice" problems with installing podman distro
// packages side-by-side into the host, just to find out that the distro
// packagers destroy any existing docker installation, we run a podman demon
// ("don't call ..." *plonk* ) inside a Docker container, as a
// socket-activated service. And we create a podman workload inside that
// container in the insane hope of discovering it. Ah, so many demons...
It("finds docker, containerd, and podman-in-docker", func(ctx context.Context) {
if os.Getuid() != 0 {
Skip("needs root")
}
By("creating a new turtle finder")
ctx, cancel := context.WithCancel(ctx)
tf := New(func() context.Context { return ctx })
Expect(tf).NotTo(BeNil())
defer cancel()
defer tf.Close()
By("discovering at least three types of engines")
// Ironically we should find containerd also when running this test on
// Docker Desktop on WSL2, where the Docker daemon lives inside a
// containerd container. In this case, we'll see another containerd
// instance that is the Docker daemon's sidekick.
Eventually(func() []*model.ContainerEngine {
lxdisco := discover.Namespaces(discover.WithFullDiscovery())
_ = tf.Containers(ctx, lxdisco.Processes, lxdisco.PIDMap)
return tf.Engines()
}).Within(spinupTimeout).ProbeEvery(spinupPolling).
Should(ContainElements(
HaveEngine(moby.Type, `^unix:///proc/\d+/root/run/docker.sock$`),
HaveEngine(containerd.Type, `^unix:///proc/\d+/root/run/(?:docker/)?containerd/containerd.sock$`),
HaveEngine(podman.Type, `^unix:///proc/\d+/root/run/podman/podman.sock$`),
))
By("checking for the presence of our dedicated podman-in-Docker engine instance...")
pid := Successful(pindCntr.PID(ctx))
Expect(tf.Engines()).To(ContainElement(
HaveEngine(podman.Type, fmt.Sprintf(`^unix:///proc/%d/root/run/podman/podman.sock$`, pid)),
), "missing podman-in-Docker engine")
By("creating podman workload")
pmCmd := Successful(pindCntr.Exec(ctx,
exec.Command("podman", "run", "-d", "-it", "--rm",
"--name", canaryContainerName, "--net", "host", canaryImageRef),
exec.WithCombinedOutput(timestamper.New(GinkgoWriter))))
Expect(pmCmd.Wait(ctx)).To(BeZero())
By("discovering podman workload and its managing podman engine hierarchy")
Eventually(func() []*model.Container {
lxdisco := discover.Namespaces(discover.WithFullDiscovery())
return tf.Containers(ctx, lxdisco.Processes, lxdisco.PIDMap)
}).Within(spinupTimeout).ProbeEvery(spinupPolling).
Should(ContainElement(And(
matcher.HaveContainerNameID(canaryContainerName),
HaveField("Type", podman.Type),
HaveField("Labels", HaveKeyWithValue(
TurtlefinderContainerPrefixLabelName, pindName)),
)))
})
})