Skip to content

Commit

Permalink
Merge pull request #54 from thediveo/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
thediveo authored Nov 18, 2024
2 parents d184353 + ec990e5 commit 4e59428
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 23 deletions.
1 change: 1 addition & 0 deletions api/types/discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var _ = Describe("discovery result JSON", func() {
"with-mounts": true,
"with-socket-processes": false,
"with-affinity-scheduling": false,
"with-task-affinity-scheduling": false,
"labels": {},
"scanned-namespace-types": [
"time",
Expand Down
27 changes: 17 additions & 10 deletions discover/discovery_affinity_sched.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,25 @@ package discover
import "github.com/thediveo/lxkns/model"

// discoverAffinityScheduling discovers the CPU affinity lists and scheduler
// settings for the leader processes of all discovered namespaces.
// settings for either the leader processes of all discovered namespaces, or for
// all tasks, if requested.
func discoverAffinityScheduling(result *Result) {
if !result.Options.DiscoverAffinityScheduling {
return
}
for nstype := model.MountNS; nstype < model.NamespaceTypesCount; nstype++ {
for _, ns := range result.Namespaces[nstype] {
for _, leader := range ns.Leaders() {
if leader.Affinity != nil {
continue
switch {
case result.Options.DiscoverTaskAffinityScheduling:
for _, proc := range result.Processes {
for _, task := range proc.Tasks {
_ = task.RetrieveAffinityScheduling()
}
}
case result.Options.DiscoverAffinityScheduling:
for nstype := model.MountNS; nstype < model.NamespaceTypesCount; nstype++ {
for _, ns := range result.Namespaces[nstype] {
for _, leader := range ns.Leaders() {
if leader.Affinity != nil {
continue
}
_ = leader.RetrieveAffinityScheduling()
}
_ = leader.RetrieveAffinityScheduling()
}
}
}
Expand Down
32 changes: 20 additions & 12 deletions discover/discovery_opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ type DiscoverOpts struct {
// If zero, defaults to discovering all namespaces.
NamespaceTypes species.NamespaceType `json:"-"`

ScanProcs bool `json:"from-procs"` // Scan processes for attached namespaces.
ScanTasks bool `json:"from-tasks"` // Scan all tasks for attached namespaces.
ScanFds bool `json:"from-fds"` // Scan open file descriptors for namespaces.
ScanBindmounts bool `json:"from-bindmounts"` // Scan bind-mounts for namespaces.
DiscoverHierarchy bool `json:"with-hierarchy"` // Discover the hierarchy of PID and user namespaces.
DiscoverOwnership bool `json:"with-ownership"` // Discover the ownership of non-user namespaces.
DiscoverFreezerState bool `json:"with-freezer"` // Discover the cgroup freezer state of processes.
DiscoverMounts bool `json:"with-mounts"` // Discover mount point hierarchy with mount paths and visibility.
DiscoverSocketProcesses bool `json:"with-socket-processes"` // Discover the processes related to specific socket inode numbers.
DiscoverAffinityScheduling bool `json:"with-affinity-scheduling"` // Disover CPU affinity and scheduling of leader processes.
Labels map[string]string `json:"labels"` // Pass options (in form of labels) to decorators
ScanProcs bool `json:"from-procs"` // Scan processes for attached namespaces.
ScanTasks bool `json:"from-tasks"` // Scan all tasks for attached namespaces.
ScanFds bool `json:"from-fds"` // Scan open file descriptors for namespaces.
ScanBindmounts bool `json:"from-bindmounts"` // Scan bind-mounts for namespaces.
DiscoverHierarchy bool `json:"with-hierarchy"` // Discover the hierarchy of PID and user namespaces.
DiscoverOwnership bool `json:"with-ownership"` // Discover the ownership of non-user namespaces.
DiscoverFreezerState bool `json:"with-freezer"` // Discover the cgroup freezer state of processes.
DiscoverMounts bool `json:"with-mounts"` // Discover mount point hierarchy with mount paths and visibility.
DiscoverSocketProcesses bool `json:"with-socket-processes"` // Discover the processes related to specific socket inode numbers.
DiscoverAffinityScheduling bool `json:"with-affinity-scheduling"` // Disover CPU affinity and scheduling of leader processes.
DiscoverTaskAffinityScheduling bool `json:"with-task-affinity-scheduling"` // Discovery CPU affinity and scheduling of all tasks.
Labels map[string]string `json:"labels"` // Pass options (in form of labels) to decorators

Containerizer containerizer.Containerizer `json:"-"` // Discover containers using containerizer.

Expand Down Expand Up @@ -123,7 +124,6 @@ func NotFromProcs() DiscoveryOption {
func FromTasks() DiscoveryOption {
return func(o *DiscoverOpts) {
o.ScanTasks = true
o.ScanTasks = true
}
}

Expand All @@ -141,6 +141,14 @@ func WithoutAffinityAndScheduling() DiscoveryOption {
return func(o *DiscoverOpts) { o.DiscoverAffinityScheduling = false }
}

func WithTaskAffinityAndScheduling() DiscoveryOption {
return func(o *DiscoverOpts) { o.DiscoverTaskAffinityScheduling = true }
}

func WithoutTaskAffinityAndScheduling() DiscoveryOption {
return func(o *DiscoverOpts) { o.DiscoverTaskAffinityScheduling = false }
}

// FromFds opts to find namespaces from the open file descriptors of processes.
func FromFds() DiscoveryOption {
return func(o *DiscoverOpts) { o.ScanFds = true }
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/thediveo/lxkns

go 1.21
go 1.22

require (
github.com/PaesslerAG/jsonpath v0.1.1
Expand Down
88 changes: 88 additions & 0 deletions model/process_iter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2024 Harald Albrecht.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

//go:build go1.23

package model

import "iter"

// Tasks returns an iterator over all tasks of the process identified by PID. In
// case of non-existing/invalid PIDs, Tasks returns an empty iterator. If the
// process table does not contain information about the tasks of the process,
// the iterator won't emit any tasks, not even the task group leader
// representing the process.
func (t ProcessTable) Tasks(pid PIDType) iter.Seq[*Task] {
return Tasks(t, pid)
}

// Tasks returns an iterator over all tasks of the process identified by PID. In
// case of non-existing/invalid PIDs, Tasks returns an empty iterator. If the
// process table does not contain information about the tasks of the process,
// the iterator won't emit any tasks, not even the task group leader
// representing the process.
func Tasks(t ProcessTable, pid PIDType) iter.Seq[*Task] {
proc := t[pid]
if proc == nil {
return func(yield func(*Task) bool) {}
}
return func(yield func(*Task) bool) {
tasksOfProcess(proc, yield)
}
}

// TasksRecursive returns an iterator over all tasks of the process identified
// by PID, as well as all tasks of children, grandchildren, et cetera. If the
// process table does not contain information about the tasks of the process,
// the iterator won't emit any tasks, not even the task group leader
// representing the process.
func (t ProcessTable) TasksRecursive(pid PIDType) iter.Seq[*Task] {
return TasksRecursive(t, pid)
}

// TasksRecursive returns an iterator over all tasks of the process identified
// by PID, as well as all tasks of children, grandchildren, et cetera. If the
// process table does not contain information about the tasks of the process,
// the iterator won't emit any tasks, not even the task group leader
// representing the process.
func TasksRecursive(t ProcessTable, pid PIDType) iter.Seq[*Task] {
proc := t[pid]
if proc == nil {
return func(yield func(*Task) bool) {}
}
return func(yield func(*Task) bool) {
tasksRecursive(proc, yield)
}
}

func tasksRecursive(proc *Process, yield func(*Task) bool) bool {
if !tasksOfProcess(proc, yield) {
return false
}
for _, child := range proc.Children {
if !tasksRecursive(child, yield) {
return false
}
}
return true
}

func tasksOfProcess(proc *Process, yield func(*Task) bool) bool {
for _, task := range proc.Tasks {
if !yield(task) {
return false
}
}
return true
}
96 changes: 96 additions & 0 deletions model/process_iter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2024 Harald Albrecht.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

//go:build go1.23

package model

import (
"iter"

. "github.com/onsi/ginkgo/v2/dsl/core"
. "github.com/onsi/gomega"
)

func taskNames(it iter.Seq[*Task]) []string {
names := []string{}
for task := range it {
names = append(names, task.Name)
}
return names
}

// Returns only the first task name and then tells the iterator to shut up. This
// exercises also the unhappy little paths...
func firstTaskName(it iter.Seq[*Task]) []string {
names := []string{}
for task := range it {
names = append(names, task.Name)
break
}
return names
}

var _ = Describe("process and task iterators", func() {

pt := ProcessTable{
42: {
ProTaskCommon: ProTaskCommon{Name: "pfoo"},
Tasks: []*Task{
{
ProTaskCommon: ProTaskCommon{Name: "rfoo"},
},
{
ProTaskCommon: ProTaskCommon{Name: "rbar"},
},
{
ProTaskCommon: ProTaskCommon{Name: "rbaz"},
},
},
Children: []*Process{
{
ProTaskCommon: ProTaskCommon{Name: "pfrobz"},
Tasks: []*Task{
{
ProTaskCommon: ProTaskCommon{Name: "ffrobz"},
},
},
},
{
ProTaskCommon: ProTaskCommon{Name: "pgnampf"},
Tasks: []*Task{
{
ProTaskCommon: ProTaskCommon{Name: "fgnampf"},
},
},
},
},
},
}

It("iterates over tasks of a single process", func() {
Expect(taskNames(Tasks(pt, 0))).To(BeEmpty())
Expect(firstTaskName(Tasks(pt, 42))).To(ConsistOf("rfoo"))
Expect(taskNames(Tasks(pt, 42))).To(ConsistOf(
"rfoo", "rbar", "rbaz"))
})

It("iterates over tasks of processes recursively", func() {
Expect(taskNames(TasksRecursive(pt, 0))).To(BeEmpty())
Expect(firstTaskName(TasksRecursive(pt, 42))).To(ConsistOf("rfoo"))
Expect(taskNames(TasksRecursive(pt, 42))).To(ConsistOf(
"rfoo", "rbar", "rbaz", "ffrobz", "fgnampf"))
})

})

0 comments on commit 4e59428

Please sign in to comment.