From ad74d4932008e0758d9a064bf57b0773254fac99 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Mon, 19 Feb 2024 17:42:48 +0000 Subject: [PATCH 01/15] add podresourcesstore to awscontainerinsightsreceivers --- .../internal/stores/podresourcesstore.go | 85 +++++++++++++++++++ .../internal/stores/podresourcesstore_test.go | 84 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go create mode 100644 receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go new file mode 100644 index 000000000000..e86ac80abd33 --- /dev/null +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -0,0 +1,85 @@ +package stores + +import ( + "context" + "fmt" + "net" + "os" + "sync" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" +) + +const ( + socketPath = "/var/lib/kubelet/pod-resources/kubelet.sock" + connectionTimeout = 10 * time.Second +) + +var ( + instance *PodResourcesStore + once sync.Once +) + +type PodResourcesStore struct { +} + +func init() { + once.Do(func() { + instance = &PodResourcesStore{} + }) +} + +func (p *PodResourcesStore) GetPodResources() (*podresourcesapi.ListPodResourcesResponse, error) { + _, err := os.Stat(socketPath) + if os.IsNotExist(err) { + return nil, fmt.Errorf("socket path does not exist: %s", socketPath) + } else if err != nil { + return nil, fmt.Errorf("failed to check socket path: %v", err) + } + + conn, cleanup, err := p.connectToServer(socketPath) + if err != nil { + return nil, fmt.Errorf("failed to connect to server: %v", err) + } + defer cleanup() + + return p.listPods(conn) +} + +func (p *PodResourcesStore) connectToServer(socket string) (*grpc.ClientConn, func(), error) { + ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) + defer cancel() + + conn, err := grpc.DialContext(ctx, + socket, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithBlock(), + grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { + d := net.Dialer{} + return d.DialContext(ctx, "unix", addr) + }), + ) + + if err != nil { + return nil, func() {}, fmt.Errorf("failure connecting to '%s': %v", socket, err) + } + + return conn, func() { conn.Close() }, nil +} + +func (p *PodResourcesStore) listPods(conn *grpc.ClientConn) (*podresourcesapi.ListPodResourcesResponse, error) { + client := podresourcesapi.NewPodResourcesListerClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) + defer cancel() + + resp, err := client.List(ctx, &podresourcesapi.ListPodResourcesRequest{}) + if err != nil { + return nil, fmt.Errorf("failure getting pod resources: %v", err) + } + + return resp, nil +} diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go new file mode 100644 index 000000000000..0eafd6f12b2f --- /dev/null +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -0,0 +1,84 @@ +package stores + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" +) + +func TestGetPodResources_Success(t *testing.T) { + instance = &PodResourcesStore{} + + osStatOrig := osStat + osStat = func(name string) (os.FileInfo, error) { + return nil, nil + } + defer func() { osStat = osStatOrig }() + + connectToServerOrig := instance.connectToServer + instance.connectToServer = func(socket string) (*grpc.ClientConn, func(), error) { + mockClientConn := &grpc.ClientConn{} + mockCleanup := func() {} + return mockClientConn, mockCleanup, nil + } + defer func() { instance.connectToServer = connectToServerOrig }() + + listPodsOrig := instance.listPods + mockResponse := &podresourcesapi.ListPodResourcesResponse{} + instance.listPods = func(conn *grpc.ClientConn) (*podresourcesapi.ListPodResourcesResponse, error) { + return mockResponse, nil + } + defer func() { instance.listPods = listPodsOrig }() + + resp, err := instance.GetPodResources() + + assert.NoError(t, err) + assert.Equal(t, mockResponse, resp) +} + +func TestGetPodResources_Error(t *testing.T) { + instance = &PodResourcesStore{} + + osStatOrig := osStat + osStat = func(name string) (os.FileInfo, error) { + return nil, assert.AnError + } + defer func() { osStat = osStatOrig }() + + _, err := instance.GetPodResources() + + assert.Error(t, err) +} + +func TestConnectToServer_Error(t *testing.T) { + instance = &PodResourcesStore{} + + grpcDialContextOrig := grpcDialContext + grpcDialContext = func(ctx context.Context, target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + return nil, assert.AnError + } + defer func() { grpcDialContext = grpcDialContextOrig }() + + _, _, err := instance.connectToServer("dummy-socket") + + assert.Error(t, err) +} + +func TestListPods_Error(t *testing.T) { + instance = &PodResourcesStore{} + + listOrig := clientList + clientList = func(ctx context.Context, in *podresourcesapi.ListPodResourcesRequest, opts ...grpc.CallOption) (*podresourcesapi.ListPodResourcesResponse, error) { + return nil, assert.AnError + } + defer func() { clientList = listOrig }() + + _, err := instance.listPods(&grpc.ClientConn{}) + + assert.Error(t, err) +} From 5c5cc34a362e7df5d2d6711b0015c38ad77283c8 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Tue, 20 Feb 2024 11:28:20 +0000 Subject: [PATCH 02/15] adding refresh logic --- receiver/awscontainerinsightreceiver/go.mod | 1 + receiver/awscontainerinsightreceiver/go.sum | 2 + .../stores/kubeletutil/podresourcesclient.go | 79 ++++++++ .../internal/stores/podresourcesstore.go | 138 ++++++++----- .../internal/stores/podresourcesstore_test.go | 181 ++++++++++++------ 5 files changed, 300 insertions(+), 101 deletions(-) create mode 100644 receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go diff --git a/receiver/awscontainerinsightreceiver/go.mod b/receiver/awscontainerinsightreceiver/go.mod index 16bec34a0a86..a7ed5a4dc52a 100644 --- a/receiver/awscontainerinsightreceiver/go.mod +++ b/receiver/awscontainerinsightreceiver/go.mod @@ -138,6 +138,7 @@ require ( gotest.tools/v3 v3.0.3 // indirect k8s.io/klog/v2 v2.90.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/kubelet v0.27.3 // indirect k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect diff --git a/receiver/awscontainerinsightreceiver/go.sum b/receiver/awscontainerinsightreceiver/go.sum index 0e5067e8f90d..c78149ea57df 100644 --- a/receiver/awscontainerinsightreceiver/go.sum +++ b/receiver/awscontainerinsightreceiver/go.sum @@ -1184,6 +1184,8 @@ k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/kubelet v0.27.3 h1:5WhTV1iiBu9q/rr+gvy65LQ+K/e7dmgcaYjys5ipLqY= +k8s.io/kubelet v0.27.3/go.mod h1:Mz42qgZZgWgPmOJEYaR5evmh+EoSwFzEvPBozA2y9mg= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= diff --git a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go new file mode 100644 index 000000000000..7b0cbd31c726 --- /dev/null +++ b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go @@ -0,0 +1,79 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeletutil // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil" + +import ( + "context" + "fmt" + "net" + "os" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" +) + +const ( + socketPath = "/var/lib/kubelet/pod-resources/kubelet.sock" + connectionTimeout = 10 * time.Second +) + +type PodResourcesClient struct { + delegateClient podresourcesapi.PodResourcesListerClient +} + +func NewPodResourcesClient() (*PodResourcesClient, error) { + podResourcesClient := &PodResourcesClient{} + + conn, cleanup, err := podResourcesClient.connectToServer(socketPath) + if err != nil { + return nil, fmt.Errorf("failed to connect to server: %w", err) + } + defer cleanup() + + podResourcesClient.delegateClient = podresourcesapi.NewPodResourcesListerClient(conn) + + return podResourcesClient, nil +} + +func (p *PodResourcesClient) connectToServer(socket string) (*grpc.ClientConn, func(), error) { + ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) + defer cancel() + + conn, err := grpc.DialContext(ctx, + socket, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithBlock(), + grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { + d := net.Dialer{} + return d.DialContext(ctx, "unix", addr) + }), + ) + + if err != nil { + return nil, func() {}, fmt.Errorf("failure connecting to '%s': %w", socket, err) + } + + return conn, func() { conn.Close() }, nil +} + +func (p *PodResourcesClient) ListPods() (*podresourcesapi.ListPodResourcesResponse, error) { + _, err := os.Stat(socketPath) + if os.IsNotExist(err) { + return nil, fmt.Errorf("socket path does not exist: %s", socketPath) + } else if err != nil { + return nil, fmt.Errorf("failed to check socket path: %w", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) + defer cancel() + + resp, err := p.delegateClient.List(ctx, &podresourcesapi.ListPodResourcesRequest{}) + if err != nil { + return nil, fmt.Errorf("failure getting pod resources: %w", err) + } + + return resp, nil +} diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go index e86ac80abd33..aa09de13b545 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -1,21 +1,23 @@ -package stores +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package stores // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver/internal/stores" import ( "context" "fmt" - "net" - "os" "sync" "time" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil" + "go.uber.org/zap" + podresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1" ) const ( socketPath = "/var/lib/kubelet/pod-resources/kubelet.sock" connectionTimeout = 10 * time.Second + taskTimeout = 10 * time.Second ) var ( @@ -23,63 +25,107 @@ var ( once sync.Once ) +type ContainerInfo struct { + podName string + containerName string + namespace string +} + +type ResourceInfo struct { + resourceName string + deviceID string +} + +type PodResourcesClientInterface interface { + ListPods() (*podresourcesv1.ListPodResourcesResponse, error) +} + type PodResourcesStore struct { + containerInfoToResourcesMap map[ContainerInfo][]ResourceInfo + resourceToPodContainerMap map[ResourceInfo]ContainerInfo + lastRefreshed time.Time + ctx context.Context + cancel context.CancelFunc + logger *zap.Logger + podResourcesClient PodResourcesClientInterface } -func init() { +func NewPodResourcesStore(logger *zap.Logger) *PodResourcesStore { once.Do(func() { - instance = &PodResourcesStore{} + podResourcesClient, _ := kubeletutil.NewPodResourcesClient() + ctx, cancel := context.WithCancel(context.Background()) + instance = &PodResourcesStore{ + containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), + resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), + lastRefreshed: time.Now(), + ctx: ctx, + cancel: cancel, + logger: logger, + podResourcesClient: podResourcesClient, + } + + go func() { + refreshTicker := time.NewTicker(time.Second) + for { + select { + case <-refreshTicker.C: + instance.refreshTick() + case <-instance.ctx.Done(): + refreshTicker.Stop() + return + } + } + }() }) + return instance } -func (p *PodResourcesStore) GetPodResources() (*podresourcesapi.ListPodResourcesResponse, error) { - _, err := os.Stat(socketPath) - if os.IsNotExist(err) { - return nil, fmt.Errorf("socket path does not exist: %s", socketPath) - } else if err != nil { - return nil, fmt.Errorf("failed to check socket path: %v", err) +func (p *PodResourcesStore) refreshTick() { + now := time.Now() + if now.Sub(p.lastRefreshed) >= taskTimeout { + p.refresh() + p.lastRefreshed = now } - - conn, cleanup, err := p.connectToServer(socketPath) - if err != nil { - return nil, fmt.Errorf("failed to connect to server: %v", err) - } - defer cleanup() - - return p.listPods(conn) } -func (p *PodResourcesStore) connectToServer(socket string) (*grpc.ClientConn, func(), error) { - ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) - defer cancel() - - conn, err := grpc.DialContext(ctx, - socket, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithBlock(), - grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - d := net.Dialer{} - return d.DialContext(ctx, "unix", addr) - }), - ) - - if err != nil { - return nil, func() {}, fmt.Errorf("failure connecting to '%s': %v", socket, err) +func (p *PodResourcesStore) refresh() { + doRefresh := func() { + p.updateMaps() } - return conn, func() { conn.Close() }, nil + refreshWithTimeout(p.ctx, doRefresh, taskTimeout) } -func (p *PodResourcesStore) listPods(conn *grpc.ClientConn) (*podresourcesapi.ListPodResourcesResponse, error) { - client := podresourcesapi.NewPodResourcesListerClient(conn) +func (p *PodResourcesStore) updateMaps() { + p.containerInfoToResourcesMap = make(map[ContainerInfo][]ResourceInfo) + p.resourceToPodContainerMap = make(map[ResourceInfo]ContainerInfo) - ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) - defer cancel() + devicePods, err := p.podResourcesClient.ListPods() - resp, err := client.List(ctx, &podresourcesapi.ListPodResourcesRequest{}) if err != nil { - return nil, fmt.Errorf("failure getting pod resources: %v", err) + p.logger.Error(fmt.Sprintf("Error getting pod resources: %v", err)) + return } - return resp, nil + for _, pod := range devicePods.GetPodResources() { + for _, container := range pod.GetContainers() { + for _, device := range container.GetDevices() { + + containerInfo := ContainerInfo{ + podName: pod.GetName(), + namespace: pod.GetNamespace(), + containerName: container.GetName(), + } + + for _, deviceID := range device.GetDeviceIds() { + resourceInfo := ResourceInfo{ + resourceName: device.GetResourceName(), + deviceID: deviceID, + } + p.containerInfoToResourcesMap[containerInfo] = append(p.containerInfoToResourcesMap[containerInfo], resourceInfo) + p.resourceToPodContainerMap[resourceInfo] = containerInfo + } + } + } + } } diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index 0eafd6f12b2f..cef6a5933a50 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -1,84 +1,155 @@ -package stores +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package stores // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver/internal/stores" import ( "context" + "fmt" "testing" + "time" "github.com/stretchr/testify/assert" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" -) + "go.uber.org/zap" -func TestGetPodResources_Success(t *testing.T) { - instance = &PodResourcesStore{} - - osStatOrig := osStat - osStat = func(name string) (os.FileInfo, error) { - return nil, nil - } - defer func() { osStat = osStatOrig }() + podresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1" + v1 "k8s.io/kubelet/pkg/apis/podresources/v1" +) - connectToServerOrig := instance.connectToServer - instance.connectToServer = func(socket string) (*grpc.ClientConn, func(), error) { - mockClientConn := &grpc.ClientConn{} - mockCleanup := func() {} - return mockClientConn, mockCleanup, nil +var ( + expectedContainerInfoToResourcesMap = map[ContainerInfo][]ResourceInfo{ + { // ContainerInfo + podName: "test-pod", + containerName: "test-container", + namespace: "test-namespace", + }: { + { // ResourceInfo + resourceName: "test-resource", + deviceID: "device-id-1", + }, + { // ResourceInfo + resourceName: "test-resource", + deviceID: "device-id-2", + }, + }, } - defer func() { instance.connectToServer = connectToServerOrig }() - listPodsOrig := instance.listPods - mockResponse := &podresourcesapi.ListPodResourcesResponse{} - instance.listPods = func(conn *grpc.ClientConn) (*podresourcesapi.ListPodResourcesResponse, error) { - return mockResponse, nil + expectedResourceToPodContainerMap = map[ResourceInfo]ContainerInfo{ + { // ResourceInfo + resourceName: "test-resource", + deviceID: "device-id-1", + }: { // ContainerInfo + podName: "test-pod", + containerName: "test-container", + namespace: "test-namespace", + }, + { // ResourceInfo + resourceName: "test-resource", + deviceID: "device-id-2", + }: { // ContainerInfo + podName: "test-pod", + containerName: "test-container", + namespace: "test-namespace", + }, } - defer func() { instance.listPods = listPodsOrig }() +) - resp, err := instance.GetPodResources() +// MockPodResourcesClient is a mock implementation of PodResourcesClient +type MockPodResourcesClient struct { +} - assert.NoError(t, err) - assert.Equal(t, mockResponse, resp) +// ListPods mocks the ListPods method of PodResourcesClient +func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesResponse, error) { + mockResp := &podresourcesv1.ListPodResourcesResponse{ + PodResources: []*v1.PodResources{ + { + Name: "test-pod", + Namespace: "test-namespace", + Containers: []*v1.ContainerResources{ + { + Name: "test-container", + Devices: []*v1.ContainerDevices{ + { + ResourceName: "test-resource", + DeviceIds: []string{"device-id-1", "device-id-2"}, + }, + }, + }, + }, + }, + }, + } + return mockResp, nil } -func TestGetPodResources_Error(t *testing.T) { - instance = &PodResourcesStore{} +func TestNewPodResourcesStore(t *testing.T) { + // Test initialization of PodResourcesStore + logger := zap.NewNop() + store := NewPodResourcesStore(logger) + assert.NotNil(t, store, "PodResourcesStore should not be nil") + assert.NotNil(t, store.ctx, "Context should not be nil") + assert.NotNil(t, store.cancel, "Cancel function should not be nil") +} - osStatOrig := osStat - osStat = func(name string) (os.FileInfo, error) { - return nil, assert.AnError +func TestRefreshTick(t *testing.T) { + logger, _ := zap.NewDevelopment() + + // Create a PodResourcesStore instance with the mocked client and logger + store := &PodResourcesStore{ + containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), + resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), + lastRefreshed: time.Now(), + ctx: context.Background(), + cancel: func() {}, + logger: logger, + podResourcesClient: &MockPodResourcesClient{}, } - defer func() { osStat = osStatOrig }() - _, err := instance.GetPodResources() + // Set the lastRefreshed time to an hour ago + store.lastRefreshed = time.Now().Add(-time.Hour) - assert.Error(t, err) -} + // Call refreshTick + store.refreshTick() -func TestConnectToServer_Error(t *testing.T) { - instance = &PodResourcesStore{} + // Check if lastRefreshed has been updated + assert.True(t, store.lastRefreshed.After(time.Now().Add(-time.Hour)), "lastRefreshed should have been updated") +} - grpcDialContextOrig := grpcDialContext - grpcDialContext = func(ctx context.Context, target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { - return nil, assert.AnError +func TestUpdateMaps(t *testing.T) { + logger, _ := zap.NewDevelopment() + + // Create a PodResourcesStore instance with the mocked client and logger + store := &PodResourcesStore{ + containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), + resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), + lastRefreshed: time.Now(), + ctx: context.Background(), + cancel: func() {}, + logger: logger, + podResourcesClient: &MockPodResourcesClient{}, } - defer func() { grpcDialContext = grpcDialContextOrig }() - _, _, err := instance.connectToServer("dummy-socket") + // Call the updateMaps method + store.updateMaps() - assert.Error(t, err) -} - -func TestListPods_Error(t *testing.T) { - instance = &PodResourcesStore{} + // Assert that the maps are updated correctly + assert.NotNil(t, store.containerInfoToResourcesMap) + assert.NotNil(t, store.resourceToPodContainerMap) - listOrig := clientList - clientList = func(ctx context.Context, in *podresourcesapi.ListPodResourcesRequest, opts ...grpc.CallOption) (*podresourcesapi.ListPodResourcesResponse, error) { - return nil, assert.AnError + // Print containerInfoToResourcesMap + fmt.Println("containerInfoToResourcesMap:") + for key, value := range store.containerInfoToResourcesMap { + fmt.Printf("Key: %+v, Value: %+v\n", key, value) } - defer func() { clientList = listOrig }() - _, err := instance.listPods(&grpc.ClientConn{}) + // Print resourceToPodContainerMap + fmt.Println("\nresourceToPodContainerMap:") + for key, value := range store.resourceToPodContainerMap { + fmt.Printf("Key: %+v, Value: %+v\n", key, value) + } - assert.Error(t, err) + assert.Equal(t, len(expectedContainerInfoToResourcesMap), len(store.containerInfoToResourcesMap)) + assert.Equal(t, len(expectedResourceToPodContainerMap), len(store.resourceToPodContainerMap)) + assert.Equal(t, expectedContainerInfoToResourcesMap, store.containerInfoToResourcesMap) + assert.Equal(t, expectedResourceToPodContainerMap, store.resourceToPodContainerMap) } From a5dbcc0336b9f562fd10749a9dc952c63f06210a Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Tue, 20 Feb 2024 16:50:53 +0000 Subject: [PATCH 03/15] refactor tests --- .../internal/stores/podresourcesstore_test.go | 38 ++++--------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index cef6a5933a50..9eefbe1061fa 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -5,7 +5,6 @@ package stores // import "github.com/open-telemetry/opentelemetry-collector-cont import ( "context" - "fmt" "testing" "time" @@ -18,16 +17,16 @@ import ( var ( expectedContainerInfoToResourcesMap = map[ContainerInfo][]ResourceInfo{ - { // ContainerInfo + { podName: "test-pod", containerName: "test-container", namespace: "test-namespace", }: { - { // ResourceInfo + { resourceName: "test-resource", deviceID: "device-id-1", }, - { // ResourceInfo + { resourceName: "test-resource", deviceID: "device-id-2", }, @@ -35,18 +34,18 @@ var ( } expectedResourceToPodContainerMap = map[ResourceInfo]ContainerInfo{ - { // ResourceInfo + { resourceName: "test-resource", deviceID: "device-id-1", - }: { // ContainerInfo + }: { podName: "test-pod", containerName: "test-container", namespace: "test-namespace", }, - { // ResourceInfo + { resourceName: "test-resource", deviceID: "device-id-2", - }: { // ContainerInfo + }: { podName: "test-pod", containerName: "test-container", namespace: "test-namespace", @@ -54,11 +53,9 @@ var ( } ) -// MockPodResourcesClient is a mock implementation of PodResourcesClient type MockPodResourcesClient struct { } -// ListPods mocks the ListPods method of PodResourcesClient func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesResponse, error) { mockResp := &podresourcesv1.ListPodResourcesResponse{ PodResources: []*v1.PodResources{ @@ -83,7 +80,6 @@ func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesRes } func TestNewPodResourcesStore(t *testing.T) { - // Test initialization of PodResourcesStore logger := zap.NewNop() store := NewPodResourcesStore(logger) assert.NotNil(t, store, "PodResourcesStore should not be nil") @@ -94,7 +90,6 @@ func TestNewPodResourcesStore(t *testing.T) { func TestRefreshTick(t *testing.T) { logger, _ := zap.NewDevelopment() - // Create a PodResourcesStore instance with the mocked client and logger store := &PodResourcesStore{ containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), @@ -105,20 +100,16 @@ func TestRefreshTick(t *testing.T) { podResourcesClient: &MockPodResourcesClient{}, } - // Set the lastRefreshed time to an hour ago store.lastRefreshed = time.Now().Add(-time.Hour) - // Call refreshTick store.refreshTick() - // Check if lastRefreshed has been updated assert.True(t, store.lastRefreshed.After(time.Now().Add(-time.Hour)), "lastRefreshed should have been updated") } func TestUpdateMaps(t *testing.T) { logger, _ := zap.NewDevelopment() - // Create a PodResourcesStore instance with the mocked client and logger store := &PodResourcesStore{ containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), @@ -129,25 +120,10 @@ func TestUpdateMaps(t *testing.T) { podResourcesClient: &MockPodResourcesClient{}, } - // Call the updateMaps method store.updateMaps() - // Assert that the maps are updated correctly assert.NotNil(t, store.containerInfoToResourcesMap) assert.NotNil(t, store.resourceToPodContainerMap) - - // Print containerInfoToResourcesMap - fmt.Println("containerInfoToResourcesMap:") - for key, value := range store.containerInfoToResourcesMap { - fmt.Printf("Key: %+v, Value: %+v\n", key, value) - } - - // Print resourceToPodContainerMap - fmt.Println("\nresourceToPodContainerMap:") - for key, value := range store.resourceToPodContainerMap { - fmt.Printf("Key: %+v, Value: %+v\n", key, value) - } - assert.Equal(t, len(expectedContainerInfoToResourcesMap), len(store.containerInfoToResourcesMap)) assert.Equal(t, len(expectedResourceToPodContainerMap), len(store.resourceToPodContainerMap)) assert.Equal(t, expectedContainerInfoToResourcesMap, store.containerInfoToResourcesMap) From 4dfc19488d9889c4f3f68eaba0a717981170575a Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Tue, 20 Feb 2024 17:36:15 +0000 Subject: [PATCH 04/15] adding getters to get pod and resouce info --- .../internal/stores/podresourcesstore.go | 23 ++++++++-- .../internal/stores/podresourcesstore_test.go | 42 ++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go index aa09de13b545..2acb0fb9fbb2 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -9,9 +9,10 @@ import ( "sync" "time" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil" "go.uber.org/zap" - podresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1" + v1 "k8s.io/kubelet/pkg/apis/podresources/v1" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil" ) const ( @@ -37,7 +38,7 @@ type ResourceInfo struct { } type PodResourcesClientInterface interface { - ListPods() (*podresourcesv1.ListPodResourcesResponse, error) + ListPods() (*v1.ListPodResourcesResponse, error) } type PodResourcesStore struct { @@ -129,3 +130,19 @@ func (p *PodResourcesStore) updateMaps() { } } } + +func (p *PodResourcesStore) GetContainerInfo(deviceID string, resourceName string) *ContainerInfo { + key := ResourceInfo{deviceID: deviceID, resourceName: resourceName} + if containerInfo, ok := p.resourceToPodContainerMap[key]; ok { + return &containerInfo + } + return nil +} + +func (p *PodResourcesStore) GetResourcesInfo(podName string, containerName string, namespace string) *[]ResourceInfo { + key := ContainerInfo{podName: podName, containerName: containerName, namespace: namespace} + if resourceInfo, ok := p.containerInfoToResourcesMap[key]; ok { + return &resourceInfo + } + return nil +} diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index 9eefbe1061fa..006328632df5 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -10,9 +10,7 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/zap" - podresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1" - v1 "k8s.io/kubelet/pkg/apis/podresources/v1" ) var ( @@ -51,6 +49,23 @@ var ( namespace: "test-namespace", }, } + + expectedContainerInfo = ContainerInfo{ + podName: "test-pod", + containerName: "test-container", + namespace: "test-namespace", + } + + expectedResourceInfo = []ResourceInfo{ + { + resourceName: "test-resource", + deviceID: "device-id-1", + }, + { + resourceName: "test-resource", + deviceID: "device-id-2", + }, + } ) type MockPodResourcesClient struct { @@ -58,14 +73,14 @@ type MockPodResourcesClient struct { func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesResponse, error) { mockResp := &podresourcesv1.ListPodResourcesResponse{ - PodResources: []*v1.PodResources{ + PodResources: []*podresourcesv1.PodResources{ { Name: "test-pod", Namespace: "test-namespace", - Containers: []*v1.ContainerResources{ + Containers: []*podresourcesv1.ContainerResources{ { Name: "test-container", - Devices: []*v1.ContainerDevices{ + Devices: []*podresourcesv1.ContainerDevices{ { ResourceName: "test-resource", DeviceIds: []string{"device-id-1", "device-id-2"}, @@ -129,3 +144,20 @@ func TestUpdateMaps(t *testing.T) { assert.Equal(t, expectedContainerInfoToResourcesMap, store.containerInfoToResourcesMap) assert.Equal(t, expectedResourceToPodContainerMap, store.resourceToPodContainerMap) } + +func TestGets(t *testing.T) { + logger, _ := zap.NewDevelopment() + + store := &PodResourcesStore{ + containerInfoToResourcesMap: expectedContainerInfoToResourcesMap, + resourceToPodContainerMap: expectedResourceToPodContainerMap, + lastRefreshed: time.Now(), + ctx: context.Background(), + cancel: func() {}, + logger: logger, + podResourcesClient: &MockPodResourcesClient{}, + } + + assert.Equal(t, expectedContainerInfo, *store.GetContainerInfo("device-id-1", "test-resource")) + assert.Equal(t, expectedResourceInfo, *store.GetResourcesInfo("test-pod", "test-container", "test-namespace")) +} From 69bb375a77e7f696540afca58d428ccb5ed38076 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Fri, 23 Feb 2024 14:18:23 +0000 Subject: [PATCH 05/15] add shutdown methods to podresourcesclient and podresourcesstore, cover edge cases in unit tests --- .../stores/kubeletutil/podresourcesclient.go | 29 ++-- .../internal/stores/podresourcesstore.go | 7 +- .../internal/stores/podresourcesstore_test.go | 152 +++++++++++++----- 3 files changed, 138 insertions(+), 50 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go index 7b0cbd31c726..caf28218c0b8 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go @@ -22,23 +22,31 @@ const ( type PodResourcesClient struct { delegateClient podresourcesapi.PodResourcesListerClient + conn *grpc.ClientConn } func NewPodResourcesClient() (*PodResourcesClient, error) { podResourcesClient := &PodResourcesClient{} - conn, cleanup, err := podResourcesClient.connectToServer(socketPath) + conn, err := podResourcesClient.connectToServer(socketPath) if err != nil { return nil, fmt.Errorf("failed to connect to server: %w", err) } - defer cleanup() podResourcesClient.delegateClient = podresourcesapi.NewPodResourcesListerClient(conn) + podResourcesClient.conn = conn return podResourcesClient, nil } -func (p *PodResourcesClient) connectToServer(socket string) (*grpc.ClientConn, func(), error) { +func (p *PodResourcesClient) connectToServer(socket string) (*grpc.ClientConn, error) { + _, err := os.Stat(socket) + if os.IsNotExist(err) { + return nil, fmt.Errorf("socket path does not exist: %s", socket) + } else if err != nil { + return nil, fmt.Errorf("failed to check socket path: %w", err) + } + ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) defer cancel() @@ -53,20 +61,13 @@ func (p *PodResourcesClient) connectToServer(socket string) (*grpc.ClientConn, f ) if err != nil { - return nil, func() {}, fmt.Errorf("failure connecting to '%s': %w", socket, err) + return nil, fmt.Errorf("failure connecting to '%s': %w", socket, err) } - return conn, func() { conn.Close() }, nil + return conn, nil } func (p *PodResourcesClient) ListPods() (*podresourcesapi.ListPodResourcesResponse, error) { - _, err := os.Stat(socketPath) - if os.IsNotExist(err) { - return nil, fmt.Errorf("socket path does not exist: %s", socketPath) - } else if err != nil { - return nil, fmt.Errorf("failed to check socket path: %w", err) - } - ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) defer cancel() @@ -77,3 +78,7 @@ func (p *PodResourcesClient) ListPods() (*podresourcesapi.ListPodResourcesRespon return resp, nil } + +func (p *PodResourcesClient) Shutdown() { + p.conn.Close() +} diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go index 2acb0fb9fbb2..a002d977424c 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -16,7 +16,6 @@ import ( ) const ( - socketPath = "/var/lib/kubelet/pod-resources/kubelet.sock" connectionTimeout = 10 * time.Second taskTimeout = 10 * time.Second ) @@ -39,6 +38,7 @@ type ResourceInfo struct { type PodResourcesClientInterface interface { ListPods() (*v1.ListPodResourcesResponse, error) + Shutdown() } type PodResourcesStore struct { @@ -146,3 +146,8 @@ func (p *PodResourcesStore) GetResourcesInfo(podName string, containerName strin } return nil } + +func (p *PodResourcesStore) Shutdown() { + p.podResourcesClient.Shutdown() + p.cancel() +} diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index 006328632df5..4182c90f916c 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -5,6 +5,7 @@ package stores // import "github.com/open-telemetry/opentelemetry-collector-cont import ( "context" + "fmt" "testing" "time" @@ -66,13 +67,8 @@ var ( deviceID: "device-id-2", }, } -) -type MockPodResourcesClient struct { -} - -func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesResponse, error) { - mockResp := &podresourcesv1.ListPodResourcesResponse{ + listPodResourcesResponse = &podresourcesv1.ListPodResourcesResponse{ PodResources: []*podresourcesv1.PodResources{ { Name: "test-pod", @@ -89,9 +85,38 @@ func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesRes }, }, }, + { + Name: "test-pod-no-device", + Namespace: "test-namespace-no-device", + Containers: []*podresourcesv1.ContainerResources{ + { + Name: "test-container-no-device", + Devices: []*podresourcesv1.ContainerDevices{}, + }, + }, + }, }, } - return mockResp, nil + + listPodResourcesResponseEmptyPodResources = &podresourcesv1.ListPodResourcesResponse{ + PodResources: []*podresourcesv1.PodResources{}, + } + + listPodResourcesResponseEmptyResponse = &podresourcesv1.ListPodResourcesResponse{} +) + +type MockPodResourcesClient struct { + response *podresourcesv1.ListPodResourcesResponse + err error + shutdownCalled bool +} + +func (m *MockPodResourcesClient) ListPods() (*podresourcesv1.ListPodResourcesResponse, error) { + return m.response, m.err +} + +func (m *MockPodResourcesClient) Shutdown() { + m.shutdownCalled = true } func TestNewPodResourcesStore(t *testing.T) { @@ -103,17 +128,7 @@ func TestNewPodResourcesStore(t *testing.T) { } func TestRefreshTick(t *testing.T) { - logger, _ := zap.NewDevelopment() - - store := &PodResourcesStore{ - containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), - resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), - lastRefreshed: time.Now(), - ctx: context.Background(), - cancel: func() {}, - logger: logger, - podResourcesClient: &MockPodResourcesClient{}, - } + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponse, nil) store.lastRefreshed = time.Now().Add(-time.Hour) @@ -122,19 +137,19 @@ func TestRefreshTick(t *testing.T) { assert.True(t, store.lastRefreshed.After(time.Now().Add(-time.Hour)), "lastRefreshed should have been updated") } -func TestUpdateMaps(t *testing.T) { - logger, _ := zap.NewDevelopment() +func TestShutdown(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponse, nil) - store := &PodResourcesStore{ - containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), - resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), - lastRefreshed: time.Now(), - ctx: context.Background(), - cancel: func() {}, - logger: logger, - podResourcesClient: &MockPodResourcesClient{}, - } + mockClient := &MockPodResourcesClient{listPodResourcesResponse, nil, false} + store.podResourcesClient = mockClient + + store.Shutdown() + + assert.True(t, mockClient.shutdownCalled, "Shutdown method of the client should have been called") +} +func TestUpdateMaps(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponse, nil) store.updateMaps() assert.NotNil(t, store.containerInfoToResourcesMap) @@ -146,18 +161,81 @@ func TestUpdateMaps(t *testing.T) { } func TestGets(t *testing.T) { - logger, _ := zap.NewDevelopment() + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponse, nil) + store.updateMaps() + + assert.Equal(t, expectedContainerInfo, *store.GetContainerInfo("device-id-1", "test-resource")) + assert.Equal(t, expectedResourceInfo, *store.GetResourcesInfo("test-pod", "test-container", "test-namespace")) + + actualResourceInfo := store.GetResourcesInfo("test-pod-no-device", "test-container-no-device", "test-namespace-no-device") + if actualResourceInfo != nil { + t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) + } +} + +func TestGetsWhenThereAreNoPods(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseEmptyPodResources, nil) + store.updateMaps() + + assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) + assert.Equal(t, 0, len(store.resourceToPodContainerMap)) + + actualContainerInfo := store.GetContainerInfo("device-id-1", "test-resource") + if actualContainerInfo != nil { + t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) + } + + actualResourceInfo := store.GetResourcesInfo("test-pod", "test-container", "test-namespace") + if actualResourceInfo != nil { + t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) + } +} + +func TestGetsWhenPodReourcesResponseIsEmpty(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseEmptyResponse, nil) + store.updateMaps() + + assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) + assert.Equal(t, 0, len(store.resourceToPodContainerMap)) + + actualContainerInfo := store.GetContainerInfo("device-id-1", "test-resource") + if actualContainerInfo != nil { + t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) + } + + actualResourceInfo := store.GetResourcesInfo("test-pod", "test-container", "test-namespace") + if actualResourceInfo != nil { + t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) + } +} + +func TestGetsWhenPodReourcesThrowsError(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseEmptyResponse, fmt.Errorf("mocked behavior")) + store.updateMaps() + + assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) + assert.Equal(t, 0, len(store.resourceToPodContainerMap)) - store := &PodResourcesStore{ - containerInfoToResourcesMap: expectedContainerInfoToResourcesMap, - resourceToPodContainerMap: expectedResourceToPodContainerMap, + actualContainerInfo := store.GetContainerInfo("device-id-1", "test-resource") + if actualContainerInfo != nil { + t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) + } + + actualResourceInfo := store.GetResourcesInfo("test-pod", "test-container", "test-namespace") + if actualResourceInfo != nil { + t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) + } +} + +func constructPodResourcesStore(containerToDevices map[ContainerInfo][]ResourceInfo, deviceToContainer map[ResourceInfo]ContainerInfo, podResourcesResponse *podresourcesv1.ListPodResourcesResponse, podResourcesError error) *PodResourcesStore { + logger, _ := zap.NewDevelopment() + return &PodResourcesStore{ + containerInfoToResourcesMap: containerToDevices, + resourceToPodContainerMap: deviceToContainer, lastRefreshed: time.Now(), ctx: context.Background(), cancel: func() {}, logger: logger, - podResourcesClient: &MockPodResourcesClient{}, + podResourcesClient: &MockPodResourcesClient{podResourcesResponse, podResourcesError, false}, } - - assert.Equal(t, expectedContainerInfo, *store.GetContainerInfo("device-id-1", "test-resource")) - assert.Equal(t, expectedResourceInfo, *store.GetResourcesInfo("test-pod", "test-container", "test-namespace")) } From f8cfd9434eb5e78f9be66469b60b9901bfa8ac94 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Fri, 23 Feb 2024 14:27:16 +0000 Subject: [PATCH 06/15] fix typos and rename constants --- .../internal/stores/podresourcesstore_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index 4182c90f916c..04adde71c6fe 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -98,11 +98,11 @@ var ( }, } - listPodResourcesResponseEmptyPodResources = &podresourcesv1.ListPodResourcesResponse{ + listPodResourcesResponseWithEmptyPodResources = &podresourcesv1.ListPodResourcesResponse{ PodResources: []*podresourcesv1.PodResources{}, } - listPodResourcesResponseEmptyResponse = &podresourcesv1.ListPodResourcesResponse{} + listPodResourcesResponseWithEmptyResponse = &podresourcesv1.ListPodResourcesResponse{} ) type MockPodResourcesClient struct { @@ -174,7 +174,7 @@ func TestGets(t *testing.T) { } func TestGetsWhenThereAreNoPods(t *testing.T) { - store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseEmptyPodResources, nil) + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseWithEmptyPodResources, nil) store.updateMaps() assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) @@ -191,8 +191,8 @@ func TestGetsWhenThereAreNoPods(t *testing.T) { } } -func TestGetsWhenPodReourcesResponseIsEmpty(t *testing.T) { - store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseEmptyResponse, nil) +func TestGetsWhenPodResourcesResponseIsEmpty(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseWithEmptyResponse, nil) store.updateMaps() assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) @@ -209,8 +209,8 @@ func TestGetsWhenPodReourcesResponseIsEmpty(t *testing.T) { } } -func TestGetsWhenPodReourcesThrowsError(t *testing.T) { - store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseEmptyResponse, fmt.Errorf("mocked behavior")) +func TestGetsWhenPodResourcesThrowsError(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseWithEmptyResponse, fmt.Errorf("mocked behavior")) store.updateMaps() assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) From 0ed0726cf4bd371601d760c363adc62ec54fc17e Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Sat, 24 Feb 2024 00:11:33 +0000 Subject: [PATCH 07/15] adding AddResourceName method to podresourcestore and refactoring tests --- .../internal/stores/podresourcesstore.go | 18 +- .../internal/stores/podresourcesstore_test.go | 172 ++++++++++-------- 2 files changed, 112 insertions(+), 78 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go index a002d977424c..c874846d327f 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -16,8 +16,7 @@ import ( ) const ( - connectionTimeout = 10 * time.Second - taskTimeout = 10 * time.Second + taskTimeout = 10 * time.Second ) var ( @@ -44,6 +43,7 @@ type PodResourcesClientInterface interface { type PodResourcesStore struct { containerInfoToResourcesMap map[ContainerInfo][]ResourceInfo resourceToPodContainerMap map[ResourceInfo]ContainerInfo + resourceNameSet map[string]struct{} lastRefreshed time.Time ctx context.Context cancel context.CancelFunc @@ -58,6 +58,7 @@ func NewPodResourcesStore(logger *zap.Logger) *PodResourcesStore { instance = &PodResourcesStore{ containerInfoToResourcesMap: make(map[ContainerInfo][]ResourceInfo), resourceToPodContainerMap: make(map[ResourceInfo]ContainerInfo), + resourceNameSet: make(map[string]struct{}), lastRefreshed: time.Now(), ctx: ctx, cancel: cancel, @@ -123,8 +124,11 @@ func (p *PodResourcesStore) updateMaps() { resourceName: device.GetResourceName(), deviceID: deviceID, } - p.containerInfoToResourcesMap[containerInfo] = append(p.containerInfoToResourcesMap[containerInfo], resourceInfo) - p.resourceToPodContainerMap[resourceInfo] = containerInfo + _, found := p.resourceNameSet[resourceInfo.resourceName] + if found { + p.containerInfoToResourcesMap[containerInfo] = append(p.containerInfoToResourcesMap[containerInfo], resourceInfo) + p.resourceToPodContainerMap[resourceInfo] = containerInfo + } } } } @@ -147,7 +151,11 @@ func (p *PodResourcesStore) GetResourcesInfo(podName string, containerName strin return nil } +func (p *PodResourcesStore) AddResourceName(resourceName string) { + p.resourceNameSet[resourceName] = struct{}{} +} + func (p *PodResourcesStore) Shutdown() { - p.podResourcesClient.Shutdown() p.cancel() + p.podResourcesClient.Shutdown() } diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index 04adde71c6fe..9ecbb1cf4cee 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -14,83 +14,102 @@ import ( podresourcesv1 "k8s.io/kubelet/pkg/apis/podresources/v1" ) +const ( + defaultResourceName = "Resource-1" + defaultPodName = "Pod-1" + defaultNamespace = "Namespace-1" + defaultContainerName = "Container-1" + defaultDeviceID1 = "Device-1" + defaultDeviceID2 = "Device-2" + defaultDeviceID3 = "Device-3" + defaultDeviceID4 = "Device-4" + defaultResourceNameSkipped = "Resource-Skipped" + defaultContainerNameNoDevice = "Container-NoDevice" + defaultNamespaceNoDevice = "Namespace-NoDevice" + defaultPodNameNoDevice = "Pod-NoDevice" +) + var ( expectedContainerInfoToResourcesMap = map[ContainerInfo][]ResourceInfo{ { - podName: "test-pod", - containerName: "test-container", - namespace: "test-namespace", + podName: defaultPodName, + containerName: defaultContainerName, + namespace: defaultNamespace, }: { { - resourceName: "test-resource", - deviceID: "device-id-1", + resourceName: defaultResourceName, + deviceID: defaultDeviceID1, }, { - resourceName: "test-resource", - deviceID: "device-id-2", + resourceName: defaultResourceName, + deviceID: defaultDeviceID2, }, }, } expectedResourceToPodContainerMap = map[ResourceInfo]ContainerInfo{ { - resourceName: "test-resource", - deviceID: "device-id-1", + resourceName: defaultResourceName, + deviceID: defaultDeviceID1, }: { - podName: "test-pod", - containerName: "test-container", - namespace: "test-namespace", + podName: defaultPodName, + containerName: defaultContainerName, + namespace: defaultNamespace, }, { - resourceName: "test-resource", - deviceID: "device-id-2", + resourceName: defaultResourceName, + deviceID: defaultDeviceID2, }: { - podName: "test-pod", - containerName: "test-container", - namespace: "test-namespace", + podName: defaultPodName, + containerName: defaultContainerName, + namespace: defaultNamespace, }, } expectedContainerInfo = ContainerInfo{ - podName: "test-pod", - containerName: "test-container", - namespace: "test-namespace", + podName: defaultPodName, + containerName: defaultContainerName, + namespace: defaultNamespace, } expectedResourceInfo = []ResourceInfo{ { - resourceName: "test-resource", - deviceID: "device-id-1", + resourceName: defaultResourceName, + deviceID: defaultDeviceID1, }, { - resourceName: "test-resource", - deviceID: "device-id-2", + resourceName: defaultResourceName, + deviceID: defaultDeviceID2, }, } listPodResourcesResponse = &podresourcesv1.ListPodResourcesResponse{ PodResources: []*podresourcesv1.PodResources{ { - Name: "test-pod", - Namespace: "test-namespace", + Name: defaultPodName, + Namespace: defaultNamespace, Containers: []*podresourcesv1.ContainerResources{ { - Name: "test-container", + Name: defaultContainerName, Devices: []*podresourcesv1.ContainerDevices{ { - ResourceName: "test-resource", - DeviceIds: []string{"device-id-1", "device-id-2"}, + ResourceName: defaultResourceName, + DeviceIds: []string{defaultDeviceID1, defaultDeviceID2}, + }, + { + ResourceName: defaultResourceNameSkipped, + DeviceIds: []string{defaultDeviceID3, defaultDeviceID4}, }, }, }, }, }, { - Name: "test-pod-no-device", - Namespace: "test-namespace-no-device", + Name: defaultPodNameNoDevice, + Namespace: defaultNamespaceNoDevice, Containers: []*podresourcesv1.ContainerResources{ { - Name: "test-container-no-device", + Name: defaultContainerNameNoDevice, Devices: []*podresourcesv1.ContainerDevices{}, }, }, @@ -103,6 +122,10 @@ var ( } listPodResourcesResponseWithEmptyResponse = &podresourcesv1.ListPodResourcesResponse{} + + resourceNameSet = map[string]struct{}{ + defaultResourceName: {}, + } ) type MockPodResourcesClient struct { @@ -164,67 +187,41 @@ func TestGets(t *testing.T) { store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponse, nil) store.updateMaps() - assert.Equal(t, expectedContainerInfo, *store.GetContainerInfo("device-id-1", "test-resource")) - assert.Equal(t, expectedResourceInfo, *store.GetResourcesInfo("test-pod", "test-container", "test-namespace")) - - actualResourceInfo := store.GetResourcesInfo("test-pod-no-device", "test-container-no-device", "test-namespace-no-device") - if actualResourceInfo != nil { - t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) - } + assertMapsContainData(t, store) } func TestGetsWhenThereAreNoPods(t *testing.T) { store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseWithEmptyPodResources, nil) store.updateMaps() - assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) - assert.Equal(t, 0, len(store.resourceToPodContainerMap)) - - actualContainerInfo := store.GetContainerInfo("device-id-1", "test-resource") - if actualContainerInfo != nil { - t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) - } - - actualResourceInfo := store.GetResourcesInfo("test-pod", "test-container", "test-namespace") - if actualResourceInfo != nil { - t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) - } + assertMapsDontContainData(t, store) } func TestGetsWhenPodResourcesResponseIsEmpty(t *testing.T) { store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseWithEmptyResponse, nil) store.updateMaps() - assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) - assert.Equal(t, 0, len(store.resourceToPodContainerMap)) - - actualContainerInfo := store.GetContainerInfo("device-id-1", "test-resource") - if actualContainerInfo != nil { - t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) - } - - actualResourceInfo := store.GetResourcesInfo("test-pod", "test-container", "test-namespace") - if actualResourceInfo != nil { - t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) - } + assertMapsDontContainData(t, store) } func TestGetsWhenPodResourcesThrowsError(t *testing.T) { store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponseWithEmptyResponse, fmt.Errorf("mocked behavior")) store.updateMaps() - assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) - assert.Equal(t, 0, len(store.resourceToPodContainerMap)) + assertMapsDontContainData(t, store) +} - actualContainerInfo := store.GetContainerInfo("device-id-1", "test-resource") - if actualContainerInfo != nil { - t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) - } +func TestAddResourceName(t *testing.T) { + store := constructPodResourcesStore(make(map[ContainerInfo][]ResourceInfo), make(map[ResourceInfo]ContainerInfo), listPodResourcesResponse, nil) - actualResourceInfo := store.GetResourcesInfo("test-pod", "test-container", "test-namespace") - if actualResourceInfo != nil { - t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) - } + store.resourceNameSet = make(map[string]struct{}) + store.updateMaps() + assertMapsDontContainData(t, store) + + // After adding resource to map + store.AddResourceName(defaultResourceName) + store.updateMaps() + assertMapsContainData(t, store) } func constructPodResourcesStore(containerToDevices map[ContainerInfo][]ResourceInfo, deviceToContainer map[ResourceInfo]ContainerInfo, podResourcesResponse *podresourcesv1.ListPodResourcesResponse, podResourcesError error) *PodResourcesStore { @@ -232,6 +229,7 @@ func constructPodResourcesStore(containerToDevices map[ContainerInfo][]ResourceI return &PodResourcesStore{ containerInfoToResourcesMap: containerToDevices, resourceToPodContainerMap: deviceToContainer, + resourceNameSet: resourceNameSet, lastRefreshed: time.Now(), ctx: context.Background(), cancel: func() {}, @@ -239,3 +237,31 @@ func constructPodResourcesStore(containerToDevices map[ContainerInfo][]ResourceI podResourcesClient: &MockPodResourcesClient{podResourcesResponse, podResourcesError, false}, } } + +func assertMapsContainData(t *testing.T, store *PodResourcesStore) { + assert.Equal(t, len(expectedContainerInfoToResourcesMap), len(store.containerInfoToResourcesMap)) + assert.Equal(t, len(expectedResourceToPodContainerMap), len(store.resourceToPodContainerMap)) + + assert.Equal(t, expectedContainerInfo, *store.GetContainerInfo(defaultDeviceID1, defaultResourceName)) + assert.Equal(t, expectedResourceInfo, *store.GetResourcesInfo(defaultPodName, defaultContainerName, defaultNamespace)) + + actualResourceInfo := store.GetResourcesInfo(defaultPodNameNoDevice, defaultContainerNameNoDevice, defaultNamespaceNoDevice) + if actualResourceInfo != nil { + t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) + } +} + +func assertMapsDontContainData(t *testing.T, store *PodResourcesStore) { + assert.Equal(t, 0, len(store.containerInfoToResourcesMap)) + assert.Equal(t, 0, len(store.resourceToPodContainerMap)) + + actualContainerInfo := store.GetContainerInfo(defaultDeviceID1, defaultResourceName) + if actualContainerInfo != nil { + t.Errorf("Expected GetContainerInfo to return nil for an unexpected key, but got %v", actualContainerInfo) + } + + actualResourceInfo := store.GetResourcesInfo(defaultPodName, defaultContainerName, defaultNamespace) + if actualResourceInfo != nil { + t.Errorf("Expected GetResourcesInfo to return nil for an unexpected key, but got %v", actualResourceInfo) + } +} From b0d10008977db8499bb985dc595026b9754f5721 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Tue, 27 Feb 2024 15:22:53 +0000 Subject: [PATCH 08/15] skip updating maps when no resource names are allowlisted --- .../internal/stores/podresourcesstore.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go index c874846d327f..299535a6c423 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -102,6 +102,11 @@ func (p *PodResourcesStore) updateMaps() { p.containerInfoToResourcesMap = make(map[ContainerInfo][]ResourceInfo) p.resourceToPodContainerMap = make(map[ResourceInfo]ContainerInfo) + if len(p.resourceNameSet) == 0 { + p.logger.Warn("No resource names allowlisted thus skipping updating of maps.") + return + } + devicePods, err := p.podResourcesClient.ListPods() if err != nil { From 81bb4f2e17512c4b05c1753cd708fe373b577626 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Fri, 1 Mar 2024 16:56:35 +0000 Subject: [PATCH 09/15] minor refactoring in the podresourcesclient --- .../internal/stores/kubeletutil/podresourcesclient.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go index caf28218c0b8..214a9e968e92 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go @@ -6,11 +6,11 @@ package kubeletutil // import "github.com/open-telemetry/opentelemetry-collector import ( "context" "fmt" + "google.golang.org/grpc" "net" "os" "time" - "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" ) @@ -29,12 +29,12 @@ func NewPodResourcesClient() (*PodResourcesClient, error) { podResourcesClient := &PodResourcesClient{} conn, err := podResourcesClient.connectToServer(socketPath) + podResourcesClient.conn = conn if err != nil { return nil, fmt.Errorf("failed to connect to server: %w", err) } podResourcesClient.delegateClient = podresourcesapi.NewPodResourcesListerClient(conn) - podResourcesClient.conn = conn return podResourcesClient, nil } @@ -80,5 +80,8 @@ func (p *PodResourcesClient) ListPods() (*podresourcesapi.ListPodResourcesRespon } func (p *PodResourcesClient) Shutdown() { - p.conn.Close() + err := p.conn.Close() + if err != nil { + return + } } From ccc26ca4f405b6ae8cbb4a005fa20e3bf64da4e9 Mon Sep 17 00:00:00 2001 From: Aditya Purang Date: Mon, 4 Mar 2024 18:20:00 +0000 Subject: [PATCH 10/15] make fields of the ContainerInfo and REsourceInfo structs publicly accessible --- .../internal/stores/podresourcesstore.go | 26 +++++----- .../internal/stores/podresourcesstore_test.go | 48 +++++++++---------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go index 299535a6c423..d2266ffd1cbb 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore.go @@ -25,14 +25,14 @@ var ( ) type ContainerInfo struct { - podName string - containerName string - namespace string + PodName string + ContainerName string + Namespace string } type ResourceInfo struct { - resourceName string - deviceID string + ResourceName string + DeviceID string } type PodResourcesClientInterface interface { @@ -119,17 +119,17 @@ func (p *PodResourcesStore) updateMaps() { for _, device := range container.GetDevices() { containerInfo := ContainerInfo{ - podName: pod.GetName(), - namespace: pod.GetNamespace(), - containerName: container.GetName(), + PodName: pod.GetName(), + Namespace: pod.GetNamespace(), + ContainerName: container.GetName(), } for _, deviceID := range device.GetDeviceIds() { resourceInfo := ResourceInfo{ - resourceName: device.GetResourceName(), - deviceID: deviceID, + ResourceName: device.GetResourceName(), + DeviceID: deviceID, } - _, found := p.resourceNameSet[resourceInfo.resourceName] + _, found := p.resourceNameSet[resourceInfo.ResourceName] if found { p.containerInfoToResourcesMap[containerInfo] = append(p.containerInfoToResourcesMap[containerInfo], resourceInfo) p.resourceToPodContainerMap[resourceInfo] = containerInfo @@ -141,7 +141,7 @@ func (p *PodResourcesStore) updateMaps() { } func (p *PodResourcesStore) GetContainerInfo(deviceID string, resourceName string) *ContainerInfo { - key := ResourceInfo{deviceID: deviceID, resourceName: resourceName} + key := ResourceInfo{DeviceID: deviceID, ResourceName: resourceName} if containerInfo, ok := p.resourceToPodContainerMap[key]; ok { return &containerInfo } @@ -149,7 +149,7 @@ func (p *PodResourcesStore) GetContainerInfo(deviceID string, resourceName strin } func (p *PodResourcesStore) GetResourcesInfo(podName string, containerName string, namespace string) *[]ResourceInfo { - key := ContainerInfo{podName: podName, containerName: containerName, namespace: namespace} + key := ContainerInfo{PodName: podName, ContainerName: containerName, Namespace: namespace} if resourceInfo, ok := p.containerInfoToResourcesMap[key]; ok { return &resourceInfo } diff --git a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go index 9ecbb1cf4cee..02fbac32f44f 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/podresourcesstore_test.go @@ -32,54 +32,54 @@ const ( var ( expectedContainerInfoToResourcesMap = map[ContainerInfo][]ResourceInfo{ { - podName: defaultPodName, - containerName: defaultContainerName, - namespace: defaultNamespace, + PodName: defaultPodName, + ContainerName: defaultContainerName, + Namespace: defaultNamespace, }: { { - resourceName: defaultResourceName, - deviceID: defaultDeviceID1, + ResourceName: defaultResourceName, + DeviceID: defaultDeviceID1, }, { - resourceName: defaultResourceName, - deviceID: defaultDeviceID2, + ResourceName: defaultResourceName, + DeviceID: defaultDeviceID2, }, }, } expectedResourceToPodContainerMap = map[ResourceInfo]ContainerInfo{ { - resourceName: defaultResourceName, - deviceID: defaultDeviceID1, + ResourceName: defaultResourceName, + DeviceID: defaultDeviceID1, }: { - podName: defaultPodName, - containerName: defaultContainerName, - namespace: defaultNamespace, + PodName: defaultPodName, + ContainerName: defaultContainerName, + Namespace: defaultNamespace, }, { - resourceName: defaultResourceName, - deviceID: defaultDeviceID2, + ResourceName: defaultResourceName, + DeviceID: defaultDeviceID2, }: { - podName: defaultPodName, - containerName: defaultContainerName, - namespace: defaultNamespace, + PodName: defaultPodName, + ContainerName: defaultContainerName, + Namespace: defaultNamespace, }, } expectedContainerInfo = ContainerInfo{ - podName: defaultPodName, - containerName: defaultContainerName, - namespace: defaultNamespace, + PodName: defaultPodName, + ContainerName: defaultContainerName, + Namespace: defaultNamespace, } expectedResourceInfo = []ResourceInfo{ { - resourceName: defaultResourceName, - deviceID: defaultDeviceID1, + ResourceName: defaultResourceName, + DeviceID: defaultDeviceID1, }, { - resourceName: defaultResourceName, - deviceID: defaultDeviceID2, + ResourceName: defaultResourceName, + DeviceID: defaultDeviceID2, }, } From 423710e3d93fed949acb1e5d5367283d04ee622d Mon Sep 17 00:00:00 2001 From: Aditya Purang <44022838+aditya-purang@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:39:15 +0000 Subject: [PATCH 11/15] adding changelog --- .chloggen/add-pod-resources-store.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .chloggen/add-pod-resources-store.yaml diff --git a/.chloggen/add-pod-resources-store.yaml b/.chloggen/add-pod-resources-store.yaml new file mode 100644 index 000000000000..67c5d7777528 --- /dev/null +++ b/.chloggen/add-pod-resources-store.yaml @@ -0,0 +1,20 @@ +# Use this changelog template to create an entry for release notes. +# If your change doesn't affect end users, such as a test fix or a tooling change, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: awscontainerinsightreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: added a new podresourcestore which provides mapping from resource to container and vice-versa + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [167] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: this change provides a new store in awscontainerinsightreceiver, which when started provides mapping from resources to container and vice versa using kubelet podresourcesapi. \ No newline at end of file From 9dac565a81edefc2c742e5ebf18a37290c1b1dea Mon Sep 17 00:00:00 2001 From: Aditya Purang <44022838+aditya-purang@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:45:53 +0000 Subject: [PATCH 12/15] fix spacing in go.mod --- receiver/awscontainerinsightreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/awscontainerinsightreceiver/go.mod b/receiver/awscontainerinsightreceiver/go.mod index 6505d7d401d6..dad986eb14fd 100644 --- a/receiver/awscontainerinsightreceiver/go.mod +++ b/receiver/awscontainerinsightreceiver/go.mod @@ -149,7 +149,7 @@ require ( k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - k8s.io/kubelet v0.27.3 // indirect + k8s.io/kubelet v0.27.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect From 687e5961636918be9b1e777b10b9910d89837944 Mon Sep 17 00:00:00 2001 From: Aditya Purang <44022838+aditya-purang@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:55:43 +0000 Subject: [PATCH 13/15] crosslinking go mod with make crosslink --- receiver/awscontainerinsightreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/awscontainerinsightreceiver/go.mod b/receiver/awscontainerinsightreceiver/go.mod index dad986eb14fd..f11abbc4e11c 100644 --- a/receiver/awscontainerinsightreceiver/go.mod +++ b/receiver/awscontainerinsightreceiver/go.mod @@ -149,7 +149,7 @@ require ( k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - k8s.io/kubelet v0.27.3 // indirect + k8s.io/kubelet v0.27.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect From 0cb3bd0ccafdf621e76d8c60767a546f5c65390d Mon Sep 17 00:00:00 2001 From: Aditya Purang <44022838+aditya-purang@users.noreply.github.com> Date: Tue, 5 Mar 2024 19:07:40 +0000 Subject: [PATCH 14/15] tidy mod file using make gotidy --- receiver/awscontainerinsightreceiver/go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/receiver/awscontainerinsightreceiver/go.mod b/receiver/awscontainerinsightreceiver/go.mod index f11abbc4e11c..76345567b4b3 100644 --- a/receiver/awscontainerinsightreceiver/go.mod +++ b/receiver/awscontainerinsightreceiver/go.mod @@ -23,10 +23,12 @@ require ( go.opentelemetry.io/otel/trace v1.23.1 go.uber.org/goleak v1.3.0 go.uber.org/zap v1.26.0 + google.golang.org/grpc v1.61.0 k8s.io/api v0.29.2 k8s.io/apimachinery v0.29.2 k8s.io/client-go v0.29.2 k8s.io/klog v1.0.0 + k8s.io/kubelet v0.27.3 ) require ( @@ -140,7 +142,6 @@ require ( golang.org/x/time v0.4.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/grpc v1.61.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -149,7 +150,6 @@ require ( k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - k8s.io/kubelet v0.27.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect From 0cc295c03a6b90f84bd7eba6e8f99418c8595fc1 Mon Sep 17 00:00:00 2001 From: Aditya Purang <44022838+aditya-purang@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:17:06 +0000 Subject: [PATCH 15/15] rerunning make on project to resolve issues --- .../internal/stores/kubeletutil/podresourcesclient.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go index 214a9e968e92..d9ec7c769522 100644 --- a/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go +++ b/receiver/awscontainerinsightreceiver/internal/stores/kubeletutil/podresourcesclient.go @@ -6,11 +6,11 @@ package kubeletutil // import "github.com/open-telemetry/opentelemetry-collector import ( "context" "fmt" - "google.golang.org/grpc" "net" "os" "time" + "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" )