github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-collection/match_test.go (about)

     1  // Copyright 2019-2021 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  	"reflect"
    20  	"testing"
    21  
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	k8sTypes "k8s.io/apimachinery/pkg/types"
    24  
    25  	"github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    26  )
    27  
    28  func TestSelector(t *testing.T) {
    29  	table := []struct {
    30  		description string
    31  		match       bool
    32  		selector    *ContainerSelector
    33  		container   *Container
    34  	}{
    35  		{
    36  			description: "Selector without filter",
    37  			match:       true,
    38  			selector:    &ContainerSelector{},
    39  			container: &Container{
    40  				K8s: K8sMetadata{
    41  					BasicK8sMetadata: types.BasicK8sMetadata{
    42  						Namespace:     "this-namespace",
    43  						PodName:       "this-pod",
    44  						ContainerName: "this-container",
    45  					},
    46  				},
    47  			},
    48  		},
    49  		{
    50  			description: "Selector with all filters",
    51  			match:       true,
    52  			selector: &ContainerSelector{
    53  				K8s: K8sSelector{
    54  					BasicK8sMetadata: types.BasicK8sMetadata{
    55  						Namespace:     "this-namespace",
    56  						PodName:       "this-pod",
    57  						ContainerName: "this-container",
    58  						PodLabels: map[string]string{
    59  							"key1": "value1",
    60  							"key2": "value2",
    61  						},
    62  					},
    63  				},
    64  			},
    65  			container: &Container{
    66  				K8s: K8sMetadata{
    67  					BasicK8sMetadata: types.BasicK8sMetadata{
    68  						Namespace:     "this-namespace",
    69  						PodName:       "this-pod",
    70  						ContainerName: "this-container",
    71  						PodLabels: map[string]string{
    72  							"unrelated-label": "here",
    73  							"key1":            "value1",
    74  							"key2":            "value2",
    75  						},
    76  					},
    77  				},
    78  			},
    79  		},
    80  		{
    81  			description: "Podname does not match",
    82  			match:       false,
    83  			selector: &ContainerSelector{
    84  				K8s: K8sSelector{
    85  					BasicK8sMetadata: types.BasicK8sMetadata{
    86  						Namespace: "this-namespace",
    87  						PodName:   "this-pod",
    88  					},
    89  				},
    90  			},
    91  			container: &Container{
    92  				K8s: K8sMetadata{
    93  					BasicK8sMetadata: types.BasicK8sMetadata{
    94  						Namespace:     "this-namespace",
    95  						PodName:       "a-misnamed-pod",
    96  						ContainerName: "this-container",
    97  					},
    98  				},
    99  			},
   100  		},
   101  		{
   102  			description: "One label doesn't match",
   103  			match:       false,
   104  			selector: &ContainerSelector{
   105  				K8s: K8sSelector{
   106  					BasicK8sMetadata: types.BasicK8sMetadata{
   107  						Namespace:     "this-namespace",
   108  						PodName:       "this-pod",
   109  						ContainerName: "this-container",
   110  						PodLabels: map[string]string{
   111  							"key1": "value1",
   112  							"key2": "value2",
   113  						},
   114  					},
   115  				},
   116  			},
   117  			container: &Container{
   118  				K8s: K8sMetadata{
   119  					BasicK8sMetadata: types.BasicK8sMetadata{
   120  						Namespace:     "this-namespace",
   121  						PodName:       "this-pod",
   122  						ContainerName: "this-container",
   123  						PodLabels: map[string]string{
   124  							"key1": "value1",
   125  							"key2": "something-else",
   126  						},
   127  					},
   128  				},
   129  			},
   130  		},
   131  		{
   132  			description: "Several namespaces without match",
   133  			match:       false,
   134  			selector: &ContainerSelector{
   135  				K8s: K8sSelector{
   136  					BasicK8sMetadata: types.BasicK8sMetadata{
   137  						Namespace: "ns1,ns2,ns3",
   138  						PodName:   "this-pod",
   139  					},
   140  				},
   141  			},
   142  			container: &Container{
   143  				K8s: K8sMetadata{
   144  					BasicK8sMetadata: types.BasicK8sMetadata{
   145  						Namespace:     "this-namespace",
   146  						PodName:       "this-pod",
   147  						ContainerName: "this-container",
   148  					},
   149  				},
   150  			},
   151  		},
   152  		{
   153  			description: "Several namespaces with match",
   154  			match:       true,
   155  			selector: &ContainerSelector{
   156  				K8s: K8sSelector{
   157  					BasicK8sMetadata: types.BasicK8sMetadata{
   158  						Namespace: "ns1,ns2,ns3",
   159  						PodName:   "this-pod",
   160  					},
   161  				},
   162  			},
   163  			container: &Container{
   164  				K8s: K8sMetadata{
   165  					BasicK8sMetadata: types.BasicK8sMetadata{
   166  						Namespace:     "ns2",
   167  						PodName:       "this-pod",
   168  						ContainerName: "this-container",
   169  					},
   170  				},
   171  			},
   172  		},
   173  	}
   174  
   175  	for i, entry := range table {
   176  		result := ContainerSelectorMatches(entry.selector, entry.container)
   177  		if entry.match != result {
   178  			t.Fatalf("Failed test %q (index %d): result %v expected %v",
   179  				entry.description, i, result, entry.match)
   180  		}
   181  	}
   182  }
   183  
   184  func TestContainerResolver(t *testing.T) {
   185  	opts := []ContainerCollectionOption{}
   186  
   187  	cc := &ContainerCollection{}
   188  	err := cc.Initialize(opts...)
   189  	if err != nil {
   190  		t.Fatalf("Failed to initialize container collection: %s", err)
   191  	}
   192  
   193  	// Add 3 Containers
   194  	for i := 0; i < 3; i++ {
   195  		cc.AddContainer(&Container{
   196  			Runtime: RuntimeMetadata{
   197  				BasicRuntimeMetadata: types.BasicRuntimeMetadata{
   198  					ContainerID: fmt.Sprintf("abcde%d", i),
   199  				},
   200  			},
   201  			Mntns:      55555 + uint64(i),
   202  			Pid:        uint32(100 + i),
   203  			CgroupPath: "/none",
   204  			CgroupID:   1,
   205  			K8s: K8sMetadata{
   206  				BasicK8sMetadata: types.BasicK8sMetadata{
   207  					Namespace:     "this-namespace",
   208  					PodName:       "my-pod",
   209  					ContainerName: fmt.Sprintf("container%d", i),
   210  				},
   211  				ownerReference: &metav1.OwnerReference{
   212  					UID: k8sTypes.UID(fmt.Sprintf("abcde%d", i)),
   213  				},
   214  			},
   215  		})
   216  	}
   217  
   218  	// Remove 1 Container
   219  	cc.RemoveContainer("abcde1")
   220  
   221  	// Remove non-existent Container
   222  	cc.RemoveContainer("abcde99")
   223  
   224  	// Check content
   225  	if cc.ContainerLen() != 2 {
   226  		t.Fatalf("Error while checking containers: len %d", cc.ContainerLen())
   227  	}
   228  	if cc.GetContainer("abcde0") == nil {
   229  		t.Fatalf("Error while checking container %s: not found", "abcde0")
   230  	}
   231  	if cc.GetContainer("abcde2") == nil {
   232  		t.Fatalf("Error while checking container %s: not found", "abcde2")
   233  	}
   234  
   235  	// Check content using LookupMntnsByPod
   236  	mntnsByContainer := cc.LookupMntnsByPod("this-namespace", "my-pod")
   237  	if !reflect.DeepEqual(mntnsByContainer, map[string]uint64{"container0": 55555, "container2": 55557}) {
   238  		t.Fatalf("Error while looking up mount ns by Pod: unexpected %v", mntnsByContainer)
   239  	}
   240  	mntnsByContainer = cc.LookupMntnsByPod("this-namespace", "this-other-pod")
   241  	if !reflect.DeepEqual(mntnsByContainer, map[string]uint64{}) {
   242  		t.Fatalf("Error while looking up mount ns by Pod: unexpected %v", mntnsByContainer)
   243  	}
   244  
   245  	// Check content using LookupMntnsByContainer
   246  	mntns := cc.LookupMntnsByContainer("this-namespace", "my-pod", "container0")
   247  	if mntns != 55555 {
   248  		t.Fatalf("Error while looking up container0: unexpected mntns %v", mntns)
   249  	}
   250  	mntns = cc.LookupMntnsByContainer("this-namespace", "my-pod", "container1")
   251  	if mntns != 0 {
   252  		t.Fatalf("Error while looking up container1: unexpected mntns %v", mntns)
   253  	}
   254  	mntns = cc.LookupMntnsByContainer("this-namespace", "my-pod", "container2")
   255  	if mntns != 55557 {
   256  		t.Fatalf("Error while looking up container2: unexpected mntns %v", mntns)
   257  	}
   258  
   259  	// Check content using LookupPIDByPod
   260  	pidByContainer := cc.LookupPIDByPod("this-namespace", "my-pod")
   261  	if !reflect.DeepEqual(pidByContainer, map[string]uint32{"container0": 100, "container2": 102}) {
   262  		t.Fatalf("Error while looking up PID by Pod: unexpected %v", pidByContainer)
   263  	}
   264  	pidByContainer = cc.LookupPIDByPod("this-namespace", "this-other-pod")
   265  	if !reflect.DeepEqual(pidByContainer, map[string]uint32{}) {
   266  		t.Fatalf("Error while looking up PID by Pod: unexpected %v", pidByContainer)
   267  	}
   268  
   269  	// Check content using LookupPIDByContainer
   270  	pid := cc.LookupPIDByContainer("this-namespace", "my-pod", "container0")
   271  	if pid != 100 {
   272  		t.Fatalf("Error while looking up container0: unexpected pid %v", pid)
   273  	}
   274  	pid = cc.LookupPIDByContainer("this-namespace", "my-pod", "container1")
   275  	if pid != 0 {
   276  		t.Fatalf("Error while looking up container1: unexpected pid %v", pid)
   277  	}
   278  	pid = cc.LookupPIDByContainer("this-namespace", "my-pod", "container2")
   279  	if pid != 102 {
   280  		t.Fatalf("Error while looking up container2: unexpected pid %v", pid)
   281  	}
   282  
   283  	// Check content using LookupOwnerReferenceByMntns
   284  	ownerRef := cc.LookupOwnerReferenceByMntns(55555)
   285  	if ownerRef == nil || ownerRef.UID != "abcde0" {
   286  		t.Fatalf("Error while looking up owner reference: unexpected %v", ownerRef)
   287  	}
   288  
   289  	ownerRef = cc.LookupOwnerReferenceByMntns(55557)
   290  	if ownerRef == nil || ownerRef.UID != "abcde2" {
   291  		t.Fatalf("Error while looking up owner reference: unexpected %v", ownerRef)
   292  	}
   293  
   294  	// Non-existent mntns
   295  	ownerRef = cc.LookupOwnerReferenceByMntns(55556)
   296  	if ownerRef != nil {
   297  		t.Fatalf("Error while looking up owner reference: unexpected %v", ownerRef)
   298  	}
   299  
   300  	// Check LookupContainerByMntns
   301  	containerByMntns0 := cc.LookupContainerByMntns(55555)
   302  	if containerByMntns0.K8s.ContainerName != "container0" {
   303  		t.Fatalf("Error in LookupContainerByMntns: expected %s, found %s", "container0",
   304  			containerByMntns0.K8s.ContainerName)
   305  	}
   306  
   307  	// Check LookupContainerByMntns
   308  	containerByMntns2 := cc.LookupContainerByMntns(55555 + 2)
   309  	if containerByMntns2.K8s.ContainerName != "container2" {
   310  		t.Fatalf("Error in LookupContainerByMntns: expected %s, found %s", "container2",
   311  			containerByMntns2.K8s.ContainerName)
   312  	}
   313  
   314  	containerByMntnsNotFound := cc.LookupContainerByMntns(989898)
   315  	if containerByMntnsNotFound != nil {
   316  		t.Fatalf("Error in LookupContainerByMntns: returned non nil")
   317  	}
   318  
   319  	// Add new container with same pod and container name of container0 but in different namespace
   320  	cc.AddContainer(&Container{
   321  		Runtime: RuntimeMetadata{
   322  			BasicRuntimeMetadata: types.BasicRuntimeMetadata{
   323  				ContainerID: "abcde0-different",
   324  			},
   325  		},
   326  		K8s: K8sMetadata{
   327  			BasicK8sMetadata: types.BasicK8sMetadata{
   328  				Namespace:     "another-namespace",
   329  				PodName:       "my-pod",
   330  				ContainerName: "container0",
   331  				PodLabels: map[string]string{
   332  					"key1": "value1",
   333  					"key2": "value2",
   334  				},
   335  			},
   336  		},
   337  	})
   338  
   339  	// Look up containers with label 'key1=value1'
   340  	selectedContainers := cc.GetContainersBySelector(&ContainerSelector{
   341  		K8s: K8sSelector{
   342  			BasicK8sMetadata: types.BasicK8sMetadata{
   343  				PodLabels: map[string]string{
   344  					"key1": "value1",
   345  				},
   346  			},
   347  		},
   348  	})
   349  	if len(selectedContainers) != 1 {
   350  		t.Fatalf("Error while looking up containers by one label: invalid number of matches")
   351  	}
   352  	if v, found := selectedContainers[0].K8s.PodLabels["key1"]; !found || v != "value1" {
   353  		t.Fatalf("Error while looking up containers by one label: unexpected container %+v",
   354  			selectedContainers[0])
   355  	}
   356  
   357  	// Look up containers with label 'key1=value1' and 'key2=value2'
   358  	selector := ContainerSelector{
   359  		K8s: K8sSelector{
   360  			BasicK8sMetadata: types.BasicK8sMetadata{
   361  				PodLabels: map[string]string{
   362  					"key1": "value1",
   363  					"key2": "value2",
   364  				},
   365  			},
   366  		},
   367  	}
   368  	selectedContainers = cc.GetContainersBySelector(&selector)
   369  	if len(selectedContainers) != 1 {
   370  		t.Fatalf("Error while looking up containers by multiple labels: invalid number of matches")
   371  	}
   372  	for sk, sv := range selector.K8s.PodLabels {
   373  		if v, found := selectedContainers[0].K8s.PodLabels[sk]; !found || v != sv {
   374  			t.Fatalf("Error while looking up containers by multiple labels: unexpected container %+v",
   375  				selectedContainers[0])
   376  		}
   377  	}
   378  
   379  	// Look up containers in 'this-namespace'
   380  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   381  		K8s: K8sSelector{
   382  			BasicK8sMetadata: types.BasicK8sMetadata{
   383  				Namespace: "this-namespace",
   384  			},
   385  		},
   386  	})
   387  	if len(selectedContainers) != 2 {
   388  		t.Fatalf("Error while looking up containers by namespace: invalid number of matches")
   389  	}
   390  	for _, container := range selectedContainers {
   391  		if container.K8s.Namespace != "this-namespace" {
   392  			t.Fatalf("Error while looking up containers by namespace: unexpected container %+v",
   393  				container)
   394  		}
   395  	}
   396  
   397  	// Look up containers in 'this-namespace' and 'my-pod'
   398  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   399  		K8s: K8sSelector{
   400  			BasicK8sMetadata: types.BasicK8sMetadata{
   401  				Namespace: "this-namespace",
   402  				PodName:   "my-pod",
   403  			},
   404  		},
   405  	})
   406  	if len(selectedContainers) != 2 {
   407  		t.Fatalf("Error while looking up containers by namespace and pod: invalid number of matches")
   408  	}
   409  	for _, container := range selectedContainers {
   410  		if container.K8s.Namespace != "this-namespace" || container.K8s.PodName != "my-pod" {
   411  			t.Fatalf("Error while looking up containers by namespace and pod: unexpected container %+v",
   412  				container)
   413  		}
   414  	}
   415  
   416  	// Look up containers named 'container0' anywhere
   417  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   418  		K8s: K8sSelector{
   419  			BasicK8sMetadata: types.BasicK8sMetadata{
   420  				ContainerName: "container0",
   421  			},
   422  		},
   423  	})
   424  	if len(selectedContainers) != 2 {
   425  		t.Fatalf("Error while looking up containers by name: invalid number of matches")
   426  	}
   427  	for _, container := range selectedContainers {
   428  		if container.K8s.ContainerName != "container0" {
   429  			t.Fatalf("Error while looking up containers by name: unexpected container %+v",
   430  				container)
   431  		}
   432  	}
   433  
   434  	// Look up containers named 'container0' in 'my-pod' but any namespace
   435  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   436  		K8s: K8sSelector{
   437  			BasicK8sMetadata: types.BasicK8sMetadata{
   438  				PodName:       "my-pod",
   439  				ContainerName: "container0",
   440  			},
   441  		},
   442  	})
   443  	if len(selectedContainers) != 2 {
   444  		t.Fatalf("Error while looking up containers by name and pod: invalid number of matches")
   445  	}
   446  	for _, container := range selectedContainers {
   447  		if container.K8s.PodName != "my-pod" || container.K8s.ContainerName != "container0" {
   448  			t.Fatalf("Error while looking up containers by name and pod: unexpected container %+v",
   449  				container)
   450  		}
   451  	}
   452  
   453  	// Look up container0 in 'this-namespace' and 'my-pod'
   454  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   455  		K8s: K8sSelector{
   456  			BasicK8sMetadata: types.BasicK8sMetadata{
   457  				Namespace:     "this-namespace",
   458  				PodName:       "my-pod",
   459  				ContainerName: "container0",
   460  			},
   461  		},
   462  	})
   463  	if len(selectedContainers) != 1 {
   464  		t.Fatalf("Error while looking up specific container: invalid number of matches")
   465  	}
   466  	if selectedContainers[0].K8s.Namespace != "this-namespace" || selectedContainers[0].K8s.PodName != "my-pod" || selectedContainers[0].K8s.ContainerName != "container0" {
   467  		t.Fatalf("Error while looking up specific container: unexpected container %+v",
   468  			selectedContainers[0])
   469  	}
   470  
   471  	// Look up container0 in 'another-namespace' and 'my-pod'
   472  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   473  		K8s: K8sSelector{
   474  			BasicK8sMetadata: types.BasicK8sMetadata{
   475  				Namespace:     "another-namespace",
   476  				PodName:       "my-pod",
   477  				ContainerName: "container0",
   478  			},
   479  		},
   480  	})
   481  	if len(selectedContainers) != 1 {
   482  		t.Fatalf("Error while looking up specific container: invalid number of matches")
   483  	}
   484  	if selectedContainers[0].K8s.Namespace != "another-namespace" || selectedContainers[0].K8s.PodName != "my-pod" || selectedContainers[0].K8s.ContainerName != "container0" {
   485  		t.Fatalf("Error while looking up specific container: unexpected container %+v",
   486  			selectedContainers[0])
   487  	}
   488  
   489  	// Look up container2 in 'this-namespace' and 'my-pod'
   490  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   491  		K8s: K8sSelector{
   492  			BasicK8sMetadata: types.BasicK8sMetadata{
   493  				Namespace:     "this-namespace",
   494  				PodName:       "my-pod",
   495  				ContainerName: "container2",
   496  			},
   497  		},
   498  	})
   499  	if len(selectedContainers) != 1 {
   500  		t.Fatalf("Error while looking up specific container: invalid number of matches")
   501  	}
   502  	if selectedContainers[0].K8s.Namespace != "this-namespace" || selectedContainers[0].K8s.PodName != "my-pod" || selectedContainers[0].K8s.ContainerName != "container2" {
   503  		t.Fatalf("Error while looking up specific container: unexpected container %+v",
   504  			selectedContainers[0])
   505  	}
   506  
   507  	// Look up a non-existent container
   508  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   509  		K8s: K8sSelector{
   510  			BasicK8sMetadata: types.BasicK8sMetadata{
   511  				Namespace:     "this-namespace",
   512  				PodName:       "my-pod",
   513  				ContainerName: "non-existent",
   514  			},
   515  		},
   516  	})
   517  	if len(selectedContainers) != 0 {
   518  		t.Fatalf("Error while looking up a non-existent container")
   519  	}
   520  
   521  	// Look up containers in a non-existent pod
   522  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   523  		K8s: K8sSelector{
   524  			BasicK8sMetadata: types.BasicK8sMetadata{
   525  				Namespace: "this-namespace",
   526  				PodName:   "non-existent",
   527  			},
   528  		},
   529  	})
   530  	if len(selectedContainers) != 0 {
   531  		t.Fatalf("Error while looking up containers in a non-existent pod")
   532  	}
   533  
   534  	// Look up containers in a non-existent pod
   535  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   536  		K8s: K8sSelector{
   537  			BasicK8sMetadata: types.BasicK8sMetadata{
   538  				Namespace:     "this-namespace",
   539  				PodName:       "non-existent",
   540  				ContainerName: "container0",
   541  			},
   542  		},
   543  	})
   544  	if len(selectedContainers) != 0 {
   545  		t.Fatalf("Error while looking up containers in a non-existent namespace")
   546  	}
   547  
   548  	// Look up containers in a non-existent namespace
   549  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   550  		K8s: K8sSelector{
   551  			BasicK8sMetadata: types.BasicK8sMetadata{
   552  				Namespace: "non-existent",
   553  			},
   554  		},
   555  	})
   556  	if len(selectedContainers) != 0 {
   557  		t.Fatalf("Error while looking up containers in a non-existent namespace")
   558  	}
   559  
   560  	// Look up containers in a non-existent namespace
   561  	selectedContainers = cc.GetContainersBySelector(&ContainerSelector{
   562  		K8s: K8sSelector{
   563  			BasicK8sMetadata: types.BasicK8sMetadata{
   564  				Namespace:     "non-existent",
   565  				PodName:       "my-pod",
   566  				ContainerName: "container0",
   567  			},
   568  		},
   569  	})
   570  	if len(selectedContainers) != 0 {
   571  		t.Fatalf("Error while looking up containers in a non-existent namespace")
   572  	}
   573  }