container.go (2693B)
1 // Copyright The OpenTelemetry Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package resource // import "go.opentelemetry.io/otel/sdk/resource" 16 17 import ( 18 "bufio" 19 "context" 20 "errors" 21 "io" 22 "os" 23 "regexp" 24 25 semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 26 ) 27 28 type containerIDProvider func() (string, error) 29 30 var ( 31 containerID containerIDProvider = getContainerIDFromCGroup 32 cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*-)?([0-9a-f]+)(?:\.|\s*$)`) 33 ) 34 35 type cgroupContainerIDDetector struct{} 36 37 const cgroupPath = "/proc/self/cgroup" 38 39 // Detect returns a *Resource that describes the id of the container. 40 // If no container id found, an empty resource will be returned. 41 func (cgroupContainerIDDetector) Detect(ctx context.Context) (*Resource, error) { 42 containerID, err := containerID() 43 if err != nil { 44 return nil, err 45 } 46 47 if containerID == "" { 48 return Empty(), nil 49 } 50 return NewWithAttributes(semconv.SchemaURL, semconv.ContainerID(containerID)), nil 51 } 52 53 var ( 54 defaultOSStat = os.Stat 55 osStat = defaultOSStat 56 57 defaultOSOpen = func(name string) (io.ReadCloser, error) { 58 return os.Open(name) 59 } 60 osOpen = defaultOSOpen 61 ) 62 63 // getContainerIDFromCGroup returns the id of the container from the cgroup file. 64 // If no container id found, an empty string will be returned. 65 func getContainerIDFromCGroup() (string, error) { 66 if _, err := osStat(cgroupPath); errors.Is(err, os.ErrNotExist) { 67 // File does not exist, skip 68 return "", nil 69 } 70 71 file, err := osOpen(cgroupPath) 72 if err != nil { 73 return "", err 74 } 75 defer file.Close() 76 77 return getContainerIDFromReader(file), nil 78 } 79 80 // getContainerIDFromReader returns the id of the container from reader. 81 func getContainerIDFromReader(reader io.Reader) string { 82 scanner := bufio.NewScanner(reader) 83 for scanner.Scan() { 84 line := scanner.Text() 85 86 if id := getContainerIDFromLine(line); id != "" { 87 return id 88 } 89 } 90 return "" 91 } 92 93 // getContainerIDFromLine returns the id of the container from one string line. 94 func getContainerIDFromLine(line string) string { 95 matches := cgroupContainerIDRe.FindStringSubmatch(line) 96 if len(matches) <= 1 { 97 return "" 98 } 99 return matches[1] 100 }