github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-collection/container-collection_test.go (about) 1 // Copyright 2023 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package containercollection 16 17 import ( 18 "fmt" 19 "math/rand" 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/require" 24 25 utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test" 26 types "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 27 ) 28 29 type fakeTracerMapsUpdater struct { 30 containers map[string]*Container 31 } 32 33 var r *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) 34 35 func (f *fakeTracerMapsUpdater) TracerMapsUpdater() FuncNotify { 36 return func(event PubSubEvent) { 37 switch event.Type { 38 case EventTypeAddContainer: 39 f.containers[event.Container.Runtime.ContainerID] = event.Container 40 case EventTypeRemoveContainer: 41 delete(f.containers, event.Container.Runtime.ContainerID) 42 } 43 } 44 } 45 46 func BenchmarkCreateContainerCollection(b *testing.B) { 47 b.ReportAllocs() 48 49 for n := 0; n < b.N; n++ { 50 cc := ContainerCollection{} 51 cc.AddContainer(&Container{ 52 Runtime: RuntimeMetadata{ 53 BasicRuntimeMetadata: types.BasicRuntimeMetadata{ 54 ContainerID: fmt.Sprint(n), 55 }, 56 }, 57 Mntns: uint64(n), 58 }) 59 } 60 } 61 62 const ( 63 TestContainerCount = 10000 64 ) 65 66 func BenchmarkLookupContainerByMntns(b *testing.B) { 67 cc := ContainerCollection{} 68 69 for n := 0; n < TestContainerCount; n++ { 70 cc.AddContainer(&Container{ 71 Runtime: RuntimeMetadata{ 72 BasicRuntimeMetadata: types.BasicRuntimeMetadata{ 73 ContainerID: fmt.Sprint(n), 74 }, 75 }, 76 Mntns: uint64(n), 77 }) 78 } 79 80 b.ResetTimer() 81 82 for n := 0; n < b.N; n++ { 83 mntnsID := uint64(r.Intn(TestContainerCount)) 84 container := cc.LookupContainerByMntns(mntnsID) 85 if container == nil { 86 b.Fatalf("there should be a container for mount namespace ID %d", mntnsID) 87 } 88 } 89 } 90 91 func BenchmarkLookupContainerByNetns(b *testing.B) { 92 cc := ContainerCollection{} 93 94 for n := 0; n < TestContainerCount; n++ { 95 cc.AddContainer(&Container{ 96 Runtime: RuntimeMetadata{ 97 BasicRuntimeMetadata: types.BasicRuntimeMetadata{ 98 ContainerID: fmt.Sprint(n), 99 }, 100 }, 101 Netns: uint64(n), 102 }) 103 } 104 105 b.ResetTimer() 106 107 for n := 0; n < b.N; n++ { 108 netnsID := uint64(r.Intn(TestContainerCount)) 109 container := cc.LookupContainersByNetns(netnsID) 110 if len(container) == 0 { 111 b.Fatalf("there should be a container for net namespace ID %d", netnsID) 112 } 113 } 114 } 115 116 func TestWithTracerCollection(t *testing.T) { 117 t.Parallel() 118 119 // We need root to create the runners that will act as containers on this test 120 utilstest.RequireRoot(t) 121 122 cc := ContainerCollection{} 123 f := &fakeTracerMapsUpdater{containers: make(map[string]*Container)} 124 125 if err := cc.Initialize(WithTracerCollection(f)); err != nil { 126 t.Fatalf("Failed to initialize container collection: %s", err) 127 } 128 129 nContainers := 5 130 131 // We have to use real runners here as the WithTracerCollection() will drop the enricher if 132 // this doesn't have a valid PID 133 runners := make([]*utilstest.Runner, nContainers) 134 containers := make([]*Container, nContainers) 135 136 for i := 0; i < nContainers; i++ { 137 runner, err := utilstest.NewRunner(nil) 138 if err != nil { 139 t.Fatalf("Creating runner: %s", err) 140 } 141 t.Cleanup(runner.Close) 142 143 runners[i] = runner 144 145 containers[i] = &Container{ 146 Runtime: RuntimeMetadata{ 147 BasicRuntimeMetadata: types.BasicRuntimeMetadata{ 148 RuntimeName: types.RuntimeNameDocker, 149 ContainerName: fmt.Sprintf("name%d", i), 150 ContainerID: fmt.Sprintf("id%d", i), 151 }, 152 }, 153 Mntns: runner.Info.MountNsID, 154 Netns: runner.Info.NetworkNsID, 155 Pid: uint32(runner.Info.Pid), 156 K8s: K8sMetadata{ 157 BasicK8sMetadata: types.BasicK8sMetadata{ 158 ContainerName: fmt.Sprintf("name%d", i), 159 Namespace: fmt.Sprintf("namespace%d", i), 160 PodName: fmt.Sprintf("pod%d", i), 161 }, 162 }, 163 } 164 cc.AddContainer(containers[i]) 165 } 166 167 require.Equal(t, nContainers, len(f.containers), "number of containers should be equal") 168 169 verifyEnrichByMntNs := func() { 170 for i := 0; i < nContainers; i++ { 171 ev := types.CommonData{} 172 expected := types.CommonData{ 173 Runtime: types.BasicRuntimeMetadata{ 174 RuntimeName: containers[i].Runtime.RuntimeName, 175 ContainerName: containers[i].Runtime.ContainerName, 176 ContainerID: containers[i].Runtime.ContainerID, 177 }, 178 K8s: types.K8sMetadata{ 179 BasicK8sMetadata: types.BasicK8sMetadata{ 180 Namespace: containers[i].K8s.Namespace, 181 PodName: containers[i].K8s.PodName, 182 ContainerName: containers[i].K8s.ContainerName, 183 }, 184 }, 185 } 186 187 cc.EnrichByMntNs(&ev, containers[i].Mntns) 188 189 require.Equal(t, expected, ev, "events should be equal") 190 } 191 } 192 193 verifyEnrichByNetNs := func() { 194 for i := 0; i < nContainers; i++ { 195 ev := types.CommonData{} 196 expected := types.CommonData{ 197 Runtime: types.BasicRuntimeMetadata{ 198 RuntimeName: containers[i].Runtime.RuntimeName, 199 ContainerName: containers[i].Runtime.ContainerName, 200 ContainerID: containers[i].Runtime.ContainerID, 201 }, 202 K8s: types.K8sMetadata{ 203 BasicK8sMetadata: types.BasicK8sMetadata{ 204 Namespace: containers[i].K8s.Namespace, 205 PodName: containers[i].K8s.PodName, 206 ContainerName: containers[i].K8s.ContainerName, 207 }, 208 }, 209 } 210 211 cc.EnrichByNetNs(&ev, containers[i].Netns) 212 213 require.Equal(t, expected, ev, "events should be equal") 214 } 215 } 216 217 // Enrich by should work 218 verifyEnrichByMntNs() 219 verifyEnrichByNetNs() 220 221 cc.RemoveContainer(containers[0].Runtime.ContainerID) 222 223 // Pubsub events should be triggered immediately after container removal 224 require.Equal(t, nContainers-1, len(f.containers), "number of containers should be equal") 225 226 time.Sleep(1 * time.Second) 227 228 // Enrich should work 1 second after removing container 229 verifyEnrichByMntNs() 230 verifyEnrichByNetNs() 231 232 time.Sleep(6 * time.Second) 233 234 // Enrich should **not** work after removing container more than 6 seconds ago 235 ev := types.CommonData{} 236 expected := types.CommonData{} 237 cc.EnrichByMntNs(&ev, containers[0].Mntns) 238 require.Equal(t, expected, ev, "events should be equal") 239 240 // This is in a separated line to understand who is causing the issue. 241 cc.EnrichByNetNs(&ev, containers[0].Netns) 242 require.Equal(t, expected, ev, "events should be equal") 243 }