Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[receiver/k8scluster] Remove included tags #6436

Merged
merged 11 commits into from
Jan 3, 2022
Merged
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

## 🛑 Breaking changes 🛑

- `awsecscontainermetricsreceiver`: remove tag from `container.image.name` (#6436)
- `k8sclusterreceiver`: remove tag from `container.image.name` (#6436)

## 🚀 New components 🚀

## 🧰 Bug fixes 🧰

## 💡 Enhancements 💡

- `k8sclusterreceiver` add `container.image.tag` attribute (#6436)

## v0.40.0

## 🛑 Breaking changes 🛑
Expand Down
54 changes: 54 additions & 0 deletions internal/common/docker/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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://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 (
"fmt"
"regexp"

"go.uber.org/zap"
)

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

// 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) (string, string, error) {
jpkrohling marked this conversation as resolved.
Show resolved Hide resolved
if image == "" {
return "", "", fmt.Errorf("empty image")
}

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

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

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

return repository, tag, nil
}

func LogParseError(err error, image string, logger *zap.Logger) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be inlined instead of having extra public APIs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an attempt to keep the code DRY as we have the need for this in multiple packages.
Are you referring to the API of the package or the project? If the later is the case, the new package is in an internal one.

logger.Debug(err.Error(),
zap.String("image", image),
)
}
147 changes: 147 additions & 0 deletions internal/common/docker/images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// 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://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
wantErr bool
}{
{
name: "empty string",
args: args{
image: "",
},
wantRepository: "",
wantTag: "",
wantErr: true,
},
{
name: "malformed image",
args: args{
image: "aaa:",
},
wantRepository: "",
wantTag: "",
wantErr: true,
},
{
name: "image with sha256 hash",
args: args{
image: "alpine:test@sha256:00000000000000",
},
wantRepository: "alpine",
wantTag: "test",
wantErr: false,
},
{
name: "shorthand only",
args: args{
image: "alpine",
},
wantRepository: "alpine",
wantTag: "latest",
wantErr: false,
},
{
name: "shorthand with tag",
args: args{
image: "alpine:v1.0.0",
},
wantRepository: "alpine",
wantTag: "v1.0.0",
wantErr: false,
},
{
name: "repository without registry and tag",
args: args{
image: "alpine/alpine",
},
wantRepository: "alpine/alpine",
wantTag: "latest",
wantErr: false,
},
{
name: "repository without registry",
args: args{
image: "alpine/alpine:2.0.0",
},
wantRepository: "alpine/alpine",
wantTag: "2.0.0",
wantErr: false,
},
{
name: "repository with registry and without tag",
args: args{
image: "example.com/alpine/alpine",
},
wantRepository: "example.com/alpine/alpine",
wantTag: "latest",
wantErr: false,
},
{
name: "repository with registry and tag",
args: args{
image: "example.com/alpine/alpine:1",
},
wantRepository: "example.com/alpine/alpine",
wantTag: "1",
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",
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",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repository, tag, err := ParseImageName(tt.args.image)
if (err != nil) != tt.wantErr {
t.Errorf("ParseImageName() error = %v, wantErr %v", err, tt.wantErr)
return
}
if repository != tt.wantRepository {
t.Errorf("ParseImageName() repository = %v, want %v", repository, tt.wantRepository)
}
if tag != tt.wantTag {
t.Errorf("ParseImageName() tag = %v, want %v", tag, tt.wantTag)
}
})
}
}
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.11+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.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.40.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.40.0
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/collector v0.40.0
go.opentelemetry.io/collector/model v0.40.0
Expand Down Expand Up @@ -47,3 +48,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()

imageName, imageTag, 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, imageName)
resource.Attributes().UpsertString(attributeContainerImageID, cm.ImageID)
resource.Attributes().UpsertString(conventions.AttributeContainerImageTag, getVersionFromIamge(cm.Image))
resource.Attributes().UpsertString(conventions.AttributeContainerImageTag, imageTag)
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