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  }