-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add container id support to Resource (#2418)
* Add container id support to Resource * Fix wrong test case name * Add WithContainer option * Update CHANGELOG * Fix comments * Update CHANGELOG * Use regex to find container id * Add tests for reading cgroup file * Update sdk/resource/container.go Co-authored-by: Chester Cheung <[email protected]> * Update format Co-authored-by: Chester Cheung <[email protected]> Co-authored-by: Anthony Mirabella <[email protected]>
- Loading branch information
1 parent
0d0a732
commit f4ec95d
Showing
7 changed files
with
359 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
"errors" | ||
"io" | ||
"os" | ||
"regexp" | ||
|
||
semconv "go.opentelemetry.io/otel/semconv/v1.7.0" | ||
) | ||
|
||
type containerIDProvider func() (string, error) | ||
|
||
var ( | ||
containerID containerIDProvider = getContainerIDFromCGroup | ||
cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*-)?([0-9a-f]+)(?:\.|\s*$)`) | ||
) | ||
|
||
type cgroupContainerIDDetector struct{} | ||
|
||
const cgroupPath = "/proc/self/cgroup" | ||
|
||
// Detect returns a *Resource that describes the id of the container. | ||
// If no container id found, an empty resource will be returned. | ||
func (cgroupContainerIDDetector) Detect(ctx context.Context) (*Resource, error) { | ||
containerID, err := containerID() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if containerID == "" { | ||
return Empty(), nil | ||
} | ||
return NewWithAttributes(semconv.SchemaURL, semconv.ContainerIDKey.String(containerID)), nil | ||
} | ||
|
||
var ( | ||
defaultOSStat = os.Stat | ||
osStat = defaultOSStat | ||
|
||
defaultOSOpen = func(name string) (io.ReadCloser, error) { | ||
return os.Open(name) | ||
} | ||
osOpen = defaultOSOpen | ||
) | ||
|
||
// getContainerIDFromCGroup returns the id of the container from the cgroup file. | ||
// If no container id found, an empty string will be returned. | ||
func getContainerIDFromCGroup() (string, error) { | ||
if _, err := osStat(cgroupPath); errors.Is(err, os.ErrNotExist) { | ||
// File does not exist, skip | ||
return "", nil | ||
} | ||
|
||
file, err := osOpen(cgroupPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer file.Close() | ||
|
||
return getContainerIDFromReader(file), nil | ||
} | ||
|
||
// getContainerIDFromReader returns the id of the container from reader. | ||
func getContainerIDFromReader(reader io.Reader) string { | ||
scanner := bufio.NewScanner(reader) | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
|
||
if id := getContainerIDFromLine(line); id != "" { | ||
return id | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
// getContainerIDFromLine returns the id of the container from one string line. | ||
func getContainerIDFromLine(line string) string { | ||
matches := cgroupContainerIDRe.FindStringSubmatch(line) | ||
if len(matches) <= 1 { | ||
return "" | ||
} | ||
return matches[1] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// 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 resource | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
"os" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func setDefaultContainerProviders() { | ||
setContainerProviders( | ||
getContainerIDFromCGroup, | ||
) | ||
} | ||
|
||
func setContainerProviders( | ||
idProvider containerIDProvider, | ||
) { | ||
containerID = idProvider | ||
} | ||
|
||
func TestGetContainerIDFromLine(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
line string | ||
expectedContainerID string | ||
}{ | ||
{ | ||
name: "with suffix", | ||
line: "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa", | ||
expectedContainerID: "ac679f8a8319c8cf7d38e1adf263bc08d23", | ||
}, | ||
{ | ||
name: "with prefix and suffix", | ||
line: "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff", | ||
expectedContainerID: "dc679f8a8319c8cf7d38e1adf263bc08d23", | ||
}, | ||
{ | ||
name: "no prefix and suffix", | ||
line: "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356", | ||
expectedContainerID: "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356", | ||
}, | ||
{ | ||
name: "with space", | ||
line: " 13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356 ", | ||
expectedContainerID: "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356", | ||
}, | ||
{ | ||
name: "invalid hex string", | ||
line: "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz", | ||
}, | ||
{ | ||
name: "no container id - 1", | ||
line: "pids: /", | ||
}, | ||
{ | ||
name: "no container id - 2", | ||
line: "pids: ", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
containerID := getContainerIDFromLine(tc.line) | ||
assert.Equal(t, tc.expectedContainerID, containerID) | ||
}) | ||
} | ||
} | ||
|
||
func TestGetContainerIDFromReader(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
reader io.Reader | ||
expectedContainerID string | ||
}{ | ||
{ | ||
name: "multiple lines", | ||
reader: strings.NewReader(`// | ||
1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23 | ||
1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d24 | ||
`), | ||
expectedContainerID: "dc579f8a8319c8cf7d38e1adf263bc08d23", | ||
}, | ||
{ | ||
name: "no container id", | ||
reader: strings.NewReader(`// | ||
1:name=systemd:/podruntime/docker | ||
`), | ||
expectedContainerID: "", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
containerID := getContainerIDFromReader(tc.reader) | ||
assert.Equal(t, tc.expectedContainerID, containerID) | ||
}) | ||
} | ||
} | ||
|
||
func TestGetContainerIDFromCGroup(t *testing.T) { | ||
t.Cleanup(func() { | ||
osStat = defaultOSStat | ||
osOpen = defaultOSOpen | ||
}) | ||
|
||
testCases := []struct { | ||
name string | ||
cgroupFileNotExist bool | ||
openFileError error | ||
content string | ||
expectedContainerID string | ||
expectedError bool | ||
}{ | ||
{ | ||
name: "the cgroup file does not exist", | ||
cgroupFileNotExist: true, | ||
}, | ||
{ | ||
name: "error when opening cgroup file", | ||
openFileError: errors.New("test"), | ||
expectedError: true, | ||
}, | ||
{ | ||
name: "cgroup file", | ||
content: "1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23", | ||
expectedContainerID: "dc579f8a8319c8cf7d38e1adf263bc08d23", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
osStat = func(name string) (os.FileInfo, error) { | ||
if tc.cgroupFileNotExist { | ||
return nil, os.ErrNotExist | ||
} | ||
return nil, nil | ||
} | ||
|
||
osOpen = func(name string) (io.ReadCloser, error) { | ||
if tc.openFileError != nil { | ||
return nil, tc.openFileError | ||
} | ||
return io.NopCloser(strings.NewReader(tc.content)), nil | ||
} | ||
|
||
containerID, err := getContainerIDFromCGroup() | ||
assert.Equal(t, tc.expectedError, err != nil) | ||
assert.Equal(t, tc.expectedContainerID, containerID) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters