Skip to content

Commit

Permalink
Merge pull request #7 from siemens/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
thediveo authored Jan 9, 2024
2 parents f5af678 + 6e88ff4 commit 98d9683
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 41 deletions.
41 changes: 28 additions & 13 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,29 @@ type Engine struct {
ID string // engine ID.
Version string // engine version.
Done chan struct{} // closed when watch is done/has terminated.
Cleanerfn func(*Engine) // if non-nil, called when the Engine is getting removed.
PPIDHint model.PIDType // PID of engine's process; for container PID translation.
}

// NewEngine returns a new Engine given the specified watcher. The Engine is
// already "warming up" and has started watching (using the given context).
func NewEngine(ctx context.Context, w watcher.Watcher) *Engine {
// NewEngine returns a new Engine given the specified watcher. As NewEngine
// returns, the Engine is already "warming up" and has started watching (using
// the given context).
//
// ppidhint optionally specifies a container engine's immediate parent process.
// This information is later necessary for lxkns to correctly translate
// container PIDs. When activating a socket-activated engine, the process tree
// scan does never include the engine, as this is only activated after the scan.
// In order to still allow lxkns to translate container PIDs related to newly
// socket-activated engines, we assume that the engine's parent process PID is
// in the same PID namespace, so we can also use that for correct PID
// translation.
func NewEngine(ctx context.Context, w watcher.Watcher, ppidhint model.PIDType) *Engine {
idctx, cancel := context.WithTimeout(ctx, 2*time.Second)
e := &Engine{
Watcher: w,
ID: w.ID(idctx),
Version: w.Version(idctx),
Done: make(chan struct{}, 1), // might never be picked up in some situations
Watcher: w,
ID: w.ID(idctx),
Version: w.Version(idctx),
Done: make(chan struct{}, 1), // might never be picked up in some situations
PPIDHint: ppidhint,
}
cancel() // ensure to quickly release cancel, silence linter
log.Infof("watching %s container engine (PID %d) with ID '%s', version '%s'",
Expand All @@ -55,13 +66,17 @@ func NewEngine(ctx context.Context, w watcher.Watcher) *Engine {

// Containers returns the alive containers managed by this engine, using the
// associated watcher.
//
// The containers returned will reference a model.ContainerEngine and thus are
// decoupled from a turtlefinder's (container) Engine object.
func (e *Engine) Containers(ctx context.Context) []*model.Container {
eng := &model.ContainerEngine{
ID: e.ID,
Type: e.Watcher.Type(),
Version: e.Version,
API: e.Watcher.API(),
PID: model.PIDType(e.Watcher.PID()),
ID: e.ID,
Type: e.Watcher.Type(),
Version: e.Version,
API: e.Watcher.API(),
PID: model.PIDType(e.Watcher.PID()),
PPIDHint: e.PPIDHint,
}
// Adapt the whalewatcher container model to the lxkns container model,
// where the latter takes container engines and groups into account of its
Expand Down
2 changes: 1 addition & 1 deletion engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var _ = Describe("container engine", Serial, Ordered, func() {
Expect(err).NotTo(HaveOccurred())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
engine := NewEngine(ctx, w)
engine := NewEngine(ctx, w, 0)
Expect(engine.ID).NotTo(BeZero())

Consistently(engine.IsAlive).Should(BeTrue())
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ require (
github.com/ory/dockertest/v3 v3.10.0
github.com/thediveo/fdooze v0.3.1
github.com/thediveo/go-plugger/v3 v3.1.0
github.com/thediveo/lxkns v0.30.0
github.com/thediveo/lxkns v0.31.0
github.com/thediveo/success v1.0.2
github.com/thediveo/whalewatcher v0.11.0
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.16.0
golang.org/x/sys v0.16.0 // indirect
golang.org/x/tools v0.16.0 // indirect
)
12 changes: 3 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
Expand All @@ -174,14 +174,12 @@ github.com/thediveo/fdooze v0.3.1 h1:T5lARTBZXdDIwdsMNgiwpEY3NT40k1WTRlkoKgk8K+0
github.com/thediveo/fdooze v0.3.1/go.mod h1:wf5DDE9ch9MqqoS5ofU5+tOOsZyvp5qrJzQjVIGXUTk=
github.com/thediveo/go-mntinfo v1.0.2 h1:PVzVhve7Hhi9cEnW7tLv+6V1K0L14LyrFkoRNIhL7e0=
github.com/thediveo/go-mntinfo v1.0.2/go.mod h1:R0OctrQ+AVz+aEbofJah3/8Hrpn9N22mc0Dym8Mv2qM=
github.com/thediveo/go-plugger/v3 v3.0.1 h1:GIlzqJa1stXgF2ouey69hyWaS+iaxLWJT37N8DMtQP8=
github.com/thediveo/go-plugger/v3 v3.0.1/go.mod h1:UYHIk8tys2lerZjpMhbWjrORb6/mXlb2bGZxCPTuR8k=
github.com/thediveo/go-plugger/v3 v3.1.0 h1:aqtzFkP7gBU/MlL/TyMOTY0MUYixebZn8JVhX/13yLo=
github.com/thediveo/go-plugger/v3 v3.1.0/go.mod h1:bED6ehF6GQUW9NDDgJG6QS/GL1J8L8hT3RUI7GTtAWo=
github.com/thediveo/ioctl v0.9.3 h1:DCxyUUY15z/Zezz+wf2nlbVf3yFh0nvfM7i7KnfgG8s=
github.com/thediveo/ioctl v0.9.3/go.mod h1:Ro3WW0UuPDh1QByEwNb/alva3ODM+GbRlb80u/LZU9o=
github.com/thediveo/lxkns v0.30.0 h1:shbrIHMX7kEgCIR1VgJO7OB6eVtJMDppaC9PM2hvXsU=
github.com/thediveo/lxkns v0.30.0/go.mod h1:36NPgrJPxQrlAQLy2GS0dO+EHQB1Gu2ijlPy5KRgWic=
github.com/thediveo/lxkns v0.31.0 h1:vjUaMfYG+Xafep6L/QUDfU+xK+jpzrZQyG0OKQAnfhg=
github.com/thediveo/lxkns v0.31.0/go.mod h1:agBgLaQTIIRC0+GzEzqdRxK4sihsZkvpp4Nsp10+Rl4=
github.com/thediveo/namspill v0.1.6 h1:eD8puqhwIkBS78vrzJtY46eurHX0o6JIAqzgkRmMLl0=
github.com/thediveo/notwork v1.3.1 h1:KG1Jh7pWU+QTl+7yMzs2Zugl8JZsP0x4OwNFt0rItL0=
github.com/thediveo/once v0.9.1 h1:gk/8dYOto5cVEBH0LK1vYyOFH9OvugcZ12e+UnJpmTo=
Expand Down Expand Up @@ -244,8 +242,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -261,8 +257,6 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
1 change: 1 addition & 0 deletions package_dockerenginefinder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func dockerEngineFinderOnly() {
backup := g.Backup()
DeferCleanup(func() {
g.Restore(backup)
clearCachedDetectorPlugins() // ...and don't forget to clean up after all
})
g.Clear()
g.Register(&dockerdEngineFinder{}, plugger.WithPlugin("test-dockerd"))
Expand Down
4 changes: 2 additions & 2 deletions socketactivator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

const sockactivatorSyncWait = 5 * time.Second

func resetActivatorPlugins() {
func clearCachedDetectorPlugins() {
muDaemonDetectorPlugins.Lock()
defer muDaemonDetectorPlugins.Unlock()
demonDetectorPlugins = nil
Expand All @@ -35,7 +35,7 @@ var _ = Describe("socket activator", Serial, Ordered, func() {

BeforeEach(test.LogToGinkgo)

BeforeEach(resetActivatorPlugins)
BeforeEach(clearCachedDetectorPlugins)

BeforeEach(func() {
goodgos := Goroutines() // avoid other failed goroutine tests to spill over
Expand Down
1 change: 1 addition & 0 deletions stacker.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func stackEngines(containers []*model.Container, engines []*Engine, proctable mo
// parent->children: we thus only need to modify the newly created
// process object and the shallow clone of the map, but we neither touch
// the original process map nor the original process objects.
proctable[proc.PID] = proc
proc.Parent = proctable[proc.PPID]
}
for _, engine := range engines {
Expand Down
40 changes: 36 additions & 4 deletions stacker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"io"
"os"
"strings"
"time"

"github.com/ory/dockertest/v3"
Expand All @@ -16,10 +17,12 @@ import (
"github.com/thediveo/lxkns/model"
"github.com/thediveo/lxkns/species"
"github.com/thediveo/whalewatcher/engineclient/containerd/test/ctr"
"github.com/thediveo/whalewatcher/engineclient/cri"
"github.com/thediveo/whalewatcher/engineclient/cri/test/img"
"github.com/thediveo/whalewatcher/test"
"github.com/thediveo/whalewatcher/watcher/containerd"
"github.com/thediveo/whalewatcher/watcher/moby"
"golang.org/x/exp/slices"

testlog "github.com/siemens/turtlefinder/internal/test"

Expand Down Expand Up @@ -157,9 +160,18 @@ var _ = Describe("turtles and elephants", Serial, Ordered, func() {
By("waiting for turtle finder to catch up")
Eventually(ctx, func() []*model.ContainerEngine {
_ = discover()
return finder.Engines()
engines := slices.DeleteFunc(finder.Engines(), isStackerTestEngineTyp)
slices.SortFunc(engines, func(a, b *model.ContainerEngine) int {
return strings.Compare(a.Type, b.Type)
})
return engines
}).Within(10 * time.Second).ProbeEvery(250 * time.Millisecond).
Should(HaveLen(len(engines) + 2 /* containerd native and CRI "twins"*/))
Should(HaveExactElements(
HaveField("Type", containerd.Type),
HaveField("Type", containerd.Type),
HaveField("Type", moby.Type),
HaveField("Type", cri.Type),
))

By("pulling a busybox image (if necessary)")
ctr.Successfully(providerCntr,
Expand Down Expand Up @@ -190,9 +202,29 @@ var _ = Describe("turtles and elephants", Serial, Ordered, func() {
By("waiting for the containerized containerd engine to vanish")
Eventually(ctx, func() []*model.ContainerEngine {
_ = discover()
return finder.Engines()
engines := slices.DeleteFunc(finder.Engines(), isStackerTestEngineTyp)
slices.SortFunc(engines, func(a, b *model.ContainerEngine) int {
return strings.Compare(a.Type, b.Type)
})
return engines
}).Within(10 * time.Second).ProbeEvery(250 * time.Millisecond).
Should(HaveLen(len(engines)))
Should(HaveExactElements(
HaveField("Type", containerd.Type), // ...only one left
HaveField("Type", moby.Type),
))
})

})

// filter for the engine types guaranteed to be present; please note that we
// filter out podman here, in order to be independent of host podman
// installations. We cover podman explicitly in the overall turtlefinder
// test(s).
func isStackerTestEngineTyp(e *model.ContainerEngine) bool {
switch e.Type {
case containerd.Type, moby.Type, cri.Type:
return false
default:
return true
}
}
24 changes: 16 additions & 8 deletions turtlefinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

"github.com/siemens/turtlefinder/activator"
"github.com/siemens/turtlefinder/detector"
"golang.org/x/exp/slices"
"golang.org/x/sync/semaphore"

_ "github.com/siemens/turtlefinder/activator/all" // pull in activator and socket-activated engine detector plugins
Expand Down Expand Up @@ -162,11 +161,9 @@ func (f *TurtleFinder) Containers(
f.update(ctx, procs)
// Now query the available engines for containers that are alive...
f.mux.Lock()
allEngines := make([]*Engine, 0, len(f.engines))
allEngines := make([]*Engine, 0, len(f.engines) /* lucky guess */)
for _, engines := range f.engines {
// create copies of the engine objects in order to not trash the
// original engine objects.
allEngines = append(allEngines, slices.Clone(engines)...)
allEngines = append(allEngines, engines...)
}
f.mux.Unlock()
allcontainers := []*model.Container{}
Expand Down Expand Up @@ -408,9 +405,9 @@ NextProcess:
// watchers when retiring a Turtlefinder.
enginectx := f.contexter()
for _, w := range engineproc.engine.detector.NewWatchers(enginectx, engineproc.proc.PID, apisox) {
// We've got a new watcher! Or two *snicker*
// We've got a new watcher! Or two... *snicker* ...so many demons!
startWatch(enginectx, w, f.initialsyncwait)
eng := NewEngine(enginectx, w)
eng := NewEngine(enginectx, w, engineproc.proc.PPID)
f.mux.Lock()
f.engines[engineproc.proc.PID] = append(f.engines[engineproc.proc.PID], eng)
f.mux.Unlock()
Expand Down Expand Up @@ -449,8 +446,19 @@ NextProcess:
// need to make sure that we're not trashing our engine map.
f.mux.Lock()
defer f.mux.Unlock()
// Freshly socket-activated engines won't yet be in the process
// tree we're working on. In order to allow downstream users of
// turtlefinders – lxkns in particular – to still do correct
// container PID translation, we get an engine's parent PID that
// we assume serves as well for PID translation between PID
// namespaces. So pay a quick visit to the proc filesystem and
// pick up this engine's PPID.
var ppidhint model.PIDType
if engproc := model.NewProcess(pid, false); engproc != nil {
ppidhint = engproc.PPID
}
f.engines[pid] = []*Engine{
NewEngine(f.contexter(), w),
NewEngine(f.contexter(), w, ppidhint),
}
},
)
Expand Down
2 changes: 1 addition & 1 deletion turtlefinder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ var _ = Describe("turtle finder", Ordered, Serial, func() {

})

BeforeEach(resetActivatorPlugins)
BeforeEach(clearCachedDetectorPlugins)

BeforeEach(test.LogToGinkgo)

Expand Down
2 changes: 1 addition & 1 deletion watch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var _ = Describe("watch", Serial, func() {
Eventually(GinkgoWriter.(fmt.Stringer).String).Should(MatchRegexp(
`beginning synchronization to 'docker.com' engine .*\n` +
`.*synchronized to 'docker.com' container engine .* with ID ` +
`'(?:[A-Z0-9]{4}(?:[A-Z0-9]{4}){11})|(?:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})'`))
`'(?:[A-Z0-9]{4}(?::[A-Z0-9]{4}){11})|(?:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})'`))
cancel()
Eventually(GinkgoWriter.(fmt.Stringer).String).Should(ContainSubstring("terminated watch"))
})
Expand Down

0 comments on commit 98d9683

Please sign in to comment.