Skip to content

Commit

Permalink
[receiver/k8scluster] Remove included tags (open-telemetry#6436)
Browse files Browse the repository at this point in the history
* feat(container.image.name): remove tags included in the name and introduce helper package

* fix(container.image.name): use `` instead "" for regex

* docs(Changelog): add changes

* chore(container.image.name): rename function to ParseImageName

* chore(container.image.name): add logging for parse failures

* feat(container.image.name): use struct to encapsulate return value

* feat(container.image): add sha256 field

* chore(container.image.name): implement change requests
  • Loading branch information
secustor committed Jan 3, 2022
1 parent dfa06ab commit d202af2
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 35 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
- `memcachedreceiver`: Fix some metric units and value types (#6895)
- `sapm` receiver: Use Jaeger status values instead of OpenCensus (#6682)
- `jaeger` receiver/exporter: Parse/set Jaeger status with OTel spec values (#6682)
- `awsecscontainermetricsreceiver`: remove tag from `container.image.name` (#6436)
- `k8sclusterreceiver`: remove tag from `container.image.name` (#6436)

## 🚀 New components 🚀

Expand All @@ -30,7 +32,7 @@
## 💡 Enhancements 💡

- `lokiexporter`: add complete log record to body (#6619)

- `k8sclusterreceiver` add `container.image.tag` attribute (#6436)
- `spanmetricproccessor`: use an LRU cache for the cached Dimensions key-value pairs (#2179)

## v0.41.0
Expand Down
66 changes: 66 additions & 0 deletions internal/common/docker/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package docker // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/docker"

import (
"errors"
"regexp"

"go.uber.org/zap"
)

var (
extractImageRegexp = regexp.MustCompile(`^(?P<repository>([^/\s]+/)?([^:\s]+))(:(?P<tag>[^@\s]+))?(@sha256:(?P<sha256>\d+))?$`)
)

type ImageRef struct {
Repository string
Tag string
SHA256 string
}

// ParseImageName extracts image repository and tag from a combined image reference
// e.g. example.com:5000/alpine/alpine:test --> `example.com:5000/alpine/alpine` and `test`
func ParseImageName(image string) (ImageRef, error) {
if image == "" {
return ImageRef{}, errors.New("empty image")
}

match := extractImageRegexp.FindStringSubmatch(image)
if len(match) == 0 {
return ImageRef{}, errors.New("failed to match regex against image")
}

tag := "latest"
if foundTag := match[extractImageRegexp.SubexpIndex("tag")]; foundTag != "" {
tag = foundTag
}

repository := match[extractImageRegexp.SubexpIndex("repository")]

hash := match[extractImageRegexp.SubexpIndex("sha256")]

return ImageRef{
Repository: repository,
Tag: tag,
SHA256: hash,
}, nil
}

func LogParseError(err error, image string, logger *zap.Logger) {
logger.Debug(err.Error(),
zap.String("image", image),
)
}
162 changes: 162 additions & 0 deletions internal/common/docker/images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package docker

import (
"testing"
)

func TestDockerImageToElements(t *testing.T) {
type args struct {
image string
}
tests := []struct {
name string
args args
wantRepository string
wantTag string
wantSHA256 string
wantErr bool
}{
{
name: "empty string",
args: args{
image: "",
},
wantRepository: "",
wantTag: "",
wantSHA256: "",
wantErr: true,
},
{
name: "malformed image",
args: args{
image: "aaa:",
},
wantRepository: "",
wantTag: "",
wantSHA256: "",
wantErr: true,
},
{
name: "image with sha256 hash",
args: args{
image: "alpine:test@sha256:00000000000000",
},
wantRepository: "alpine",
wantTag: "test",
wantSHA256: "00000000000000",
wantErr: false,
},
{
name: "shorthand only",
args: args{
image: "alpine",
},
wantRepository: "alpine",
wantTag: "latest",
wantSHA256: "",
wantErr: false,
},
{
name: "shorthand with tag",
args: args{
image: "alpine:v1.0.0",
},
wantRepository: "alpine",
wantTag: "v1.0.0",
wantSHA256: "",
wantErr: false,
},
{
name: "repository without registry and tag",
args: args{
image: "alpine/alpine",
},
wantRepository: "alpine/alpine",
wantTag: "latest",
wantSHA256: "",
wantErr: false,
},
{
name: "repository without registry",
args: args{
image: "alpine/alpine:2.0.0",
},
wantRepository: "alpine/alpine",
wantTag: "2.0.0",
wantSHA256: "",
wantErr: false,
},
{
name: "repository with registry and without tag",
args: args{
image: "example.com/alpine/alpine",
},
wantRepository: "example.com/alpine/alpine",
wantTag: "latest",
wantSHA256: "",
wantErr: false,
},
{
name: "repository with registry and tag",
args: args{
image: "example.com/alpine/alpine:1",
},
wantRepository: "example.com/alpine/alpine",
wantTag: "1",
wantSHA256: "",
wantErr: false,
},
{
name: "repository with registry and port but without tag",
args: args{
image: "example.com:3000/alpine/alpine",
},
wantRepository: "example.com:3000/alpine/alpine",
wantTag: "latest",
wantSHA256: "",
wantErr: false,
},
{
name: "repository with registry, port and tag",
args: args{
image: "example.com:3000/alpine/alpine:test",
},
wantRepository: "example.com:3000/alpine/alpine",
wantTag: "test",
wantSHA256: "",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
image, err := ParseImageName(tt.args.image)
if (err != nil) != tt.wantErr {
t.Errorf("ParseImageName() error = %v, wantErr %v", err, tt.wantErr)
return
}
if image.Repository != tt.wantRepository {
t.Errorf("ParseImageName() repository = %v, want %v", image.Repository, tt.wantRepository)
}
if image.Tag != tt.wantTag {
t.Errorf("ParseImageName() tag = %v, want %v", image.Tag, tt.wantTag)
}
if image.SHA256 != tt.wantSHA256 {
t.Errorf("ParseImageName() hash = %v, want %v", image.SHA256, tt.wantSHA256)
}
})
}
}
3 changes: 3 additions & 0 deletions internal/common/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/docker/docker v20.10.12+incompatible
github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11
github.com/stretchr/testify v1.7.0
go.uber.org/zap v1.19.1
)

require (
Expand All @@ -27,6 +28,8 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
Expand Down
12 changes: 12 additions & 0 deletions internal/common/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion internal/coreinternal/goldendataset/resource_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ func appendCloudVMAttributes(attrMap pdata.AttributeMap) {

func appendOnpremK8sAttributes(attrMap pdata.AttributeMap) {
attrMap.UpsertString(conventions.AttributeContainerName, "cert-manager")
attrMap.UpsertString(conventions.AttributeContainerImageName, "quay.io/jetstack/cert-manager-controller:v0.14.2")
attrMap.UpsertString(conventions.AttributeContainerImageName, "quay.io/jetstack/cert-manager-controller")
attrMap.UpsertString(conventions.AttributeContainerImageTag, "v0.14.2")
attrMap.UpsertString(conventions.AttributeK8SClusterName, "docker-desktop")
attrMap.UpsertString(conventions.AttributeK8SNamespaceName, "cert-manager")
attrMap.UpsertString(conventions.AttributeK8SDeploymentName, "cm-1-cert-manager")
Expand Down
3 changes: 3 additions & 0 deletions receiver/awsecscontainermetricsreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/census-instrumentation/opencensus-proto v0.3.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil v0.41.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.41.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.41.0
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/collector v0.41.1-0.20211222180302-3db1d1146483
go.opentelemetry.io/collector/model v0.41.1-0.20211222180302-3db1d1146483
Expand Down Expand Up @@ -50,3 +51,5 @@ require (
replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => ../../internal/coreinternal

replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil => ../../internal/aws/ecsutil

replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/common => ../../internal/common
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (acc *metricDataAccumulator) getMetricsData(containerStatsMap map[string]*C

for _, containerMetadata := range metadata.Containers {

containerResource := containerResource(containerMetadata)
containerResource := containerResource(containerMetadata, logger)
taskResource.Attributes().Range(func(k string, av pdata.AttributeValue) bool {
containerResource.Attributes().Upsert(k, av)
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,26 @@ import (

"go.opentelemetry.io/collector/model/pdata"
conventions "go.opentelemetry.io/collector/model/semconv/v1.5.0"
"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil"
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/docker"
)

func containerResource(cm ecsutil.ContainerMetadata) pdata.Resource {
func containerResource(cm ecsutil.ContainerMetadata, logger *zap.Logger) pdata.Resource {
resource := pdata.NewResource()

image, err := docker.ParseImageName(cm.Image)
if err != nil {
docker.LogParseError(err, cm.Image, logger)
}

resource.Attributes().UpsertString(conventions.AttributeContainerName, cm.ContainerName)
resource.Attributes().UpsertString(conventions.AttributeContainerID, cm.DockerID)
resource.Attributes().UpsertString(attributeECSDockerName, cm.DockerName)
resource.Attributes().UpsertString(conventions.AttributeContainerImageName, cm.Image)
resource.Attributes().UpsertString(conventions.AttributeContainerImageName, image.Repository)
resource.Attributes().UpsertString(attributeContainerImageID, cm.ImageID)
resource.Attributes().UpsertString(conventions.AttributeContainerImageTag, getVersionFromIamge(cm.Image))
resource.Attributes().UpsertString(conventions.AttributeContainerImageTag, image.Tag)
resource.Attributes().UpsertString(attributeContainerCreatedAt, cm.CreatedAt)
resource.Attributes().UpsertString(attributeContainerStartedAt, cm.StartedAt)
if cm.FinishedAt != "" {
Expand Down Expand Up @@ -81,17 +89,6 @@ func getResourceFromARN(arn string) (string, string, string) {
return region, accountID, taskID
}

func getVersionFromIamge(image string) string {
if image == "" {
return ""
}
splits := strings.Split(image, ":")
if len(splits) == 1 {
return "latest"
}
return splits[len(splits)-1]
}

//The Amazon Resource Name (ARN) that identifies the cluster. The ARN contains the arn:aws:ecs namespace,
//followed by the Region of the cluster, the AWS account ID of the cluster owner, the cluster namespace,
//and then the cluster name. For example, arn:aws:ecs:region:012345678910:cluster/test.
Expand Down
Loading

0 comments on commit d202af2

Please sign in to comment.