Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions plugins/container/go-worker/pkg/container/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,7 @@ func (c *containerdEngine) ctrToInfo(namespacedContext context.Context, containe
imageSize = image.Target().Size
}
}
imageRepoTag := strings.Split(info.Image, ":")
if len(imageRepoTag) == 2 {
imageRepo = imageRepoTag[0]
imageTag = imageRepoTag[1]
}
imageRepo, imageTag = parseImageRepoTag(info.Image)

// Network related - TODO

Expand Down
19 changes: 5 additions & 14 deletions plugins/container/go-worker/pkg/container/cri.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,27 +322,18 @@ func (c *criEngine) ctrToInfo(ctx context.Context, ctr *v1.ContainerStatus, podS
}
}

imageRepoTag := strings.Split(imageName, ":")
imageRepo = imageRepoTag[0]
if len(imageRepoTag) == 2 {
imageTag = imageRepoTag[1]
}
imageRepo, imageTag = parseImageRepoTag(imageName)

if getTagFromImage {
imageRepoTag = strings.Split(ctr.GetImage().GetImage(), ":")
if len(imageRepoTag) == 2 {
imageTag = imageRepoTag[1]
_, tag := parseImageRepoTag(ctr.GetImage().GetImage())
if tag != "" {
imageTag = tag
imageName += ":" + imageTag
}
}

imageStr := ctrInfo.getImage()
imageStrs := strings.Split(imageStr, ":")
if len(imageStrs) == 2 {
imageID = imageStrs[1]
} else {
imageID = imageStr
}
_, imageID = parseImageRepoTag(imageStr)
if imageID == "" {
imageID = ctr.GetImageId()
}
Expand Down
12 changes: 6 additions & 6 deletions plugins/container/go-worker/pkg/container/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,23 @@ func (dc *dockerEngine) ctrToInfo(ctx context.Context, ctr container.InspectResp
}

for _, repoTag := range img.RepoTags {
repoTagsParts := strings.Split(repoTag, ":")
if len(repoTagsParts) != 2 {
repo, tag := parseImageRepoTag(repoTag)
if repo == "" || tag == "" {
// malformed
continue
}
if imageRepo == "" {
imageRepo = repoTagsParts[0]
imageRepo = repo
}
if strings.Contains(repoTag, imageRepo) {
imageTag = repoTagsParts[1]
imageTag = tag
break
}
}

imgName := ctr.Image
if !strings.Contains(imgName, "/") && strings.Contains(imgName, ":") {
imageID = strings.Split(imgName, ":")[1]
if !strings.Contains(imgName, "/") {
_, imageID = parseImageRepoTag(imgName)
}

labels := make(map[string]string)
Expand Down
38 changes: 38 additions & 0 deletions plugins/container/go-worker/pkg/container/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,41 @@ func parsePortBindingHostPort(port string) (uint16, error) {

return uint16(convertedPort), nil
}

// parseImageRepoTag parses a container image string and returns the repository and tag.
// It correctly handles registry URLs with port numbers by only splitting on the last colon
// that appears after the last slash. If the reference includes a digest (via "@"), the
// digest portion is removed first, and then the tag is extracted from the remaining string.
//
// Examples:
// - "registry.example.com:5000/foo/bar:latest@sha256:digest" -> ("registry.example.com:5000/foo/bar", "latest")
// - "registry.example.com:5000/foo/bar@sha256:digest" -> ("registry.example.com:5000/foo/bar", "")
// - "registry.example.com:5000/foo/bar:latest" -> ("registry.example.com:5000/foo/bar", "latest")
// - "registry.example.com:5000/foo/bar" -> ("registry.example.com:5000/foo/bar", "")
// - "foo/bar:latest" -> ("foo/bar", "latest")
// - "foo:latest" -> ("foo", "latest")
func parseImageRepoTag(image string) (repo, tag string) {
if image == "" {
return "", ""
}

// Remove digest portion (e.g., @sha256:...) if present
if at := strings.Index(image, "@"); at != -1 {
image = image[:at]
}

// Find the last slash to separate the registry/path from the image name
lastSlash := strings.LastIndex(image, "/")

// Find the last colon after the last slash (if any)
// This colon separates the tag from the repo
lastColon := strings.LastIndex(image, ":")

// If there's no colon, or the colon appears before the last slash
// (meaning it's part of a registry port), then there's no tag
if lastColon == -1 || (lastSlash != -1 && lastColon < lastSlash) {
return image, ""
}

return image[:lastColon], image[lastColon+1:]
}
67 changes: 67 additions & 0 deletions plugins/container/go-worker/pkg/container/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,70 @@ func TestParsePortBindingHostPort(t *testing.T) {
})
}
}

func TestParseImageRepoTag(t *testing.T) {
tCases := map[string]struct {
image string
expectedRepo string
expectedTag string
}{
"Registry with port and tag": {
image: "registry.example.com:5000/foo/bar:latest",
expectedRepo: "registry.example.com:5000/foo/bar",
expectedTag: "latest",
},
"Registry with port without tag": {
image: "registry.example.com:5000/foo/bar",
expectedRepo: "registry.example.com:5000/foo/bar",
expectedTag: "",
},
"Simple image with tag": {
image: "foo/bar:latest",
expectedRepo: "foo/bar",
expectedTag: "latest",
},
"Simple image without path with tag": {
image: "foo:latest",
expectedRepo: "foo",
expectedTag: "latest",
},
"Simple image without tag": {
image: "foo/bar",
expectedRepo: "foo/bar",
expectedTag: "",
},
"Empty string": {
image: "",
expectedRepo: "",
expectedTag: "",
},
"Digest based image": {
image: "registry.example.com:5000/foo/bar@sha256:abc123",
expectedRepo: "registry.example.com:5000/foo/bar",
expectedTag: "",
},
"Both tag and digest": {
image: "registry.example.com:5000/foo/bar:latest@sha256:abc123",
expectedRepo: "registry.example.com:5000/foo/bar",
expectedTag: "latest",
},
"Multi-level path with registry port and tag": {
image: "registry.example.com:5000/org/project/image:v1.2.3",
expectedRepo: "registry.example.com:5000/org/project/image",
expectedTag: "v1.2.3",
},
"Localhost with port and tag": {
image: "localhost:5000/myimage:latest",
expectedRepo: "localhost:5000/myimage",
expectedTag: "latest",
},
}

for name, tc := range tCases {
t.Run(name, func(t *testing.T) {
repo, tag := parseImageRepoTag(tc.image)
assert.Equal(t, tc.expectedRepo, repo)
assert.Equal(t, tc.expectedTag, tag)
})
}
}
6 changes: 1 addition & 5 deletions plugins/container/go-worker/pkg/container/podman.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ func (pc *podmanEngine) ctrToInfo(ctr *define.InspectContainerData) event.Info {
imageRepo string
imageTag string
)
imageRepoTag := strings.Split(ctr.ImageName, ":")
if len(imageRepoTag) == 2 {
imageRepo = imageRepoTag[0]
imageTag = imageRepoTag[1]
}
imageRepo, imageTag = parseImageRepoTag(ctr.ImageName)

labels := make(map[string]string)
var (
Expand Down
Loading