diff --git a/receiver/k8sclusterreceiver/internal/container/containers.go b/receiver/k8sclusterreceiver/internal/container/containers.go index 5ca4eb26cf72e..74f9a48bd14de 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers.go +++ b/receiver/k8sclusterreceiver/internal/container/containers.go @@ -79,56 +79,51 @@ func RecordSpecMetrics(logger *zap.Logger, mb *metadata.MetricsBuilder, c corev1 } } - rb := mb.NewResourceBuilder() - var containerID string - var imageStr string - for i := range pod.Status.ContainerStatuses { - cs := pod.Status.ContainerStatuses[i] - if cs.Name != c.Name { - continue - } - containerID = cs.ContainerID - imageStr = cs.Image - mb.RecordK8sContainerRestartsDataPoint(ts, int64(cs.RestartCount)) - mb.RecordK8sContainerReadyDataPoint(ts, boolToInt64(cs.Ready)) - if cs.LastTerminationState.Terminated != nil { - rb.SetK8sContainerStatusLastTerminatedReason(cs.LastTerminationState.Terminated.Reason) - } - switch { - case cs.State.Running != nil: - mb.RecordK8sContainerStatusStateDataPoint(ts, 1, metadata.AttributeK8sContainerStatusStateRunning) - mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateWaiting) - mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateTerminated) - case cs.State.Terminated != nil: - mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateRunning) - mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateWaiting) - mb.RecordK8sContainerStatusStateDataPoint(ts, 1, metadata.AttributeK8sContainerStatusStateTerminated) - case cs.State.Waiting != nil: - mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateRunning) - mb.RecordK8sContainerStatusStateDataPoint(ts, 1, metadata.AttributeK8sContainerStatusStateWaiting) - mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateTerminated) - } - - // Record k8s.container.status.reason metric: for each known reason emit 1 for the current one, 0 otherwise. - var reason string - switch { - case cs.State.Terminated != nil: - reason = cs.State.Terminated.Reason - case cs.State.Waiting != nil: - reason = cs.State.Waiting.Reason - default: - reason = "" - } - // Emit in deterministic order for test stability. - for _, attrVal := range allContainerStatusReasons { - val := int64(0) - if reason != "" && reason == attrVal.String() { - val = 1 - } - mb.RecordK8sContainerStatusReasonDataPoint(ts, val, attrVal) - } - break - } + rb := mb.NewResourceBuilder() + var containerID string + var imageStr string + if cs, ok := findContainerStatusForName(pod, c.Name); ok { + containerID = cs.ContainerID + imageStr = cs.Image + mb.RecordK8sContainerRestartsDataPoint(ts, int64(cs.RestartCount)) + mb.RecordK8sContainerReadyDataPoint(ts, boolToInt64(cs.Ready)) + if cs.LastTerminationState.Terminated != nil { + rb.SetK8sContainerStatusLastTerminatedReason(cs.LastTerminationState.Terminated.Reason) + } + switch { + case cs.State.Running != nil: + mb.RecordK8sContainerStatusStateDataPoint(ts, 1, metadata.AttributeK8sContainerStatusStateRunning) + mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateWaiting) + mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateTerminated) + case cs.State.Terminated != nil: + mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateRunning) + mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateWaiting) + mb.RecordK8sContainerStatusStateDataPoint(ts, 1, metadata.AttributeK8sContainerStatusStateTerminated) + case cs.State.Waiting != nil: + mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateRunning) + mb.RecordK8sContainerStatusStateDataPoint(ts, 1, metadata.AttributeK8sContainerStatusStateWaiting) + mb.RecordK8sContainerStatusStateDataPoint(ts, 0, metadata.AttributeK8sContainerStatusStateTerminated) + } + + // Record k8s.container.status.reason metric: for each known reason emit 1 for the current one, 0 otherwise. + var reason string + switch { + case cs.State.Terminated != nil: + reason = cs.State.Terminated.Reason + case cs.State.Waiting != nil: + reason = cs.State.Waiting.Reason + default: + reason = "" + } + // Emit in deterministic order for test stability. + for _, attrVal := range allContainerStatusReasons { + val := int64(0) + if reason != "" && reason == attrVal.String() { + val = 1 + } + mb.RecordK8sContainerStatusReasonDataPoint(ts, val, attrVal) + } + } rb.SetK8sPodUID(string(pod.UID)) rb.SetK8sPodName(pod.Name) @@ -146,6 +141,30 @@ func RecordSpecMetrics(logger *zap.Logger, mb *metadata.MetricsBuilder, c corev1 mb.EmitForResource(metadata.WithResource(rb.Emit())) } +// findContainerStatusForName returns the ContainerStatus matching the given name from +// any of container, init container, or ephemeral container statuses. +func findContainerStatusForName(pod *corev1.Pod, name string) (*corev1.ContainerStatus, bool) { + for i := range pod.Status.ContainerStatuses { + cs := &pod.Status.ContainerStatuses[i] + if cs.Name == name { + return cs, true + } + } + for i := range pod.Status.InitContainerStatuses { + cs := &pod.Status.InitContainerStatuses[i] + if cs.Name == name { + return cs, true + } + } + for i := range pod.Status.EphemeralContainerStatuses { + cs := &pod.Status.EphemeralContainerStatuses[i] + if cs.Name == name { + return cs, true + } + } + return nil, false +} + func GetMetadata(pod *corev1.Pod, cs corev1.ContainerStatus, logger *zap.Logger) *metadata.KubernetesMetadata { mdata := map[string]string{} diff --git a/receiver/k8sclusterreceiver/internal/pod/pods.go b/receiver/k8sclusterreceiver/internal/pod/pods.go index b0e274c7ff279..f6f6d8a72a8f9 100644 --- a/receiver/k8sclusterreceiver/internal/pod/pods.go +++ b/receiver/k8sclusterreceiver/internal/pod/pods.go @@ -60,6 +60,30 @@ func Transform(pod *corev1.Pod) *corev1.Pod { LastTerminationState: cs.LastTerminationState, }) } + for i := range pod.Status.InitContainerStatuses { + cs := &pod.Status.InitContainerStatuses[i] + newPod.Status.InitContainerStatuses = append(newPod.Status.InitContainerStatuses, corev1.ContainerStatus{ + Name: cs.Name, + Image: cs.Image, + ContainerID: cs.ContainerID, + RestartCount: cs.RestartCount, + Ready: cs.Ready, + State: cs.State, + LastTerminationState: cs.LastTerminationState, + }) + } + for i := range pod.Status.EphemeralContainerStatuses { + cs := &pod.Status.EphemeralContainerStatuses[i] + newPod.Status.EphemeralContainerStatuses = append(newPod.Status.EphemeralContainerStatuses, corev1.ContainerStatus{ + Name: cs.Name, + Image: cs.Image, + ContainerID: cs.ContainerID, + RestartCount: cs.RestartCount, + Ready: cs.Ready, + State: cs.State, + LastTerminationState: cs.LastTerminationState, + }) + } for i := range pod.Spec.Containers { c := &pod.Spec.Containers[i] newPod.Spec.Containers = append(newPod.Spec.Containers, corev1.Container{ @@ -70,6 +94,28 @@ func Transform(pod *corev1.Pod) *corev1.Pod { }, }) } + for i := range pod.Spec.InitContainers { + c := &pod.Spec.InitContainers[i] + newPod.Spec.InitContainers = append(newPod.Spec.InitContainers, corev1.Container{ + Name: c.Name, + Resources: corev1.ResourceRequirements{ + Requests: c.Resources.Requests, + Limits: c.Resources.Limits, + }, + }) + } + for i := range pod.Spec.EphemeralContainers { + c := &pod.Spec.EphemeralContainers[i] + newPod.Spec.EphemeralContainers = append(newPod.Spec.EphemeralContainers, corev1.EphemeralContainer{ + EphemeralContainerCommon: corev1.EphemeralContainerCommon{ + Name: c.Name, + Resources: corev1.ResourceRequirements{ + Requests: c.Resources.Requests, + Limits: c.Resources.Limits, + }, + }, + }) + } return newPod } @@ -88,6 +134,20 @@ func RecordMetrics(logger *zap.Logger, mb *metadata.MetricsBuilder, pod *corev1. c := pod.Spec.Containers[i] container.RecordSpecMetrics(logger, mb, c, pod, ts) } + for i := range pod.Spec.InitContainers { + c := pod.Spec.InitContainers[i] + container.RecordSpecMetrics(logger, mb, c, pod, ts) + } + for i := range pod.Spec.EphemeralContainers { + ec := pod.Spec.EphemeralContainers[i] + // Convert EphemeralContainer to a minimal Container for metrics (name/resources) + c := corev1.Container{ + Name: ec.Name, + Resources: ec.Resources, + } + container.RecordSpecMetrics(logger, mb, c, pod, ts) + } + } func reasonToInt(reason string) int32 { @@ -277,6 +337,16 @@ func getPodContainerProperties(pod *corev1.Pod, logger *zap.Logger) map[experime md := container.GetMetadata(pod, cs, logger) km[md.ResourceID] = md } + for i := range pod.Status.InitContainerStatuses { + cs := pod.Status.InitContainerStatuses[i] + md := container.GetMetadata(pod, cs, logger) + km[md.ResourceID] = md + } + for i := range pod.Status.EphemeralContainerStatuses { + cs := pod.Status.EphemeralContainerStatuses[i] + md := container.GetMetadata(pod, cs, logger) + km[md.ResourceID] = md + } return km } diff --git a/receiver/k8sclusterreceiver/receiver.go b/receiver/k8sclusterreceiver/receiver.go index d06cb6a9c513c..eef6cb25d5274 100644 --- a/receiver/k8sclusterreceiver/receiver.go +++ b/receiver/k8sclusterreceiver/receiver.go @@ -54,7 +54,6 @@ func (kr *kubernetesReceiver) startReceiver(ctx context.Context, host component. return errors.New("unable to get exporters") } exporters := ge.GetExporters() - if err := kr.resourceWatcher.setupMetadataExporters( exporters[pipeline.SignalMetrics], kr.config.MetadataExporters); err != nil { return err @@ -167,7 +166,7 @@ func (kr *kubernetesReceiver) dispatchMetrics(ctx context.Context) { kr.obsrecv.EndMetricsOp(c, metadata.Type.String(), numPoints, err) } -// newMetricsReceiver creates the Kubernetes cluster receiver with the given configuration. +// newMetricsReceiver creates the Kubernetes cluster metrics receiver with the given configuration. func newMetricsReceiver( ctx context.Context, set receiver.Settings, cfg component.Config, consumer consumer.Metrics, ) (receiver.Metrics, error) { @@ -186,7 +185,7 @@ func newMetricsReceiver( return r, nil } -// newMetricsReceiver creates the Kubernetes cluster receiver with the given configuration. +// newLogsReceiver creates the Kubernetes cluster logs receiver with the given configuration. func newLogsReceiver( ctx context.Context, set receiver.Settings, cfg component.Config, consumer consumer.Logs, ) (receiver.Logs, error) { @@ -205,7 +204,7 @@ func newLogsReceiver( return r, nil } -// newMetricsReceiver creates the Kubernetes cluster receiver with the given configuration. +// newReceiver creates the Kubernetes cluster receiver with the given configuration. func newReceiver(_ context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { rCfg := cfg.(*Config) obsrecv, err := receiverhelper.NewObsReport( @@ -227,4 +226,4 @@ func newReceiver(_ context.Context, set receiver.Settings, cfg component.Config) config: rCfg, obsrecv: obsrecv, }, nil -} +} \ No newline at end of file