From fbc48b647068f1af2914d4dc5be3a2f19fd2f20f Mon Sep 17 00:00:00 2001 From: suyadav1 <87668410+suyadav1@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:24:22 -0800 Subject: [PATCH] Fix for ImageId and Repository empty in Container Inventory (#1339) Fix for ImageId and Repository empty issue --- .../input/lib/kubernetescontainerinventory.go | 96 +++++++++----- .../ruby/kubernetes_container_inventory.rb | 125 +++++++++++------- 2 files changed, 145 insertions(+), 76 deletions(-) diff --git a/source/plugins/go/input/lib/kubernetescontainerinventory.go b/source/plugins/go/input/lib/kubernetescontainerinventory.go index 7481aec91..3da6108fe 100644 --- a/source/plugins/go/input/lib/kubernetescontainerinventory.go +++ b/source/plugins/go/input/lib/kubernetescontainerinventory.go @@ -79,9 +79,15 @@ func GetContainerInventoryRecords(podItem map[string]interface{}, batchTime stri if imageIDValue, ok := containerStatusMap["imageID"].(string); ok && imageIDValue != "" { if atLocation := strings.Index(imageIDValue, "@"); atLocation != -1 { containerInventoryRecord["ImageId"] = imageIDValue[atLocation+1:] + } else { + containerInventoryRecord["ImageId"] = imageIDValue } } + if imageValue, ok := containerStatusMap["image"].(string); ok && imageValue != "" { + addImageInfoToContainerInventoryRecord(containerInventoryRecord, imageValue) + } + containerInventoryRecord["ExitCode"] = 0 isContainerTerminated := false isContainerWaiting := false @@ -116,36 +122,12 @@ func GetContainerInventoryRecords(podItem map[string]interface{}, batchTime stri if containerInfoMap, ok := containersInfoMap[containerName]; ok { if imageValue, ok := containerInfoMap["image"]; ok && imageValue != "" { - atLocation := strings.Index(imageValue, "@") - isDigestSpecified := false - if atLocation != -1 { - imageValue = imageValue[:atLocation] - if containerInventoryRecord["ImageId"] == nil || containerInventoryRecord["ImageId"] == "" { - containerInventoryRecord["ImageId"] = imageValue[atLocation+1:] - } - isDigestSpecified = true - } - slashLocation := strings.Index(imageValue, "/") - colonLocation := strings.Index(imageValue, ":") - if colonLocation != -1 { - if slashLocation == -1 { - containerInventoryRecord["Image"] = imageValue[:colonLocation] - } else { - containerInventoryRecord["Repository"] = imageValue[:slashLocation] - containerInventoryRecord["Image"] = imageValue[slashLocation+1 : colonLocation] - } - containerInventoryRecord["ImageTag"] = imageValue[colonLocation+1:] - } else { - if slashLocation == -1 { - containerInventoryRecord["Image"] = imageValue - } else { - containerInventoryRecord["Repository"] = imageValue[:slashLocation] - containerInventoryRecord["Image"] = imageValue[slashLocation+1:] - } - if !isDigestSpecified { - containerInventoryRecord["ImageTag"] = "latest" - } - } + addImageInfoToContainerInventoryRecord(containerInventoryRecord, imageValue) + } + + // if the repository is still not populated, set a default value as docker.io + if containerInventoryRecord["Repository"] == nil || containerInventoryRecord["Repository"] == "" { + containerInventoryRecord["Repository"] = "docker.io" } podName := containerInfoMap["PodName"] @@ -182,6 +164,58 @@ func GetContainerInventoryRecords(podItem map[string]interface{}, batchTime stri return containerInventoryRecords } +func addImageInfoToContainerInventoryRecord(containerInventoryRecord map[string]interface{}, imageValue string) { + // image can be in any one of below formats: + // repository/image[:imagetag | @digest], repository/image:imagetag@digest, repo/image, image:imagetag, image@digest, image + if imageValue == "" { + return + } + atLocation := strings.Index(imageValue, "@") + isDigestSpecified := false + if atLocation != -1 { + if containerInventoryRecord["ImageId"] == nil || containerInventoryRecord["ImageId"] == "" { + containerInventoryRecord["ImageId"] = imageValue[atLocation+1:] + } + imageValue = imageValue[:atLocation] + isDigestSpecified = true + } + slashLocation := strings.Index(imageValue, "/") + colonLocation := strings.Index(imageValue, ":") + if colonLocation != -1 { + if slashLocation == -1 { + if containerInventoryRecord["Image"] == nil || containerInventoryRecord["Image"] == "" { + containerInventoryRecord["Image"] = imageValue[:colonLocation] + } + } else { + if containerInventoryRecord["Repository"] == nil || containerInventoryRecord["Repository"] == "" { + containerInventoryRecord["Repository"] = imageValue[:slashLocation] + } + if containerInventoryRecord["Image"] == nil || containerInventoryRecord["Image"] == "" { + containerInventoryRecord["Image"] = imageValue[slashLocation+1 : colonLocation] + } + } + if containerInventoryRecord["ImageTag"] == nil || containerInventoryRecord["ImageTag"] == "" { + containerInventoryRecord["ImageTag"] = imageValue[colonLocation+1:] + } + } else { + if slashLocation == -1 { + if containerInventoryRecord["Image"] == nil || containerInventoryRecord["Image"] == "" { + containerInventoryRecord["Image"] = imageValue + } + } else { + if containerInventoryRecord["Repository"] == nil || containerInventoryRecord["Repository"] == "" { + containerInventoryRecord["Repository"] = imageValue[:slashLocation] + } + if containerInventoryRecord["Image"] == nil || containerInventoryRecord["Image"] == "" { + containerInventoryRecord["Image"] = imageValue[slashLocation+1:] + } + } + if !isDigestSpecified && (containerInventoryRecord["ImageTag"] == nil || containerInventoryRecord["ImageTag"] == "") { + containerInventoryRecord["ImageTag"] = "latest" + } + } +} + func getContainersInfoMap(podItem map[string]interface{}, isWindows bool) map[string]map[string]string { containersInfoMap := make(map[string]map[string]string) @@ -562,7 +596,7 @@ func GetContainerInventory(namespaceFilteringMode string, namespaces []string, b return GetContainerInventoryHelper(podList, namespaceFilteringMode, namespaces, batchTime) } -func GetContainerInventoryHelper(podList map[string]interface{}, namespaceFilteringMode string, namespaces []string, batchTime string) ([]string, []map[string]interface{}){ +func GetContainerInventoryHelper(podList map[string]interface{}, namespaceFilteringMode string, namespaces []string, batchTime string) ([]string, []map[string]interface{}) { containerIds := []string{} containerInventory := []map[string]interface{}{} clusterCollectEnvironmentVar := os.Getenv("AZMON_CLUSTER_COLLECT_ENV_VAR") diff --git a/source/plugins/ruby/kubernetes_container_inventory.rb b/source/plugins/ruby/kubernetes_container_inventory.rb index 3d4598326..2dc23d5c6 100644 --- a/source/plugins/ruby/kubernetes_container_inventory.rb +++ b/source/plugins/ruby/kubernetes_container_inventory.rb @@ -43,14 +43,19 @@ def getContainerInventoryRecords(podItem, batchTime, clusterCollectEnvironmentVa # for containers that have image issues (like invalid image/tag etc..) this will be empty. do not make it all 0 containerInventoryRecord["InstanceID"] = containerId end - # imagedId is of the format - repo@sha256:imageid + # imagedId is either of the the format - repo@sha256:imageid or sha256:imageid imageIdValue = containerStatus["imageID"] if !imageIdValue.nil? && !imageIdValue.empty? atLocation = imageIdValue.index("@") if !atLocation.nil? containerInventoryRecord["ImageId"] = imageIdValue[(atLocation + 1)..-1] + else + containerInventoryRecord["ImageId"] = imageIdValue end end + + addImageInfoToContainerInventoryRecord(containerInventoryRecord, containerStatus["image"]) + containerInventoryRecord["ExitCode"] = 0 isContainerTerminated = false isContainerWaiting = false @@ -84,49 +89,12 @@ def getContainerInventoryRecords(podItem, batchTime, clusterCollectEnvironmentVa end containerInfoMap = containersInfoMap[containerName] - # image can be in any one of below format in spec - # repository/image[:imagetag | @digest], repository/image:imagetag@digest, repo/image, image:imagetag, image@digest, image - imageValue = containerInfoMap["image"] - if !imageValue.nil? && !imageValue.empty? - # Find delimiters in image format - atLocation = imageValue.index("@") - isDigestSpecified = false - if !atLocation.nil? - # repository/image@digest or repository/image:imagetag@digest, image@digest - imageValue = imageValue[0..(atLocation - 1)] - # Use Digest from the spec's image in case when the status doesnt get populated i.e. container in pending or image pull back etc. - if containerInventoryRecord["ImageId"].nil? || containerInventoryRecord["ImageId"].empty? - containerInventoryRecord["ImageId"] = imageValue[(atLocation + 1)..-1] - end - isDigestSpecified = true - end - slashLocation = imageValue.index("/") - colonLocation = imageValue.index(":") - if !colonLocation.nil? - if slashLocation.nil? - # image:imagetag - containerInventoryRecord["Image"] = imageValue[0..(colonLocation - 1)] - else - # repository/image:imagetag - containerInventoryRecord["Repository"] = imageValue[0..(slashLocation - 1)] - containerInventoryRecord["Image"] = imageValue[(slashLocation + 1)..(colonLocation - 1)] - end - containerInventoryRecord["ImageTag"] = imageValue[(colonLocation + 1)..-1] - else - if slashLocation.nil? - # image - containerInventoryRecord["Image"] = imageValue - else - # repo/image - containerInventoryRecord["Repository"] = imageValue[0..(slashLocation - 1)] - containerInventoryRecord["Image"] = imageValue[(slashLocation + 1)..-1] - end - # if no tag specified, k8s assumes latest as imagetag and this is same behavior from docker API and from status. - # Ref - https://kubernetes.io/docs/concepts/containers/images/#image-names - if isDigestSpecified == false - containerInventoryRecord["ImageTag"] = "latest" - end - end + # Populate the fields related to the image if not already populated + addImageInfoToContainerInventoryRecord(containerInventoryRecord, containerInfoMap["image"]) + + # if the repository is still not populated, set a default value as docker.io + if containerInventoryRecord["Repository"].nil? || containerInventoryRecord["Repository"].empty? + containerInventoryRecord["Repository"] = "docker.io" end podName = containerInfoMap["PodName"] @@ -169,6 +137,73 @@ def getContainerInventoryRecords(podItem, batchTime, clusterCollectEnvironmentVa return containerInventoryRecords end + def addImageInfoToContainerInventoryRecord(containerInventoryRecord, imageValue) + begin + if imageValue.nil? || imageValue.empty? + return + end + # image can be in any one of below formats: + # repository/image[:imagetag | @digest], repository/image:imagetag@digest, repo/image, image:imagetag, image@digest, image + # Find delimiters in image format + atLocation = imageValue.index("@") + isDigestSpecified = false + if !atLocation.nil? + # Use Digest from the spec's image in case when the status doesnt get populated i.e. container in pending or image pull back etc. + if containerInventoryRecord["ImageId"].nil? || containerInventoryRecord["ImageId"].empty? + containerInventoryRecord["ImageId"] = imageValue[(atLocation + 1)..-1] + end + # repository/image@digest or repository/image:imagetag@digest, image@digest + imageValue = imageValue[0..(atLocation - 1)] + isDigestSpecified = true + end + slashLocation = imageValue.index("/") + colonLocation = imageValue.index(":") + if !colonLocation.nil? + if slashLocation.nil? + # image:imagetag + if containerInventoryRecord["Image"].nil? || containerInventoryRecord["Image"].empty? + containerInventoryRecord["Image"] = imageValue[0..(colonLocation - 1)] + end + else + # repository/image:imagetag + if containerInventoryRecord["Repository"].nil? || containerInventoryRecord["Repository"].empty? + containerInventoryRecord["Repository"] = imageValue[0..(slashLocation - 1)] + end + if containerInventoryRecord["Image"].nil? || containerInventoryRecord["Image"].empty? + containerInventoryRecord["Image"] = imageValue[(slashLocation + 1)..(colonLocation - 1)] + end + end + if containerInventoryRecord["ImageTag"].nil? || containerInventoryRecord["ImageTag"].empty? + containerInventoryRecord["ImageTag"] = imageValue[(colonLocation + 1)..-1] + end + else + if slashLocation.nil? + # image + if containerInventoryRecord["Image"].nil? || containerInventoryRecord["Image"].empty? + containerInventoryRecord["Image"] = imageValue + end + else + # repo/image + if containerInventoryRecord["Repository"].nil? || containerInventoryRecord["Repository"].empty? + containerInventoryRecord["Repository"] = imageValue[0..(slashLocation - 1)] + end + if containerInventoryRecord["Image"].nil? || containerInventoryRecord["Image"].empty? + containerInventoryRecord["Image"] = imageValue[(slashLocation + 1)..-1] + end + end + # if no tag specified, k8s assumes latest as imagetag and this is same behavior from docker API and from status. + # Ref - https://kubernetes.io/docs/concepts/containers/images/#image-names + if isDigestSpecified == false && (containerInventoryRecord["ImageTag"].nil? || containerInventoryRecord["ImageTag"].empty?) + containerInventoryRecord["ImageTag"] = "latest" + end + end + rescue => error + $log.warn("KubernetesContainerInventory::addImageInfoToContainerInventoryRecord : Add Image Info to Container Inventory Records failed: #{error}") + $log.debug_backtrace(error.backtrace) + ApplicationInsightsUtility.sendExceptionTelemetry(error) + end + end + def getContainersInfoMap(podItem, isWindows) containersInfoMap = {} begin @@ -405,4 +440,4 @@ def isSandboxingPod(podItem) return isKataRuntimePod end end -end +end \ No newline at end of file