Add Amazon EKS Resource Detector (#465)
* Add EKS Resource Detector
* Remove getK8sCredHeader() from detectorUtils interface
* Change implementation for getContainerId() to be consistent with ecs resource detector
* Change code to use resource.Empty()
* Add comments to functions
* Update error handling with useful messages
* Delete unused variables in EKS resource detector tests
* Add whitespace around operators
* Add space around "-" operator
* Update comments for functions to be more descriptive
* Update CHANGELOG.md
* Fix spaces around operators.
* Change "GET" to use http.MethodGet
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for retrieving auth configmap
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for HTTP request
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for executing HTTP request
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for HTTP response
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for getClusterName
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for parsing JSON for clusterName
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Update error message for reading file
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
* Fix build errors
* Change JSON parsing to use map[string]interface{}
* Add check for HTTP response status code in error handling
* Refactor millisecondTimeOut variable name to timeoutMillis
* Update CHANGELOG.md
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/go.mod
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/go.sum
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/go.mod
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector_test.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector_test.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector_test.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/eks_resource_detector_test.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Fix build error and address changes to PR
* Change getContainerID() to use regex
* Update detectors/aws/eks/detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
* Update detectors/aws/eks/detector.go
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
parent
d1534b8459
commit
b02fae7b25
|
|
@ -27,6 +27,16 @@ updates:
|
|||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
-
|
||||
package-ecosystem: "gomod"
|
||||
directory: "/detectors/aws/eks"
|
||||
labels:
|
||||
- dependencies
|
||||
- go
|
||||
- "Skip Changelog"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
-
|
||||
package-ecosystem: "gomod"
|
||||
directory: "/detectors/aws"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Amazon EKS resource detector. (#465)
|
||||
|
||||
## [0.14.0] - 2020-11-20
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
// 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 eks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/semconv"
|
||||
)
|
||||
|
||||
const (
|
||||
k8sSvcURL = "https://kubernetes.default.svc"
|
||||
k8sTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
k8sCertPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
||||
authConfigmapPath = "/api/v1/namespaces/kube-system/configmaps/aws-auth"
|
||||
cwConfigmapPath = "/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info"
|
||||
defaultCgroupPath = "/proc/self/cgroup"
|
||||
containerIDLength = 64
|
||||
timeoutMillis = 2000
|
||||
)
|
||||
|
||||
// detectorUtils is used for testing the ResourceDetector by abstracting functions that rely on external systems.
|
||||
type detectorUtils interface {
|
||||
fileExists(filename string) bool
|
||||
fetchString(httpMethod string, URL string) (string, error)
|
||||
getContainerID() (string, error)
|
||||
}
|
||||
|
||||
// This struct will implement the detectorUtils interface
|
||||
type eksDetectorUtils struct{}
|
||||
|
||||
// ResourceDetector for detecting resources running on Amazon EKS
|
||||
type ResourceDetector struct {
|
||||
utils detectorUtils
|
||||
}
|
||||
|
||||
// This struct will help unmarshal clustername from JSON response
|
||||
type data struct {
|
||||
ClusterName string `json:"cluster.name"`
|
||||
}
|
||||
|
||||
// Compile time assertion that ResourceDetector implements the resource.Detector interface.
|
||||
var _ resource.Detector = (*ResourceDetector)(nil)
|
||||
|
||||
// Compile time assertion that eksDetectorUtils implements the detectorUtils interface.
|
||||
var _ detectorUtils = (*eksDetectorUtils)(nil)
|
||||
|
||||
// Detect returns a Resource describing the Amazon EKS environment being run in.
|
||||
func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resource, error) {
|
||||
|
||||
isEks, err := isEKS(detector.utils)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return empty resource object if not running in EKS
|
||||
if !isEks {
|
||||
return resource.Empty(), nil
|
||||
}
|
||||
|
||||
// Create variable to hold resource attributes
|
||||
labels := []label.KeyValue{}
|
||||
|
||||
// Get clusterName and append to labels
|
||||
clusterName, err := getClusterName(detector.utils)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if clusterName != "" {
|
||||
labels = append(labels, semconv.K8SClusterNameKey.String(clusterName))
|
||||
}
|
||||
|
||||
// Get containerID and append to labels
|
||||
containerID, err := detector.utils.getContainerID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if containerID != "" {
|
||||
labels = append(labels, semconv.ContainerIDKey.String(containerID))
|
||||
}
|
||||
|
||||
// Return new resource object with clusterName and containerID as attributes
|
||||
return resource.NewWithAttributes(labels...), nil
|
||||
|
||||
}
|
||||
|
||||
// isEKS checks if the current environment is running in EKS.
|
||||
func isEKS(utils detectorUtils) (bool, error) {
|
||||
if !isK8s(utils) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Make HTTP GET request
|
||||
awsAuth, err := utils.fetchString(http.MethodGet, k8sSvcURL+authConfigmapPath)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("isEks() error retrieving auth configmap: %w", err)
|
||||
}
|
||||
|
||||
return awsAuth != "", nil
|
||||
}
|
||||
|
||||
// isK8s checks if the current environment is running in a Kubernetes environment
|
||||
func isK8s(utils detectorUtils) bool {
|
||||
return utils.fileExists(k8sTokenPath) && utils.fileExists(k8sCertPath)
|
||||
}
|
||||
|
||||
// fileExists checks if a file with a given filename exists.
|
||||
func (eksUtils eksDetectorUtils) fileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
return err == nil && !info.IsDir()
|
||||
}
|
||||
|
||||
// fetchString executes an HTTP request with a given HTTP Method and URL string.
|
||||
func (eksUtils eksDetectorUtils) fetchString(httpMethod string, URL string) (string, error) {
|
||||
request, err := http.NewRequest(httpMethod, URL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create new HTTP request with method=%s, URL=%s: %w", httpMethod, URL, err)
|
||||
}
|
||||
|
||||
// Set HTTP request header with authentication credentials
|
||||
authHeader, err := getK8sCredHeader()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
request.Header.Set("Authorization", authHeader)
|
||||
|
||||
// Get certificate
|
||||
caCert, err := ioutil.ReadFile(k8sCertPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read file with path %s", k8sCertPath)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
|
||||
// Set HTTP request timeout and add certificate
|
||||
client := &http.Client{
|
||||
Timeout: timeoutMillis * time.Millisecond,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil || response.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("failed to execute HTTP request with method=%s, URL=%s, Status Code=%d: %w", httpMethod, URL, response.StatusCode, err)
|
||||
}
|
||||
|
||||
// Retrieve response body from HTTP request
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read response from HTTP request with method=%s, URL=%s: %w", httpMethod, URL, err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// getK8sCredHeader retrieves the kubernetes credential information.
|
||||
func getK8sCredHeader() (string, error) {
|
||||
content, err := ioutil.ReadFile(k8sTokenPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getK8sCredHeader() error: cannot read file with path %s", k8sTokenPath)
|
||||
}
|
||||
|
||||
return "Bearer " + string(content), nil
|
||||
}
|
||||
|
||||
// getClusterName retrieves the clusterName resource attribute
|
||||
func getClusterName(utils detectorUtils) (string, error) {
|
||||
resp, err := utils.fetchString("GET", k8sSvcURL+cwConfigmapPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getClusterName() error: %w", err)
|
||||
}
|
||||
|
||||
// parse JSON object returned from HTTP request
|
||||
var respmap map[string]json.RawMessage
|
||||
err = json.Unmarshal([]byte(resp), &respmap)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getClusterName() error: cannot parse JSON: %w", err)
|
||||
}
|
||||
var d data
|
||||
err = json.Unmarshal(respmap["data"], &d)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getClusterName() error: cannot parse JSON: %w", err)
|
||||
}
|
||||
|
||||
clusterName := d.ClusterName
|
||||
|
||||
return clusterName, nil
|
||||
}
|
||||
|
||||
// getContainerID returns the containerID if currently running within a container.
|
||||
func (eksUtils eksDetectorUtils) getContainerID() (string, error) {
|
||||
fileData, err := ioutil.ReadFile(defaultCgroupPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getContainerID() error: cannot read file with path %s: %w", defaultCgroupPath, err)
|
||||
}
|
||||
|
||||
r, err := regexp.Compile(`^.*/docker/(.+)$`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Retrieve containerID from file
|
||||
splitData := strings.Split(strings.TrimSpace(string(fileData)), "\n")
|
||||
for _, str := range splitData {
|
||||
if r.MatchString(str) {
|
||||
return str[len(str)-containerIDLength:], nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("getContainerID() error: cannot read containerID from file %s", defaultCgroupPath)
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
// 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 eks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/bmizerany/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
"go.opentelemetry.io/otel/semconv"
|
||||
)
|
||||
|
||||
type MockDetectorUtils struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Mock function for fileExists()
|
||||
func (detectorUtils *MockDetectorUtils) fileExists(filename string) bool {
|
||||
args := detectorUtils.Called(filename)
|
||||
return args.Bool(0)
|
||||
}
|
||||
|
||||
// Mock function for fetchString()
|
||||
func (detectorUtils *MockDetectorUtils) fetchString(httpMethod string, URL string) (string, error) {
|
||||
args := detectorUtils.Called(httpMethod, URL)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
// Mock function for getContainerID()
|
||||
func (detectorUtils *MockDetectorUtils) getContainerID() (string, error) {
|
||||
args := detectorUtils.Called()
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
// Tests EKS resource detector running in EKS environment
|
||||
func TestEks(t *testing.T) {
|
||||
|
||||
detectorUtils := new(MockDetectorUtils)
|
||||
|
||||
// Mock functions and set expectations
|
||||
detectorUtils.On("fileExists", k8sTokenPath).Return(true)
|
||||
detectorUtils.On("fileExists", k8sCertPath).Return(true)
|
||||
detectorUtils.On("fetchString", "GET", k8sSvcURL+authConfigmapPath).Return("not empty", nil)
|
||||
detectorUtils.On("fetchString", "GET", k8sSvcURL+cwConfigmapPath).Return(`{"data":{"cluster.name":"my-cluster"}}`, nil)
|
||||
detectorUtils.On("getContainerID").Return("0123456789A", nil)
|
||||
|
||||
// Expected resource object
|
||||
eksResourceLabels := []label.KeyValue{
|
||||
semconv.K8SClusterNameKey.String("my-cluster"),
|
||||
semconv.ContainerIDKey.String("0123456789A"),
|
||||
}
|
||||
expectedResource := resource.NewWithAttributes(eksResourceLabels...)
|
||||
|
||||
// Call EKS Resource detector to detect resources
|
||||
eksResourceDetector := ResourceDetector{detectorUtils}
|
||||
resourceObj, err := eksResourceDetector.Detect(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expectedResource, resourceObj, "Resource object returned is incorrect")
|
||||
detectorUtils.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// Tests EKS resource detector not running in EKS environment
|
||||
func TestNotEKS(t *testing.T) {
|
||||
|
||||
detectorUtils := new(MockDetectorUtils)
|
||||
|
||||
k8sTokenPath := "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
|
||||
// Mock functions and set expectations
|
||||
detectorUtils.On("fileExists", k8sTokenPath).Return(false)
|
||||
|
||||
detector := ResourceDetector{detectorUtils}
|
||||
r, err := detector.Detect(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, resource.Empty(), r, "Resource object should be empty")
|
||||
detectorUtils.AssertExpectations(t)
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
module go.opentelemetry.io/contrib/detectors/aws/eks
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/stretchr/testify v1.6.1
|
||||
go.opentelemetry.io/otel v0.14.0
|
||||
go.opentelemetry.io/otel/sdk v0.14.0
|
||||
)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
github.com/DataDog/sketches-go v0.0.1/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ=
|
||||
go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
|
||||
go.opentelemetry.io/otel/sdk v0.14.0 h1:Pqgd85y5XhyvHQlOxkKW+FD4DAX7AoeaNIDKC2VhfHQ=
|
||||
go.opentelemetry.io/otel/sdk v0.14.0/go.mod h1:kGO5pEMSNqSJppHAm8b73zztLxB5fgDQnD56/dl5xqE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
Loading…
Reference in New Issue