github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-utils/runtime-client/runtimeclient_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 runtimeclient_test
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/davecgh/go-spew/spew"
    22  	"github.com/google/go-cmp/cmp"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test"
    26  	containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils"
    27  	runtimeclient "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/runtime-client"
    28  	"github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/testutils"
    29  	containerutilsTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/types"
    30  	"github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    31  )
    32  
    33  const (
    34  	containerNamePrefix = "test-container"
    35  	containerImageName  = "docker.io/library/alpine:latest"
    36  	numContainers       = 2
    37  )
    38  
    39  func TestRuntimeClientInterface(t *testing.T) {
    40  	t.Parallel()
    41  	utilstest.RequireRoot(t)
    42  
    43  	for _, runtime := range testutils.SupportedContainerRuntimes {
    44  		t.Run(runtime.String(), func(t *testing.T) {
    45  			runtime := runtime
    46  			t.Parallel()
    47  
    48  			// Create test containers and their expected data
    49  			var expectedData []*runtimeclient.ContainerDetailsData
    50  			for i := 0; i < numContainers; i++ {
    51  				cn := fmt.Sprintf("%s-%s-%d", containerNamePrefix, runtime, i)
    52  				c, err := testutils.NewContainer(
    53  					runtime,
    54  					cn,
    55  					"sleep inf", // We simply want to keep the container running
    56  					testutils.WithImage(containerImageName),
    57  				)
    58  				require.Nil(t, err)
    59  				require.NotNil(t, c)
    60  
    61  				c.Start(t)
    62  				t.Cleanup(func() {
    63  					c.Stop(t)
    64  				})
    65  
    66  				expectedData = append(expectedData,
    67  					&runtimeclient.ContainerDetailsData{
    68  						ContainerData: runtimeclient.ContainerData{
    69  							Runtime: runtimeclient.RuntimeContainerData{
    70  								BasicRuntimeMetadata: types.BasicRuntimeMetadata{
    71  									RuntimeName:        runtime,
    72  									ContainerName:      cn,
    73  									ContainerID:        c.ID(),
    74  									ContainerImageName: containerImageName,
    75  								},
    76  								State: runtimeclient.StateRunning,
    77  							},
    78  							K8s: runtimeclient.K8sContainerData{},
    79  						},
    80  						Pid: c.Pid(),
    81  						// TODO: Is it worth to compare the cgroups path and mounts?
    82  					},
    83  				)
    84  			}
    85  
    86  			// Initialize runtime client
    87  			config := &containerutilsTypes.RuntimeConfig{
    88  				Name: runtime,
    89  			}
    90  			rc, err := containerutils.NewContainerRuntimeClient(config)
    91  			t.Cleanup(func() {
    92  				if rc != nil {
    93  					rc.Close()
    94  				}
    95  			})
    96  			require.Nil(t, err)
    97  			require.NotNil(t, rc)
    98  
    99  			// Test runtime client methods
   100  			t.Run("GetContainers", func(t *testing.T) {
   101  				t.Parallel()
   102  
   103  				containers, err := rc.GetContainers()
   104  				require.Nil(t, err)
   105  				require.NotNil(t, containers)
   106  
   107  				for _, eData := range expectedData {
   108  					found := false
   109  					for _, cData := range containers {
   110  						// ContainerImageDigest may vary among versions, so we do not check it for now
   111  						cData.Runtime.BasicRuntimeMetadata.ContainerImageDigest = ""
   112  						if cmp.Equal(*cData, eData.ContainerData) {
   113  							found = true
   114  							break
   115  						}
   116  					}
   117  					require.True(t, found, "couldn't find container:\n%s\nin:\n%s",
   118  						spew.Sdump(eData.ContainerData), spew.Sdump(containers))
   119  				}
   120  			})
   121  
   122  			t.Run("GetContainer", func(t *testing.T) {
   123  				t.Parallel()
   124  
   125  				for _, eData := range expectedData {
   126  					cData, err := rc.GetContainer(eData.Runtime.ContainerID)
   127  					// ContainerImageDigest may vary among versions, so we do not check it for now
   128  					cData.Runtime.BasicRuntimeMetadata.ContainerImageDigest = ""
   129  					require.Nil(t, err)
   130  					require.NotNil(t, cData)
   131  					require.True(t, cmp.Equal(*cData, eData.ContainerData),
   132  						"unexpected container data:\n%s", cmp.Diff(*cData, eData.ContainerData))
   133  				}
   134  			})
   135  
   136  			t.Run("GetContainerDetails", func(t *testing.T) {
   137  				t.Parallel()
   138  
   139  				for _, eData := range expectedData {
   140  					cData, err := rc.GetContainerDetails(eData.Runtime.ContainerID)
   141  					// ContainerImageDigest may vary among versions, so we do not check it for now
   142  					cData.Runtime.BasicRuntimeMetadata.ContainerImageDigest = ""
   143  					require.Nil(t, err)
   144  					require.NotNil(t, cData)
   145  
   146  					// TODO: Is it worth to compare the cgroups path and mounts?
   147  					require.NotEmpty(t, cData.CgroupsPath)
   148  					cData.CgroupsPath = eData.CgroupsPath
   149  					cData.Mounts = eData.Mounts
   150  
   151  					require.True(t, cmp.Equal(cData, eData),
   152  						"unexpected container data:\n%s", cmp.Diff(cData, eData))
   153  				}
   154  			})
   155  		})
   156  	}
   157  }
   158  
   159  // TestDeleteContainers is useful to delete containers from
   160  // TestRuntimeClientInterface that were not properly deleted because of a bug.
   161  // func TestDeleteContainers(t *testing.T) {
   162  // 	t.Parallel()
   163  // 	utilstest.RequireRoot(t)
   164  
   165  // 	for _, runtime := range testutils.SupportedContainerRuntimes {
   166  // 		t.Run(runtime.String(), func(t *testing.T) {
   167  // 			runtime := runtime
   168  // 			t.Parallel()
   169  
   170  // 			// Delete test containers from previous runs
   171  // 			for i := 0; i < numContainers; i++ {
   172  // 				cn := fmt.Sprintf("%s-%s-%d", containerNamePrefix, runtime, i)
   173  // 				c, err := testutils.NewContainer(runtime, cn, "sleep inf", testutils.WithForceDelete())
   174  // 				require.Nil(t, err)
   175  // 				require.NotNil(t, c)
   176  // 				c.Stop(t)
   177  // 			}
   178  // 		})
   179  // 	}
   180  // }