diff --git a/cmd/otelcontribcol/go.mod b/cmd/otelcontribcol/go.mod index 0dc083d4c951..d99e79dff19d 100644 --- a/cmd/otelcontribcol/go.mod +++ b/cmd/otelcontribcol/go.mod @@ -1381,3 +1381,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/rabbi replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/githubreceiver => ../../receiver/githubreceiver replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/grpcutil => ../../internal/grpcutil + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei => ../../internal/huawei diff --git a/internal/huawei/Makefile b/internal/huawei/Makefile new file mode 100644 index 000000000000..bdd863a203be --- /dev/null +++ b/internal/huawei/Makefile @@ -0,0 +1 @@ +include ../../../Makefile.Common diff --git a/receiver/huaweicloudcesreceiver/internal/backoff.go b/internal/huawei/backoff.go similarity index 77% rename from receiver/huaweicloudcesreceiver/internal/backoff.go rename to internal/huawei/backoff.go index 7ad525bfa62c..39f5fc6e81b3 100644 --- a/receiver/huaweicloudcesreceiver/internal/backoff.go +++ b/internal/huawei/backoff.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudcesreceiver/internal" +package huawei // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" import ( "context" @@ -9,9 +9,22 @@ import ( "time" "github.com/cenkalti/backoff/v4" + "go.opentelemetry.io/collector/config/configretry" "go.uber.org/zap" ) +func NewExponentialBackOff(backOffConfig *configretry.BackOffConfig) *backoff.ExponentialBackOff { + return &backoff.ExponentialBackOff{ + InitialInterval: backOffConfig.InitialInterval, + RandomizationFactor: backOffConfig.RandomizationFactor, + Multiplier: backOffConfig.Multiplier, + MaxInterval: backOffConfig.MaxInterval, + MaxElapsedTime: backOffConfig.MaxElapsedTime, + Stop: backoff.Stop, + Clock: backoff.SystemClock, + } +} + // Generic function to make an API call with exponential backoff and context cancellation handling. func MakeAPICallWithRetry[T any]( ctx context.Context, diff --git a/receiver/huaweicloudcesreceiver/internal/backoff_test.go b/internal/huawei/backoff_test.go similarity index 99% rename from receiver/huaweicloudcesreceiver/internal/backoff_test.go rename to internal/huawei/backoff_test.go index 402a1f23ace8..f8448223e046 100644 --- a/receiver/huaweicloudcesreceiver/internal/backoff_test.go +++ b/internal/huawei/backoff_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal +package huawei import ( "context" diff --git a/internal/huawei/client_config.go b/internal/huawei/client_config.go new file mode 100644 index 000000000000..cedc2c84a101 --- /dev/null +++ b/internal/huawei/client_config.go @@ -0,0 +1,57 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huawei // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" + +import ( + "net/url" + "strconv" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config" + "go.opentelemetry.io/collector/config/configopaque" +) + +type HuaweiSessionConfig struct { + AccessKey configopaque.String `mapstructure:"access_key"` + + SecretKey configopaque.String `mapstructure:"secret_key"` + // Number of seconds before timing out a request. + NoVerifySSL bool `mapstructure:"no_verify_ssl"` + // Upload segments to AWS X-Ray through a proxy. + ProxyAddress string `mapstructure:"proxy_address"` + ProxyUser string `mapstructure:"proxy_user"` + ProxyPassword string `mapstructure:"proxy_password"` +} + +func CreateHTTPConfig(cfg HuaweiSessionConfig) (*config.HttpConfig, error) { + if cfg.ProxyAddress == "" { + return config.DefaultHttpConfig().WithIgnoreSSLVerification(cfg.NoVerifySSL), nil + } + proxy, err := configureHTTPProxy(cfg) + if err != nil { + return nil, err + } + return config.DefaultHttpConfig().WithProxy(proxy), nil +} + +func configureHTTPProxy(cfg HuaweiSessionConfig) (*config.Proxy, error) { + proxyURL, err := url.Parse(cfg.ProxyAddress) + if err != nil { + return nil, err + } + + proxy := config.NewProxy(). + WithSchema(proxyURL.Scheme). + WithHost(proxyURL.Hostname()) + if len(proxyURL.Port()) > 0 { + if i, err := strconv.Atoi(proxyURL.Port()); err == nil { + proxy = proxy.WithPort(i) + } + } + + // Configure the username and password if the proxy requires authentication + if len(cfg.ProxyUser) > 0 { + proxy = proxy.WithUsername(cfg.ProxyUser).WithPassword(cfg.ProxyPassword) + } + return proxy, nil +} diff --git a/internal/huawei/client_config_test.go b/internal/huawei/client_config_test.go new file mode 100644 index 000000000000..5da969a28c47 --- /dev/null +++ b/internal/huawei/client_config_test.go @@ -0,0 +1,30 @@ +package huawei + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCreateHTTPConfigNoVerifySSL(t *testing.T) { + cfg, err := CreateHTTPConfig(HuaweiSessionConfig{NoVerifySSL: true}) + require.NoError(t, err) + assert.True(t, cfg.IgnoreSSLVerification) +} + +func TestCreateHTTPConfigWithProxy(t *testing.T) { + cfg, err := CreateHTTPConfig(HuaweiSessionConfig{ + ProxyAddress: "https://127.0.0.1:8888", + ProxyUser: "admin", + ProxyPassword: "pass", + AccessKey: "123", + SecretKey: "secret", + }) + require.NoError(t, err) + assert.Equal(t, "https", cfg.HttpProxy.Schema) + assert.Equal(t, "127.0.0.1", cfg.HttpProxy.Host) + assert.Equal(t, 8888, cfg.HttpProxy.Port) + assert.False(t, cfg.IgnoreSSLVerification) + +} diff --git a/internal/huawei/go.mod b/internal/huawei/go.mod new file mode 100644 index 000000000000..36b43a65c5d8 --- /dev/null +++ b/internal/huawei/go.mod @@ -0,0 +1,19 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei + +go 1.22.3 + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/config/configopaque v1.15.0 + go.opentelemetry.io/collector/config/configretry v1.15.0 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/internal/huawei/go.sum b/internal/huawei/go.sum new file mode 100644 index 000000000000..761089fc8bde --- /dev/null +++ b/internal/huawei/go.sum @@ -0,0 +1,166 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 h1:odui9Ua0u1hPfpkutN/tGvtt0ms55I+gQqIdU8K1rlo= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= +go.opentelemetry.io/collector/config/configopaque v1.15.0 h1:J1rmPR1WGro7BNCgni3o+VDoyB7ZqH2/SG1YK+6ujCw= +go.opentelemetry.io/collector/config/configopaque v1.15.0/go.mod h1:6zlLIyOoRpJJ+0bEKrlZOZon3rOp5Jrz9fMdR4twOS4= +go.opentelemetry.io/collector/config/configretry v1.15.0 h1:4ZUPrWWh4wiwdlGnss2lZDhvf1xkt8uwHEqmuqovMEs= +go.opentelemetry.io/collector/config/configretry v1.15.0/go.mod h1:KvQF5cfphq1rQm1dKR4eLDNQYw6iI2fY72NMZVa+0N0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/receiver/huaweicloudcesreceiver/config.go b/receiver/huaweicloudcesreceiver/config.go index 076ec7a9c024..471030b2e2fb 100644 --- a/receiver/huaweicloudcesreceiver/config.go +++ b/receiver/huaweicloudcesreceiver/config.go @@ -9,9 +9,9 @@ import ( "slices" "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/model" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configretry" "go.opentelemetry.io/collector/receiver/scraperhelper" "go.uber.org/multierr" @@ -41,7 +41,7 @@ type Config struct { // If Indices is empty, no indices will be scraped. Indices []string `mapstructure:"indices"` // Set of attributes used to configure huawei's CES SDK connection - HuaweiSessionConfig `mapstructure:",squash"` + huawei.HuaweiSessionConfig `mapstructure:",squash"` // ProjectID is a string to reference project where metrics should be associated with. // If ProjectID is not filled in, the SDK will automatically call the IAM service to query the project id corresponding to the region. @@ -67,18 +67,6 @@ type Config struct { BackOffConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"` } -type HuaweiSessionConfig struct { - AccessKey configopaque.String `mapstructure:"access_key"` - - SecretKey configopaque.String `mapstructure:"secret_key"` - // Number of seconds before timing out a request. - NoVerifySSL bool `mapstructure:"no_verify_ssl"` - // Upload segments to AWS X-Ray through a proxy. - ProxyAddress string `mapstructure:"proxy_address"` - ProxyUser string `mapstructure:"proxy_user"` - ProxyPassword string `mapstructure:"proxy_password"` -} - var _ component.Config = (*Config)(nil) // These valid periods are defined by CES API constraints: https://support.huaweicloud.com/intl/en-us/api-ces/ces_03_0034.html#section3 diff --git a/receiver/huaweicloudcesreceiver/config_test.go b/receiver/huaweicloudcesreceiver/config_test.go index 7edf5ed2a7c2..4d99321fbe75 100644 --- a/receiver/huaweicloudcesreceiver/config_test.go +++ b/receiver/huaweicloudcesreceiver/config_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/receiver/scraperhelper" ) @@ -80,7 +81,7 @@ func TestConfigValidate(t *testing.T) { { name: "Proxy user without proxy address", config: Config{ - HuaweiSessionConfig: HuaweiSessionConfig{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ ProxyUser: "user", }, ControllerConfig: scraperhelper.ControllerConfig{ @@ -96,7 +97,7 @@ func TestConfigValidate(t *testing.T) { { name: "Proxy password without proxy address", config: Config{ - HuaweiSessionConfig: HuaweiSessionConfig{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ ProxyPassword: "password", }, ControllerConfig: scraperhelper.ControllerConfig{ @@ -112,7 +113,7 @@ func TestConfigValidate(t *testing.T) { { name: "Proxy address with proxy user and password", config: Config{ - HuaweiSessionConfig: HuaweiSessionConfig{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ ProxyAddress: "http://proxy.example.com", ProxyUser: "user", ProxyPassword: "password", diff --git a/receiver/huaweicloudcesreceiver/factory.go b/receiver/huaweicloudcesreceiver/factory.go index 5f5426680e5d..7ebe0258122f 100644 --- a/receiver/huaweicloudcesreceiver/factory.go +++ b/receiver/huaweicloudcesreceiver/factory.go @@ -13,6 +13,7 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/receiver" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudcesreceiver/internal/metadata" ) @@ -33,7 +34,7 @@ func createDefaultConfig() component.Config { RandomizationFactor: backoff.DefaultRandomizationFactor, Multiplier: backoff.DefaultMultiplier, }, - HuaweiSessionConfig: HuaweiSessionConfig{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ NoVerifySSL: false, }, } diff --git a/receiver/huaweicloudcesreceiver/go.mod b/receiver/huaweicloudcesreceiver/go.mod index c75867cf7c88..6308c2188f96 100644 --- a/receiver/huaweicloudcesreceiver/go.mod +++ b/receiver/huaweicloudcesreceiver/go.mod @@ -1,18 +1,16 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudcesreceiver -go 1.22.0 - -toolchain go1.22.3 +go 1.22.3 require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei v0.108.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.108.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.109.0 go.opentelemetry.io/collector/config/confighttp v0.109.0 - go.opentelemetry.io/collector/config/configopaque v1.15.0 go.opentelemetry.io/collector/config/configretry v1.15.0 go.opentelemetry.io/collector/confmap v1.15.0 go.opentelemetry.io/collector/consumer v0.109.0 @@ -61,6 +59,7 @@ require ( go.opentelemetry.io/collector/client v1.15.0 // indirect go.opentelemetry.io/collector/config/configauth v0.109.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.15.0 // indirect + go.opentelemetry.io/collector/config/configopaque v1.15.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.109.0 // indirect go.opentelemetry.io/collector/config/configtls v1.15.0 // indirect go.opentelemetry.io/collector/config/internal v0.109.0 // indirect @@ -71,12 +70,12 @@ require ( go.opentelemetry.io/collector/pdata/pprofile v0.109.0 // indirect go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.51.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect go.opentelemetry.io/otel/sdk v1.29.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sys v0.25.0 // indirect @@ -88,6 +87,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei => ./../../internal/huawei + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest diff --git a/receiver/huaweicloudcesreceiver/go.sum b/receiver/huaweicloudcesreceiver/go.sum index cfc00296c454..ae4a2a11a1eb 100644 --- a/receiver/huaweicloudcesreceiver/go.sum +++ b/receiver/huaweicloudcesreceiver/go.sum @@ -173,18 +173,18 @@ go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0 h1:KKzdIixE/XJW go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0/go.mod h1:FKU+RFkSLWWB3tUUB6vifapZdFp1FoqVYVQ22jpHc8w= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw= go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/receiver/huaweicloudcesreceiver/receiver.go b/receiver/huaweicloudcesreceiver/receiver.go index dc4ba5683d3f..be086dc66199 100644 --- a/receiver/huaweicloudcesreceiver/receiver.go +++ b/receiver/huaweicloudcesreceiver/receiver.go @@ -7,23 +7,19 @@ import ( "context" "errors" "fmt" - "net/url" - "strconv" "strings" "time" - "github.com/cenkalti/backoff/v4" "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" - "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config" ces "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1" "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/model" "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/region" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configretry" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/receiver" "go.uber.org/zap" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudcesreceiver/internal" ) @@ -114,7 +110,7 @@ func (rcvr *cesReceiver) createClient() (*ces.CesClient, error) { return nil, err } - httpConfig, err := createHTTPConfig(rcvr.config.HuaweiSessionConfig) + httpConfig, err := huawei.CreateHTTPConfig(rcvr.config.HuaweiSessionConfig) if err != nil { return nil, err } @@ -155,7 +151,7 @@ func (rcvr *cesReceiver) pollMetricsAndConsume(ctx context.Context) error { } func (rcvr *cesReceiver) listMetricDefinitions(ctx context.Context) ([]model.MetricInfoList, error) { - response, err := internal.MakeAPICallWithRetry( + response, err := huawei.MakeAPICallWithRetry( ctx, rcvr.shutdownChan, rcvr.logger, @@ -163,7 +159,7 @@ func (rcvr *cesReceiver) listMetricDefinitions(ctx context.Context) ([]model.Met return rcvr.client.ListMetrics(&model.ListMetricsRequest{}) }, func(err error) bool { return strings.Contains(err.Error(), requestThrottledErrMsg) }, - newExponentialBackOff(&rcvr.config.BackOffConfig), + huawei.NewExponentialBackOff(&rcvr.config.BackOffConfig), ) if err != nil { return []model.MetricInfoList{}, err @@ -235,7 +231,7 @@ func (rcvr *cesReceiver) listDataPoints(ctx context.Context, metricDefinitions [ } func (rcvr *cesReceiver) listDataPointsForMetric(ctx context.Context, from, to time.Time, infoList model.MetricInfoList) (*model.ShowMetricDataResponse, error) { - return internal.MakeAPICallWithRetry( + return huawei.MakeAPICallWithRetry( ctx, rcvr.shutdownChan, rcvr.logger, @@ -254,55 +250,10 @@ func (rcvr *cesReceiver) listDataPointsForMetric(ctx context.Context, from, to t }) }, func(err error) bool { return strings.Contains(err.Error(), requestThrottledErrMsg) }, - newExponentialBackOff(&rcvr.config.BackOffConfig), + huawei.NewExponentialBackOff(&rcvr.config.BackOffConfig), ) } -func newExponentialBackOff(backOffConfig *configretry.BackOffConfig) *backoff.ExponentialBackOff { - return &backoff.ExponentialBackOff{ - InitialInterval: backOffConfig.InitialInterval, - RandomizationFactor: backOffConfig.RandomizationFactor, - Multiplier: backOffConfig.Multiplier, - MaxInterval: backOffConfig.MaxInterval, - MaxElapsedTime: backOffConfig.MaxElapsedTime, - Stop: backoff.Stop, - Clock: backoff.SystemClock, - } -} - -func createHTTPConfig(cfg HuaweiSessionConfig) (*config.HttpConfig, error) { - if cfg.ProxyAddress == "" { - return config.DefaultHttpConfig().WithIgnoreSSLVerification(cfg.NoVerifySSL), nil - } - proxy, err := configureHTTPProxy(cfg) - if err != nil { - return nil, err - } - return config.DefaultHttpConfig().WithProxy(proxy), nil -} - -func configureHTTPProxy(cfg HuaweiSessionConfig) (*config.Proxy, error) { - proxyURL, err := url.Parse(cfg.ProxyAddress) - if err != nil { - return nil, err - } - - proxy := config.NewProxy(). - WithSchema(proxyURL.Scheme). - WithHost(proxyURL.Hostname()) - if len(proxyURL.Port()) > 0 { - if i, err := strconv.Atoi(proxyURL.Port()); err == nil { - proxy = proxy.WithPort(i) - } - } - - // Configure the username and password if the proxy requires authentication - if len(cfg.ProxyUser) > 0 { - proxy = proxy.WithUsername(cfg.ProxyUser).WithPassword(cfg.ProxyPassword) - } - return proxy, nil -} - func (rcvr *cesReceiver) Shutdown(_ context.Context) error { if rcvr.cancel != nil { rcvr.cancel() diff --git a/receiver/huaweicloudcesreceiver/receiver_test.go b/receiver/huaweicloudcesreceiver/receiver_test.go index 672eea39c8b8..267f0f699a6d 100644 --- a/receiver/huaweicloudcesreceiver/receiver_test.go +++ b/receiver/huaweicloudcesreceiver/receiver_test.go @@ -274,23 +274,3 @@ func TestStartReadingMetrics(t *testing.T) { }) } } -func TestCreateHTTPConfigNoVerifySSL(t *testing.T) { - cfg, err := createHTTPConfig(HuaweiSessionConfig{NoVerifySSL: true}) - require.NoError(t, err) - assert.True(t, cfg.IgnoreSSLVerification) -} - -func TestCreateHTTPConfigWithProxy(t *testing.T) { - cfg, err := createHTTPConfig(HuaweiSessionConfig{ - ProxyAddress: "https://127.0.0.1:8888", - ProxyUser: "admin", - ProxyPassword: "pass", - AccessKey: "123", - SecretKey: "secret", - }) - require.NoError(t, err) - assert.Equal(t, "https", cfg.HttpProxy.Schema) - assert.Equal(t, "127.0.0.1", cfg.HttpProxy.Host) - assert.Equal(t, 8888, cfg.HttpProxy.Port) - assert.False(t, cfg.IgnoreSSLVerification) -} diff --git a/receiver/huaweicloudlogsreceiver/Makefile b/receiver/huaweicloudlogsreceiver/Makefile new file mode 100644 index 000000000000..c1496226e590 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common \ No newline at end of file diff --git a/receiver/huaweicloudlogsreceiver/README.md b/receiver/huaweicloudlogsreceiver/README.md new file mode 100644 index 000000000000..c3e2d6abe332 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/README.md @@ -0,0 +1,205 @@ +# Huawei Cloud CES Receiver + + +| Status | | +| ------------- |-----------| +| Stability | [development]: logs | +| Distributions | [] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Fhuaweicloudlogs%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fhuaweicloudlogs) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Fhuaweicloudlogs%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fhuaweicloudlogs) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@heitorganzeli](https://www.github.com/heitorganzeli), [@narcis96](https://www.github.com/narcis96) | + +[development]: https://github.com/open-telemetry/opentelemetry-collector#development + + +This receiver contains the implementation of the Huawei Cloud [Log Tank Service](https://www.huaweicloud.com/intl/en-us/product/lts.html) (LTS) receiver for the OpenTelemetry Collector. The receiver collects logs from Huawei Cloud's LTS service and sends them to the OpenTelemetry Collector for processing and exporting. + +## Configuration + +The following settings are required: + +- `project_id`: The ID of the project in Huawei Cloud. This is used to identify which project's logs are to be collected. See [Obtaining a Project ID](https://support.huaweicloud.com/intl/en-us/devg-apisign/api-sign-provide-proid.html). + +- `region_id`: The ID of the Huawei Cloud region from which logs are collected. For example, `eu-west-101`. The full list of the available regions can be found [here](https://pkg.go.dev/github.com/huaweicloud/huaweicloud-sdk-go-v3@v0.1.110/services/lts/v2/region). + +- `log_group_id`: A string indicating the ID of the log group. + +- `log_stream_id`: A string indicating the ID of the log stream. See [Obtaining Log Group and Log Stream IDs](https://support.huaweicloud.com/intl/en-us/api-lts/lts_api_0006.html#section1). + +- `no_verify_ssl`: A boolean flag indicating whether SSL verification should be disabled. Set to True to disable SSL verification. + +- `access_key`: The access key needed for LTS authentification. Check `Huawei Cloud SDK Authentication Setup` section for more details. + +- `secret_key`: The secret key needed for LTS authentification. Check `Huawei Cloud SDK Authentication Setup` section for more details. + +The following settings are optional: + +- `initial_delay`: The delay before the first collection of logs begins. This is a duration field, such as 5s for 5 seconds. + +- `collection_interval` (default = `60s`): This is the interval at which this receiver collects logs. This value must be a string readable by Golang's [time.ParseDuration](https://pkg.go.dev/time#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. We recommend a polling interval of at least one minute. + +- `retry_on_failure`: The following configurations can be used to control the retry policy of the LTS client. The default values are suitable for most deployment scenarios. + - `enabled` (default true) + - `initial_interval` (default 100ms) + - `max_interval` (default 1s) + - `max_elapsed_time` (default 15s) + - `randomization_factor` (default 0.5) + - `multiplier` (default 1.5) + +### Example Configuration + +```yaml +receivers: + huaweicloudlogsreceiver: + collection_interval: 3h + initial_delay: 5s + access_key: ${env:HUAWEICLOUD_SDK_AK} + secret_key: ${env:HUAWEICLOUD_SDK_SK} + project_id: project_1 + region_id: eu-west-101 + log_group_id: test-group-id + log_stream_id: test-stream-id + no_verify_ssl: True +``` + +The full list of settings exposed for this receiver are documented [here](./config.go). + +### Huawei Cloud SDK Authentication Setup + + +To ensure secure authentication, the Access Key (AK) and Secret Key (SK) used by the Huawei Cloud SDK must be stored in environment variables. See [Obtaining an AK/SK](https://support.huaweicloud.com/intl/en-us/devg-apisign/api-sign-provide-aksk.html). + +Before running the application, you need to set the environment variables `HUAWEICLOUD_SDK_AK` and `HUAWEICLOUD_SDK_SK` in your local environment. Here’s how you can do it: + +1. Open your terminal. +2. Set the environment variables by executing the following commands: + + ```sh + export HUAWEICLOUD_SDK_AK=your-access-key + export HUAWEICLOUD_SDK_SK=your-secret-key + ``` + +3. Verify that the variables are set correctly: + + ```sh + echo $HUAWEICLOUD_SDK_AK + echo $HUAWEICLOUD_SDK_SK + ``` + +## Error handling +If you encounter any LTS errors, please refer to the [Huawei Cloud Error Codes](https://support.huaweicloud.com/intl/en-us/ae-ad-1-api-lts/lts_api_0021.html). + +## Converting LTS Log Representation to OpenTelemetry Log Representation + +| Source Field | Target Field | Description | +|---------------------------|----------------------------------------------------|---------------------------------------------------------------------------------------------------| +| **Log Content (timestamp)** | `logRecord.observedTimestamp` | The timestamp extracted from the log content. It follows the layout `"2006-01-02/15:04:05"`. Converted to Unix time in nanoseconds. | +| **Log Content (body)** | `logRecord.body` | The main content of the log. Stored as a string in the OTLP log body. | +| **Labels (key)** | `logRecord.attributes` | Each label from the LTS log is converted to an attribute in the OTLP log, where the label's key is used as the attribute's key. | +| **Labels (value)** | `logRecord.attributes` | The corresponding value of each LTS log label key is stored as the attribute's value. | +| **Line Number** | `logRecord.attributes.lineNum` | The line number from the LTS log, if available. Stored as an attribute with the key `lineNum`. | +| **Project ID** | `resource.attributes.project.id` | The project ID used in the configuration file of the receiver.. | +| **Region ID** | `resource.attributes.region.id` | The region id used in the configuration file of the receiver. | +| **Group ID** | `resource.attributes.group.id` | The log group id used in the configuration file of the receiver. | +| **Stream ID** | `resource.attributes.stream.id` | The log stream id used in the configuration file of the receiver. | +| *N/A* | `resource.attributes.cloud.provider` | Set to `"huawei_cloud"` as the cloud provider. | +| *N/A* | `scopeLogs.scope.name` | Set to `"huawei_cloud_lts"` as the scope name. | +| *N/A* | `scopeLogs.scope.version` | Set to `"v2"` as the scope version. | + +This mapping ensures that logs from Huawei Cloud's LTS can be seamlessly integrated into systems using the OpenTelemetry Protocol for observability. + + +### Notes + +- The `timestamp` field in the source is converted from milliseconds to nanoseconds in the target field. +- Some fields are added in the target format with constant values to provide additional context and metadata. + +### Example: + +```json +[ + { + "content": "2020-07-25/14:40:00 this logis Error NO 2", + "line_num": "123", + "labels": { + "hostIP": "192.168.0.156", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f60", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace":"CONFIG_FILE", + "category": "LTS", + } + }, + { + "content": "2020-07-25/14:50:00 this logis Error NO 3", + "line_num": "456", + "labels": { + "hostIP": "192.168.0.156", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f60", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace":"CONFIG_FILE", + "category": "LTS", + } + }, +] +``` + +converts to + +```json +{ + "resourceLogs": [ + { + "resource": { + "attributes": { + "cloud.provider": "huawei_cloud", + "project.id": "project1", + "region.id": "region1", + "group.id": "group1", + "stream.id":"stream1" + }, + "scopeLogs": [ + { + "scope": { + "name": "huawei_cloud_lts", + "version": "v2" + }, + "logRecords": [ + { + "observedTimeUnixNano": "1595688000000000000", + "body": { + "stringValue": "2020-07-25/14:40:00 this log is Error NO 2" + }, + "attributes": { + "hostIP": "192.168.0.156", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f60", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace":"CONFIG_FILE", + "category": "LTS", + "lineNum": "123" + }, + }, + { + "observedTimeUnixNano": "1595688600000000000", + "body": { + "stringValue": "2020-07-25/14:50:00 this log is Error NO 3" + }, + "attributes": { + "hostIP": "192.168.0.156", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f60", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace":"CONFIG_FILE", + "category": "LTS", + "lineNum": "456" + }, + } + ] + } + ] + } + } + ] +} +``` \ No newline at end of file diff --git a/receiver/huaweicloudlogsreceiver/config.go b/receiver/huaweicloudlogsreceiver/config.go new file mode 100644 index 000000000000..e26c6e47686a --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/config.go @@ -0,0 +1,74 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver" + +import ( + "errors" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/receiver/scraperhelper" + "go.uber.org/multierr" +) + +var ( + // Predefined error responses for configuration validation failures + errMissingProjectID = errors.New(`"project_id" is not specified in config`) + errMissingRegionID = errors.New(`"region_id" is not specified in config`) + errMissingGroupID = errors.New(`"log_group_id" is not specified in config`) + errMissingStreamID = errors.New(`"log_stream_id" is not specified in config`) + + errInvalidProxy = errors.New(`"proxy_address" must be specified if "proxy_user" or "proxy_password" is set"`) +) + +// Config represent a configuration for the CloudWatch logs exporter. +type Config struct { + scraperhelper.ControllerConfig `mapstructure:",squash"` + confighttp.ClientConfig `mapstructure:",squash"` + // Set of attributes used to configure huawei's CES SDK connection + huawei.HuaweiSessionConfig `mapstructure:",squash"` + + // ProjectID is a string to reference project where logs should be associated with. + // If ProjectID is not filled in, the SDK will automatically call the IAM service to query the project id corresponding to the region. + ProjectID string `mapstructure:"project_id"` + + // RegionID is the ID of the LTS region. + RegionID string `mapstructure:"region_id"` + + // GroupID is the ID of the LTS log group. + GroupID string `mapstructure:"log_group_id"` + + // Stream is the ID of the LTS log stream. + StreamID string `mapstructure:"log_stream_id"` + + BackOffConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"` +} + +var _ component.Config = (*Config)(nil) + +// Validate config +func (config *Config) Validate() error { + var err error + if config.ProjectID == "" { + err = multierr.Append(err, errMissingProjectID) + } + if config.RegionID == "" { + err = multierr.Append(err, errMissingRegionID) + } + if config.GroupID == "" { + err = multierr.Append(err, errMissingGroupID) + } + if config.StreamID == "" { + err = multierr.Append(err, errMissingStreamID) + } + + // Validate that ProxyAddress is provided if ProxyUser or ProxyPassword is set + if (config.ProxyUser != "" || config.ProxyPassword != "") && config.ProxyAddress == "" { + err = multierr.Append(err, errInvalidProxy) + } + + return err +} diff --git a/receiver/huaweicloudlogsreceiver/config_test.go b/receiver/huaweicloudlogsreceiver/config_test.go new file mode 100644 index 000000000000..8ac3068ec833 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/config_test.go @@ -0,0 +1,116 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver + +import ( + "testing" + "time" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/receiver/scraperhelper" +) + +func TestConfigValidate(t *testing.T) { + tests := []struct { + name string + config Config + expectedError string + }{ + { + name: "Valid config", + config: Config{ + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: time.Hour, + }, + RegionID: "cn-north-1", + ProjectID: "my_project", + GroupID: "group-1", + StreamID: "stream-1", + }, + expectedError: "", + }, + { + name: "Missing region name", + config: Config{ + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: time.Hour, + }, + ProjectID: "my_project", + }, + expectedError: errMissingRegionID.Error(), + }, + { + name: "Missing project id", + config: Config{ + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: time.Hour, + }, + RegionID: "cn-north-1", + }, + expectedError: errMissingProjectID.Error(), + }, + { + name: "Proxy user without proxy address", + config: Config{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ + ProxyUser: "user", + }, + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: time.Hour, + }, + RegionID: "cn-north-1", + ProjectID: "my_project", + GroupID: "group-1", + StreamID: "stream-1", + }, + expectedError: errInvalidProxy.Error(), + }, + { + name: "Proxy password without proxy address", + config: Config{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ + ProxyPassword: "password", + }, + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: time.Hour, + }, + RegionID: "cn-north-1", + ProjectID: "my_project", + GroupID: "group-1", + StreamID: "stream-1", + }, + expectedError: errInvalidProxy.Error(), + }, + { + name: "Proxy address with proxy user and password", + config: Config{ + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ + ProxyAddress: "http://proxy.example.com", + ProxyUser: "user", + ProxyPassword: "password", + }, + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: time.Hour, + }, + RegionID: "cn-north-1", + ProjectID: "my_project", + GroupID: "group-1", + StreamID: "stream-1", + }, + expectedError: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.config.Validate() + if tt.expectedError == "" { + assert.NoError(t, err) + } else { + assert.ErrorContains(t, err, tt.expectedError) + } + }) + } +} diff --git a/receiver/huaweicloudlogsreceiver/factory.go b/receiver/huaweicloudlogsreceiver/factory.go new file mode 100644 index 000000000000..ec353162bde6 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/factory.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver" + +import ( + "context" + "time" + + "github.com/cenkalti/backoff/v4" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver/internal/metadata" +) + +func NewFactory() receiver.Factory { + return receiver.NewFactory( + metadata.Type, + createDefaultConfig, + receiver.WithLogs(createLogsReceiver, metadata.LogsStability)) +} + +func createDefaultConfig() component.Config { + return &Config{ + BackOffConfig: configretry.BackOffConfig{ + Enabled: true, + InitialInterval: 100 * time.Millisecond, + MaxInterval: time.Second, + MaxElapsedTime: 15 * time.Second, + RandomizationFactor: backoff.DefaultRandomizationFactor, + Multiplier: backoff.DefaultMultiplier, + }, + HuaweiSessionConfig: huawei.HuaweiSessionConfig{ + NoVerifySSL: false, + }, + } +} + +func createLogsReceiver( + _ context.Context, + params receiver.Settings, + cfg component.Config, + next consumer.Logs) (receiver.Logs, error) { + + cesCfg := cfg.(*Config) + + cesReceiver := newHuaweiCloudLogsReceiver(params, cesCfg, next) + + return cesReceiver, nil + +} diff --git a/receiver/huaweicloudlogsreceiver/factory_test.go b/receiver/huaweicloudlogsreceiver/factory_test.go new file mode 100644 index 000000000000..970a666768af --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/factory_test.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestNewFactory(t *testing.T) { + factory := NewFactory() + assert.NotNil(t, factory) + assert.Equal(t, component.MustNewType("huaweicloudlogsreceiver"), factory.Type()) +} + +func TestCreateDefaultConfig(t *testing.T) { + factory := NewFactory() + config := factory.CreateDefaultConfig() + assert.NotNil(t, config) + assert.NoError(t, componenttest.CheckConfigStruct(config)) +} + +func TestCreateLogsReceiver(t *testing.T) { + factory := NewFactory() + config := factory.CreateDefaultConfig() + + rConfig := config.(*Config) + rConfig.CollectionInterval = 60 * time.Second + rConfig.InitialDelay = time.Second + + nextConsumer := new(consumertest.LogsSink) + receiver, err := factory.CreateLogsReceiver(context.Background(), receivertest.NewNopSettings(), config, nextConsumer) + assert.NoError(t, err) + assert.NotNil(t, receiver) +} diff --git a/receiver/huaweicloudlogsreceiver/generated_component_test.go b/receiver/huaweicloudlogsreceiver/generated_component_test.go new file mode 100644 index 000000000000..c6ce35029500 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/generated_component_test.go @@ -0,0 +1,69 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package huaweicloudlogsreceiver + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/confmap/confmaptest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestComponentFactoryType(t *testing.T) { + require.Equal(t, "huaweicloudlogsreceiver", NewFactory().Type().String()) +} + +func TestComponentConfigStruct(t *testing.T) { + require.NoError(t, componenttest.CheckConfigStruct(NewFactory().CreateDefaultConfig())) +} + +func TestComponentLifecycle(t *testing.T) { + factory := NewFactory() + + tests := []struct { + name string + createFn func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) + }{ + + { + name: "logs", + createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateLogsReceiver(ctx, set, cfg, consumertest.NewNop()) + }, + }, + } + + cm, err := confmaptest.LoadConf("metadata.yaml") + require.NoError(t, err) + cfg := factory.CreateDefaultConfig() + sub, err := cm.Sub("tests::config") + require.NoError(t, err) + require.NoError(t, sub.Unmarshal(&cfg)) + + for _, test := range tests { + t.Run(test.name+"-shutdown", func(t *testing.T) { + c, err := test.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + t.Run(test.name+"-lifecycle", func(t *testing.T) { + firstRcvr, err := test.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + host := componenttest.NewNopHost() + require.NoError(t, err) + require.NoError(t, firstRcvr.Start(context.Background(), host)) + require.NoError(t, firstRcvr.Shutdown(context.Background())) + secondRcvr, err := test.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + require.NoError(t, secondRcvr.Start(context.Background(), host)) + require.NoError(t, secondRcvr.Shutdown(context.Background())) + }) + } +} diff --git a/receiver/huaweicloudlogsreceiver/generated_package_test.go b/receiver/huaweicloudlogsreceiver/generated_package_test.go new file mode 100644 index 000000000000..3b6ca7e307c7 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/generated_package_test.go @@ -0,0 +1,12 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package huaweicloudlogsreceiver + +import ( + "go.uber.org/goleak" + "testing" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/receiver/huaweicloudlogsreceiver/go.mod b/receiver/huaweicloudlogsreceiver/go.mod new file mode 100644 index 000000000000..7c6e63a93a38 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/go.mod @@ -0,0 +1,97 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver + +go 1.22.3 + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei v0.108.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.108.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/component v0.109.0 + go.opentelemetry.io/collector/config/confighttp v0.109.0 + go.opentelemetry.io/collector/config/configretry v1.15.0 + go.opentelemetry.io/collector/confmap v1.15.0 + go.opentelemetry.io/collector/consumer v0.109.0 + go.opentelemetry.io/collector/consumer/consumertest v0.109.0 + go.opentelemetry.io/collector/pdata v1.15.0 + go.opentelemetry.io/collector/receiver v0.109.0 + go.uber.org/goleak v1.3.0 + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/providers/confmap v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.108.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.57.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/tj/assert v0.0.3 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect + go.mongodb.org/mongo-driver v1.16.1 // indirect + go.opentelemetry.io/collector v0.109.0 // indirect + go.opentelemetry.io/collector/client v1.15.0 // indirect + go.opentelemetry.io/collector/config/configauth v0.109.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.15.0 // indirect + go.opentelemetry.io/collector/config/configopaque v1.15.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.109.0 // indirect + go.opentelemetry.io/collector/config/configtls v1.15.0 // indirect + go.opentelemetry.io/collector/config/internal v0.109.0 // indirect + go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0 // indirect + go.opentelemetry.io/collector/extension v0.109.0 // indirect + go.opentelemetry.io/collector/extension/auth v0.109.0 // indirect + go.opentelemetry.io/collector/featuregate v1.15.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.109.0 // indirect + go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.51.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/grpc v1.66.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei => ./../../internal/huawei + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden => ../../pkg/golden diff --git a/receiver/huaweicloudlogsreceiver/go.sum b/receiver/huaweicloudlogsreceiver/go.sum new file mode 100644 index 000000000000..502863280014 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/go.sum @@ -0,0 +1,316 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113 h1:odui9Ua0u1hPfpkutN/tGvtt0ms55I+gQqIdU8K1rlo= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.113/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY= +github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= +go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8= +go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= +go.opentelemetry.io/collector v0.109.0 h1:ULnMWuwcy4ix1oP5RFFRcmpEbaU5YabW6nWcLMQQRo0= +go.opentelemetry.io/collector v0.109.0/go.mod h1:gheyquSOc5E9Y+xsPmpA+PBrpPc+msVsIalY76/ZvnQ= +go.opentelemetry.io/collector/client v1.15.0 h1:SMUKTntljRmFvB8nCVf6KjbEQ/qm63wi+huDx+Bc/po= +go.opentelemetry.io/collector/client v1.15.0/go.mod h1:m0MdKbzRIVgyGu70qbJ6TwBmKtblk7cmPqspM45a5yY= +go.opentelemetry.io/collector/component v0.109.0 h1:AU6eubP1htO8Fvm86uWn66Kw0DMSFhgcRM2cZZTYfII= +go.opentelemetry.io/collector/component v0.109.0/go.mod h1:jRVFY86GY6JZ61SXvUN69n7CZoTjDTqWyNC+wJJvzOw= +go.opentelemetry.io/collector/config/configauth v0.109.0 h1:6I2g1dcXD7KCmzXWHaL09I6RSmiCER4b+UARYkmMw3U= +go.opentelemetry.io/collector/config/configauth v0.109.0/go.mod h1:i36T9K3m7pLSlqMFdy+npY7JxfxSg3wQc8bHNpykLLE= +go.opentelemetry.io/collector/config/configcompression v1.15.0 h1:HHzus/ahJW2dA6h4S4vs1MwlbOck27Ivk/L3o0V94UA= +go.opentelemetry.io/collector/config/configcompression v1.15.0/go.mod h1:pnxkFCLUZLKWzYJvfSwZnPrnm0twX14CYj2ADth5xiU= +go.opentelemetry.io/collector/config/confighttp v0.109.0 h1:6R2+zI1LqFarEnCL4k+1DCsFi+aVeUTbfFOQBk0JBh0= +go.opentelemetry.io/collector/config/confighttp v0.109.0/go.mod h1:fzvAO2nCnP9XRUiaCBh1AZ2whUf99iQTkEVFCyH+URk= +go.opentelemetry.io/collector/config/configopaque v1.15.0 h1:J1rmPR1WGro7BNCgni3o+VDoyB7ZqH2/SG1YK+6ujCw= +go.opentelemetry.io/collector/config/configopaque v1.15.0/go.mod h1:6zlLIyOoRpJJ+0bEKrlZOZon3rOp5Jrz9fMdR4twOS4= +go.opentelemetry.io/collector/config/configretry v1.15.0 h1:4ZUPrWWh4wiwdlGnss2lZDhvf1xkt8uwHEqmuqovMEs= +go.opentelemetry.io/collector/config/configretry v1.15.0/go.mod h1:KvQF5cfphq1rQm1dKR4eLDNQYw6iI2fY72NMZVa+0N0= +go.opentelemetry.io/collector/config/configtelemetry v0.109.0 h1:ItbYw3tgFMU+TqGcDVEOqJLKbbOpfQg3AHD8b22ygl8= +go.opentelemetry.io/collector/config/configtelemetry v0.109.0/go.mod h1:R0MBUxjSMVMIhljuDHWIygzzJWQyZHXXWIgQNxcFwhc= +go.opentelemetry.io/collector/config/configtls v1.15.0 h1:imUIYDu6lo7juxxgpJhoMQ+LJRxqQzKvjOcWTo4u0IY= +go.opentelemetry.io/collector/config/configtls v1.15.0/go.mod h1:T3pOF5UemLzmYgY7QpiZuDRrihJ8lyXB0cDe6j1F1Ek= +go.opentelemetry.io/collector/config/internal v0.109.0 h1:uAlmO9Gu4Ff5wXXWWn+7XRZKEBjwGE8YdkdJxOlodns= +go.opentelemetry.io/collector/config/internal v0.109.0/go.mod h1:JJJGJTz1hILaaT+01FxbCFcDvPf2otXqMcWk/s2KvlA= +go.opentelemetry.io/collector/confmap v1.15.0 h1:KaNVG6fBJXNqEI+/MgZasH0+aShAU1yAkSYunk6xC4E= +go.opentelemetry.io/collector/confmap v1.15.0/go.mod h1:GrIZ12P/9DPOuTpe2PIS51a0P/ZM6iKtByVee1Uf3+k= +go.opentelemetry.io/collector/consumer v0.109.0 h1:fdXlJi5Rat/poHPiznM2mLiXjcv1gPy3fyqqeirri58= +go.opentelemetry.io/collector/consumer v0.109.0/go.mod h1:E7PZHnVe1DY9hYy37toNxr9/hnsO7+LmnsixW8akLQI= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0 h1:+WZ6MEWQRC6so3IRrW916XK58rI9NnrFHKW/P19jQvc= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0/go.mod h1:spZ9Dn1MRMPDHHThdXZA5TrFhdOL1wsl0Dw45EBVoVo= +go.opentelemetry.io/collector/consumer/consumertest v0.109.0 h1:v4w9G2MXGJ/eabCmX1DvQYmxzdysC8UqIxa/BWz7ACo= +go.opentelemetry.io/collector/consumer/consumertest v0.109.0/go.mod h1:lECt0qOrx118wLJbGijtqNz855XfvJv0xx9GSoJ8qSE= +go.opentelemetry.io/collector/extension v0.109.0 h1:r/WkSCYGF1B/IpUgbrKTyJHcfn7+A5+mYfp5W7+B4U0= +go.opentelemetry.io/collector/extension v0.109.0/go.mod h1:WDE4fhiZnt2haxqSgF/2cqrr5H+QjgslN5tEnTBZuXc= +go.opentelemetry.io/collector/extension/auth v0.109.0 h1:yKUMCUG3IkjuOnHriNj0nqFU2DRdZn3Tvn9eqCI0eTg= +go.opentelemetry.io/collector/extension/auth v0.109.0/go.mod h1:wOIv49JhXIfol8CRmQvLve05ft3nZQUnTfcnuZKxdbo= +go.opentelemetry.io/collector/featuregate v1.15.0 h1:8KRWaZaE9hLlyMXnMTvnWtUJnzrBuTI0aLIvxqe8QP0= +go.opentelemetry.io/collector/featuregate v1.15.0/go.mod h1:47xrISO71vJ83LSMm8+yIDsUbKktUp48Ovt7RR6VbRs= +go.opentelemetry.io/collector/pdata v1.15.0 h1:q/T1sFpRKJnjDrUsHdJ6mq4uSqViR/f92yvGwDby/gY= +go.opentelemetry.io/collector/pdata v1.15.0/go.mod h1:2wcsTIiLAJSbqBq/XUUYbi+cP+N87d0jEJzmb9nT19U= +go.opentelemetry.io/collector/pdata/pprofile v0.109.0 h1:5lobQKeHk8p4WC7KYbzL6ZqqX3eSizsdmp5vM8pQFBs= +go.opentelemetry.io/collector/pdata/pprofile v0.109.0/go.mod h1:lXIifCdtR5ewO17JAYTUsclMqRp6h6dCowoXHhGyw8Y= +go.opentelemetry.io/collector/pdata/testdata v0.109.0 h1:gvIqy6juvqFET/6zi+zUOH1KZY/vtEDZW55u7gJ/hEo= +go.opentelemetry.io/collector/pdata/testdata v0.109.0/go.mod h1:zRttU/F5QMQ6ZXBMXCoSVG3EORTZLTK+UUS0VoMoT44= +go.opentelemetry.io/collector/receiver v0.109.0 h1:DTOM7xaDl7FUGQIjvjmWZn03JUE+aG4mJzWWfb7S8zw= +go.opentelemetry.io/collector/receiver v0.109.0/go.mod h1:jeiCHaf3PE6aXoZfHF5Uexg7aztu+Vkn9LVw0YDKm6g= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0 h1:KKzdIixE/XJWvqdCcNWAOtsEhNKu4waLKJjawjhnPLw= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0/go.mod h1:FKU+RFkSLWWB3tUUB6vifapZdFp1FoqVYVQ22jpHc8w= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw= +go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= +go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= +google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/receiver/huaweicloudlogsreceiver/integration_test.go b/receiver/huaweicloudlogsreceiver/integration_test.go new file mode 100644 index 000000000000..22494f271d30 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/integration_test.go @@ -0,0 +1,99 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudcesreceiver" + +import ( + "context" + "path/filepath" + "testing" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/plogtest" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver/internal/mocks" +) + +func TestHuaweiCloudLogsReceiverIntegration(t *testing.T) { + mc := mocks.NewLtsClient(t) + + mc.On("ListLogs", mock.Anything).Return(&model.ListLogsResponse{ + Logs: &[]model.LogContents{ + { + Content: stringPtr("2020-07-25/14:40:00 this log is Error NO 2"), + LineNum: stringPtr("123"), + Labels: map[string]string{ + "hostName": "ecs-kwxtest", + "hostIP": "192.168.0.156", + "appName": "default_appname", + "containerName": "CONFIG_FILE", + "clusterName": "CONFIG_FILE", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f55", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace": "CONFIG_FILE", + "category": "LTS", + }, + }, + { + Content: stringPtr("2020-07-25/14:50:00 this log is Error NO 3"), + LineNum: stringPtr("456"), + Labels: map[string]string{ + "hostName": "ecs-kwxtest", + "hostIP": "192.168.0.156", + "appName": "default_appname", + "containerName": "CONFIG_FILE", + "clusterName": "CONFIG_FILE", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f55", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace": "CONFIG_FILE", + "category": "LTS", + }, + }, + }, + }, nil) + + sink := &consumertest.LogsSink{} + cfg := createDefaultConfig().(*Config) + cfg.RegionID = "us-east-2" + cfg.CollectionInterval = time.Second + cfg.ProjectID = "my-project" + cfg.GroupID = "group-1" + cfg.StreamID = "stream-1" + + recv, err := NewFactory().CreateLogsReceiver( + context.Background(), + receivertest.NewNopSettings(), + cfg, + sink, + ) + require.NoError(t, err) + + rcvr, ok := recv.(*logsReceiver) + require.True(t, ok) + rcvr.client = mc + + err = recv.Start(context.Background(), componenttest.NewNopHost()) + require.NoError(t, err) + + require.Eventually(t, func() bool { + return sink.LogRecordCount() > 0 + }, 5*time.Second, 10*time.Millisecond) + + err = recv.Shutdown(context.Background()) + require.NoError(t, err) + + logs := sink.AllLogs()[0] + + expectedLogs, err := golden.ReadLogs(filepath.Join("testdata", "golden", "logs_golden.json")) + require.NoError(t, err) + require.NoError(t, plogtest.CompareLogs(expectedLogs, logs)) +} diff --git a/receiver/huaweicloudlogsreceiver/internal/lts_client.go b/receiver/huaweicloudlogsreceiver/internal/lts_client.go new file mode 100644 index 000000000000..8755afaf5e9d --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/internal/lts_client.go @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver/internal" + +import ( + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" +) + +// This interface should have all the function defined inside https://github.com/huaweicloud/huaweicloud-sdk-go-v3/blob/v0.1.110/services/lts/v2/lts_client.go +// Check https://github.com/vektra/mockery on how to install it on your machine. +// +//go:generate mockery --name LtsClient --case=underscore --output=./mocks +type LtsClient interface { + ListLogs(request *model.ListLogsRequest) (*model.ListLogsResponse, error) +} diff --git a/receiver/huaweicloudlogsreceiver/internal/lts_to_otlp.go b/receiver/huaweicloudlogsreceiver/internal/lts_to_otlp.go new file mode 100644 index 000000000000..d954ae4696ab --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/internal/lts_to_otlp.go @@ -0,0 +1,73 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver/internal" + +import ( + "fmt" + "regexp" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" +) + +const layout = "2006-01-02/15:04:05" + +// example of log content : "2020-07-25/14:44:43 this log is Error NO 2" +func extractTimestamp(log string) (time.Time, error) { + // Define the regex pattern to match the timestamp + pattern := `(\d{4}-\d{2}-\d{2}/\d{2}:\d{2}:\d{2})` + re := regexp.MustCompile(pattern) + + matches := re.FindStringSubmatch(log) + if len(matches) == 0 { + return time.Time{}, fmt.Errorf("timestamp not found") + } + + timestamp, err := time.Parse(layout, matches[0]) + if err != nil { + return time.Time{}, err + } + return timestamp, nil +} + +func ConvertLTSLogsToOTLP(projectID, regionID, groupID, streamD string, ltsLogs []model.LogContents) plog.Logs { + logs := plog.NewLogs() + resourceMetric := logs.ResourceLogs().AppendEmpty() + + resource := resourceMetric.Resource() + resource.Attributes().PutStr("cloud.provider", "huawei_cloud") + resource.Attributes().PutStr("project.id", projectID) + resource.Attributes().PutStr("region.id", regionID) + resource.Attributes().PutStr("group.id", groupID) + resource.Attributes().PutStr("stream.id", streamD) + + if len(ltsLogs) == 0 { + return logs + } + scopedLog := resourceMetric.ScopeLogs().AppendEmpty() + scopedLog.Scope().SetName("huawei_cloud_lts") + scopedLog.Scope().SetVersion("v2") + + for _, ltsLog := range ltsLogs { + + logRecord := scopedLog.LogRecords().AppendEmpty() + if ltsLog.Content != nil { + content := *ltsLog.Content + if ts, err := extractTimestamp(content); err == nil { + logRecord.SetObservedTimestamp(pcommon.Timestamp(ts.UnixNano())) + } + logRecord.Body().SetStr(content) + } + for key, value := range ltsLog.Labels { + logRecord.Attributes().PutStr(key, value) + } + if ltsLog.LineNum != nil { + logRecord.Attributes().PutStr("lineNum", *ltsLog.LineNum) + } + } + + return logs +} diff --git a/receiver/huaweicloudlogsreceiver/internal/lts_to_otlp_test.go b/receiver/huaweicloudlogsreceiver/internal/lts_to_otlp_test.go new file mode 100644 index 000000000000..684c6442588c --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/internal/lts_to_otlp_test.go @@ -0,0 +1,235 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "fmt" + "testing" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" +) + +func mustParseTime(t *testing.T, value string) time.Time { + ts, err := time.Parse(layout, value) + if err != nil { + require.NoError(t, err) + } + return ts +} + +func TestExtractTimestamp(t *testing.T) { + tests := []struct { + name string + log string + expectedTime time.Time + expectedError bool + }{ + { + name: "Valid Timestamp", + log: "2020-07-25/14:44:43 this log is Error NO 2", + expectedTime: time.Date(2020, 7, 25, 14, 44, 43, 0, time.UTC), + }, + { + name: "Invalid Timestamp Format", + log: "2020/07/25 14:44:43 this log is Error NO 2", + expectedError: true, + }, + { + name: "No Timestamp", + log: "this log is Error NO 2", + expectedError: true, + }, + { + name: "Empty Log", + log: "", + expectedError: true, + }, + { + name: "Partial Timestamp", + log: "2020-07-25/14:44 this log is Error NO 2", + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := extractTimestamp(tt.log) + + if (err != nil) != tt.expectedError { + t.Errorf("extractTimestamp() error = %v, expectedError %v", err, tt.expectedError) + return + } + if !tt.expectedError && !got.Equal(tt.expectedTime) { + t.Errorf("extractTimestamp() = %v, expected %v", got, tt.expectedTime) + } + }) + } +} + +func TestConvertLTSLogsToOTLP(t *testing.T) { + tests := []struct { + name string + projectID string + regionID string + groupID string + streamID string + ltsLogs []model.LogContents + expectLogs int + expectedTime time.Time + }{ + { + name: "Valid log content with timestamp", + projectID: "project1", + regionID: "region1", + groupID: "group1", + streamID: "stream1", + ltsLogs: []model.LogContents{ + { + Content: stringPointer("2020-07-25/14:40:00 this log is Error NO 2"), + LineNum: stringPointer("123"), + Labels: map[string]string{ + "hostName": "ecs-kwxtest", + "hostIP": "192.168.0.156", + "appName": "default_appname", + "containerName": "CONFIG_FILE", + "clusterName": "CONFIG_FILE", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f60", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace": "CONFIG_FILE", + "category": "LTS", + }, + }, + { + Content: stringPointer("2020-07-25/14:50:00 this log is Error NO 2"), + LineNum: stringPointer("456"), + Labels: map[string]string{ + "hostName": "ecs-kwxtest", + "hostIP": "192.168.0.156", + "appName": "default_appname", + "containerName": "CONFIG_FILE", + "clusterName": "CONFIG_FILE", + "hostId": "9787ef31-f171-4eff-ba71-72d580f11f60", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace": "CONFIG_FILE", + "category": "LTS", + }, + }, + }, + expectLogs: 2, + expectedTime: mustParseTime(t, "2020-07-25/14:44:43"), + }, + { + name: "Log content without timestamp", + projectID: "project2", + regionID: "region2", + groupID: "group2", + streamID: "stream2", + ltsLogs: []model.LogContents{ + { + Content: stringPointer("this log has no timestamp"), + Labels: map[string]string{"level": "info"}, + LineNum: stringPointer("456"), + }, + }, + expectLogs: 1, + expectedTime: time.Time{}, + }, + { + name: "Empty log content", + projectID: "project3", + regionID: "region3", + groupID: "group3", + streamID: "stream3", + ltsLogs: []model.LogContents{ + { + Content: stringPointer(""), + Labels: map[string]string{"level": "debug"}, + LineNum: stringPointer("789"), + }, + }, + expectLogs: 1, + expectedTime: time.Time{}, + }, + { + name: "Multiple logs", + projectID: "project4", + regionID: "region4", + groupID: "group4", + streamID: "stream4", + ltsLogs: []model.LogContents{ + { + Content: stringPointer("2020-07-25/14:44:43 log1"), + Labels: map[string]string{"level": "error"}, + LineNum: stringPointer("1"), + }, + { + Content: stringPointer("2020-07-25/14:45:43 log2"), + Labels: map[string]string{"level": "info"}, + LineNum: stringPointer("2"), + }, + }, + expectLogs: 2, + expectedTime: time.Time{}, + }, + { + name: "Invalid timestamp format", + projectID: "project5", + regionID: "region5", + groupID: "group5", + streamID: "stream5", + ltsLogs: []model.LogContents{ + { + Content: stringPointer("25/07/2020 14:44:43 this log has an invalid timestamp"), + Labels: map[string]string{"level": "error"}, + LineNum: stringPointer("123"), + }, + }, + expectLogs: 1, + expectedTime: time.Time{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logs := ConvertLTSLogsToOTLP(tt.projectID, tt.regionID, tt.groupID, tt.streamID, tt.ltsLogs) + m := &plog.JSONMarshaler{} + + jsonLogs, err := m.MarshalLogs(logs) + require.NoError(t, err) + fmt.Println(tt.name, string(jsonLogs)) + require.Equal(t, 1, logs.ResourceLogs().Len()) + require.Equal(t, 1, logs.ResourceLogs().At(0).ScopeLogs().Len()) + + logRecords := logs.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords() + assert.Equal(t, tt.expectLogs, logRecords.Len(), "unexpected number of logs") + + for i := 0; i < tt.expectLogs; i++ { + logRecord := logRecords.At(i) + if tt.ltsLogs[i].Content != nil { + content := *tt.ltsLogs[i].Content + if ts, err := extractTimestamp(content); err == nil { + assert.Equal(t, pcommon.Timestamp(ts.UnixNano()), logRecord.ObservedTimestamp(), "unexpected timestamp in log record") + } + } + + for key, value := range tt.ltsLogs[i].Labels { + attr, found := logRecord.Attributes().Get(key) + assert.True(t, found, "expected attribute %s not found", key) + assert.Equal(t, value, attr.Str(), "unexpected value for attribute %s", key) + } + } + }) + } +} + +func stringPointer(s string) *string { + return &s +} diff --git a/receiver/huaweicloudlogsreceiver/internal/metadata/generated_status.go b/receiver/huaweicloudlogsreceiver/internal/metadata/generated_status.go new file mode 100644 index 000000000000..6ec59b8b2775 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/internal/metadata/generated_status.go @@ -0,0 +1,16 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("huaweicloudlogsreceiver") + ScopeName = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver" +) + +const ( + LogsStability = component.StabilityLevelDevelopment +) diff --git a/receiver/huaweicloudlogsreceiver/internal/mocks/lts_client.go b/receiver/huaweicloudlogsreceiver/internal/mocks/lts_client.go new file mode 100644 index 000000000000..32865bd87791 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/internal/mocks/lts_client.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.44.1. DO NOT EDIT. + +package mocks + +import ( + model "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" + mock "github.com/stretchr/testify/mock" +) + +// LtsClient is an autogenerated mock type for the LtsClient type +type LtsClient struct { + mock.Mock +} + +// ListLogs provides a mock function with given fields: request +func (_m *LtsClient) ListLogs(request *model.ListLogsRequest) (*model.ListLogsResponse, error) { + ret := _m.Called(request) + + if len(ret) == 0 { + panic("no return value specified for ListLogs") + } + + var r0 *model.ListLogsResponse + var r1 error + if rf, ok := ret.Get(0).(func(*model.ListLogsRequest) (*model.ListLogsResponse, error)); ok { + return rf(request) + } + if rf, ok := ret.Get(0).(func(*model.ListLogsRequest) *model.ListLogsResponse); ok { + r0 = rf(request) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.ListLogsResponse) + } + } + + if rf, ok := ret.Get(1).(func(*model.ListLogsRequest) error); ok { + r1 = rf(request) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewLtsClient creates a new instance of LtsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLtsClient(t interface { + mock.TestingT + Cleanup(func()) +}) *LtsClient { + mock := &LtsClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/receiver/huaweicloudlogsreceiver/metadata.yaml b/receiver/huaweicloudlogsreceiver/metadata.yaml new file mode 100644 index 000000000000..b3f516ad2f16 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/metadata.yaml @@ -0,0 +1,9 @@ +type: huaweicloudlogsreceiver + +status: + class: receiver + stability: + development: [logs] + distributions: [] + codeowners: + active: [heitorganzeli, narcis96] diff --git a/receiver/huaweicloudlogsreceiver/receiver.go b/receiver/huaweicloudlogsreceiver/receiver.go new file mode 100644 index 000000000000..7f34d748eb54 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/receiver.go @@ -0,0 +1,191 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver" + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" + lts "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2" + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/region" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver/internal" +) + +const ( + // See https://support.huaweicloud.com/intl/en-us/devg-apisign/api-sign-errorcode.html + requestThrottledErrMsg = "APIGW.0308" +) + +type logsReceiver struct { + logger *zap.Logger + client internal.LtsClient + cancel context.CancelFunc + + host component.Host + nextConsumer consumer.Logs + lastTs time.Time + config *Config + shutdownChan chan struct{} +} + +func newHuaweiCloudLogsReceiver(settings receiver.Settings, cfg *Config, next consumer.Logs) *logsReceiver { + rcvr := &logsReceiver{ + logger: settings.Logger, + config: cfg, + nextConsumer: next, + shutdownChan: make(chan struct{}, 1), + } + return rcvr +} + +func (rcvr *logsReceiver) Start(ctx context.Context, host component.Host) error { + rcvr.host = host + ctx, rcvr.cancel = context.WithCancel(ctx) + + if rcvr.client == nil { + client, err := rcvr.createClient() + if err != nil { + rcvr.logger.Error(err.Error()) + return nil + } + rcvr.client = client + } + + go rcvr.startReadingLogs(ctx) + return nil +} + +func (rcvr *logsReceiver) startReadingLogs(ctx context.Context) { + if rcvr.config.InitialDelay > 0 { + <-time.After(rcvr.config.InitialDelay) + } + if err := rcvr.pollLogsAndConsume(ctx); err != nil { + rcvr.logger.Error(err.Error()) + } + ticker := time.NewTicker(rcvr.config.CollectionInterval) + + defer ticker.Stop() + for { + select { + case <-ticker.C: + // TODO: Improve error handling for client-server interactions + // The current implementation lacks robust error handling, especially for + // scenarios such as service unavailability, timeouts, and request errors. + // - Investigate how to handle service unavailability or timeouts gracefully. + // - Implement appropriate actions or retries for different types of request errors. + // - Refer to the Huawei SDK documentation to identify + // all possible client/request errors and determine how to manage them. + // - Consider implementing custom error messages or fallback mechanisms for critical failures. + + if err := rcvr.pollLogsAndConsume(ctx); err != nil { + rcvr.logger.Error(err.Error()) + } + case <-ctx.Done(): + return + } + } +} + +func (rcvr *logsReceiver) createClient() (*lts.LtsClient, error) { + auth, err := basic.NewCredentialsBuilder(). + WithAk(string(rcvr.config.AccessKey)). + WithSk(string(rcvr.config.SecretKey)). + WithProjectId(rcvr.config.ProjectID). + SafeBuild() + + if err != nil { + return nil, err + } + + httpConfig, err := huawei.CreateHTTPConfig(rcvr.config.HuaweiSessionConfig) + if err != nil { + return nil, err + } + r, err := region.SafeValueOf(rcvr.config.RegionID) + if err != nil { + return nil, err + } + + hcHTTPConfig, err := lts.LtsClientBuilder(). + WithRegion(r). + WithCredential(auth). + WithHttpConfig(httpConfig). + SafeBuild() + + if err != nil { + return nil, err + } + + return lts.NewLtsClient(hcHTTPConfig), nil +} + +func (rcvr *logsReceiver) pollLogsAndConsume(ctx context.Context) error { + if rcvr.client == nil { + return errors.New("invalid client") + } + to := time.Now() + from := rcvr.lastTs + if from.IsZero() { + from = to.Add(-1 * rcvr.config.CollectionInterval) + } + logs, err := rcvr.listLogs(ctx, from, to) + if err != nil { + return err + } + config := rcvr.config + otpLogs := internal.ConvertLTSLogsToOTLP(config.ProjectID, config.RegionID, config.GroupID, config.StreamID, logs) + if err := rcvr.nextConsumer.ConsumeLogs(ctx, otpLogs); err != nil { + return err + } + rcvr.lastTs = to + return nil +} + +func (rcvr *logsReceiver) listLogs(ctx context.Context, from, to time.Time) ([]model.LogContents, error) { + // TODO: Add pagination logic. Check IsQueryComplete field + response, err := huawei.MakeAPICallWithRetry( + ctx, + rcvr.shutdownChan, + rcvr.logger, + func() (*model.ListLogsResponse, error) { + return rcvr.client.ListLogs(&model.ListLogsRequest{ + LogGroupId: "test", + LogStreamId: "test", + Body: &model.QueryLtsLogParams{ + StartTime: from.UTC().String(), + EndTime: to.UTC().String(), + }, + }) + }, + func(err error) bool { return strings.Contains(err.Error(), requestThrottledErrMsg) }, + huawei.NewExponentialBackOff(&rcvr.config.BackOffConfig), + ) + if err != nil { + return []model.LogContents{}, err + } + if response == nil || response.Logs == nil || len((*response.Logs)) == 0 { + return []model.LogContents{}, errors.New("unexpected empty list of logs") + } + + return *response.Logs, nil +} + +func (rcvr *logsReceiver) Shutdown(_ context.Context) error { + if rcvr.cancel != nil { + rcvr.cancel() + } + rcvr.shutdownChan <- struct{}{} + close(rcvr.shutdownChan) + return nil +} diff --git a/receiver/huaweicloudlogsreceiver/receiver_test.go b/receiver/huaweicloudlogsreceiver/receiver_test.go new file mode 100644 index 000000000000..40ecf5d9c172 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/receiver_test.go @@ -0,0 +1,114 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package huaweicloudlogsreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver" + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/lts/v2/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" + "go.opentelemetry.io/collector/receiver/scraperhelper" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/huaweicloudlogsreceiver/internal/mocks" +) + +func stringPtr(s string) *string { + return &s +} + +func TestNewReceiver(t *testing.T) { + cfg := &Config{ + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: 1 * time.Second, + }, + } + mr := newHuaweiCloudLogsReceiver(receivertest.NewNopSettings(), cfg, new(consumertest.LogsSink)) + assert.NotNil(t, mr) +} + +func TestListLogsSuccess(t *testing.T) { + mc := mocks.NewLtsClient(t) + + mc.On("ListLogs", mock.Anything).Return(&model.ListLogsResponse{ + Logs: &[]model.LogContents{ + { + Content: stringPtr("2020-07-25/14:40:43 this log is Error NO 2"), + LineNum: stringPtr("10"), + Labels: map[string]string{ + "hostName": "ecs-kwxtest", + "hostIP": "192.168.0.156", + "appName": "default_appname", + "containerName": "CONFIG_FILE", + "clusterName": "CONFIG_FILE", + "hostId": "9787ef31-fd7b-4eff-ba71-72d580f11f55", + "podName": "default_procname", + "clusterId": "CONFIG_FILE", + "nameSpace": "CONFIG_FILE", + "category": "LTS", + }, + }, + { + Content: stringPtr("2020-07-26/15:00:43 this log is Error NO 3"), + }, + }, + }, nil) + + receiver := &logsReceiver{ + client: mc, + config: createDefaultConfig().(*Config), + } + + logs, err := receiver.listLogs(context.Background(), time.Now(), time.Now()) + + assert.NoError(t, err) + assert.Len(t, logs, 2) + mc.AssertExpectations(t) +} + +func TestListLogsFailure(t *testing.T) { + mc := mocks.NewLtsClient(t) + + mc.On("ListLogs", mock.Anything).Return(nil, errors.New("failed to list logs")) + receiver := &logsReceiver{ + client: mc, + config: createDefaultConfig().(*Config), + } + + logs, err := receiver.listLogs(context.Background(), time.Now(), time.Now()) + + assert.Error(t, err) + assert.Empty(t, logs) + assert.Equal(t, "failed to list logs", err.Error()) + mc.AssertExpectations(t) +} + +func TestPollLogsAndConsumeSuccess(t *testing.T) { + mc := mocks.NewLtsClient(t) + next := new(consumertest.LogsSink) + receiver := newHuaweiCloudLogsReceiver(receivertest.NewNopSettings(), &Config{}, next) + receiver.client = mc + + mc.On("ListLogs", mock.Anything).Return(&model.ListLogsResponse{ + Logs: &[]model.LogContents{ + { + Content: stringPtr("2020-07-25/14:40:43 this log is Error NO 2"), + }, + { + Content: stringPtr("2020-07-26/15:00:43 this log is Error NO 3"), + }, + }, + }, nil) + + err := receiver.pollLogsAndConsume(context.Background()) + + require.NoError(t, err) + assert.Equal(t, 2, next.LogRecordCount()) +} diff --git a/receiver/huaweicloudlogsreceiver/testdata/golden/logs_golden.yaml b/receiver/huaweicloudlogsreceiver/testdata/golden/logs_golden.yaml new file mode 100644 index 000000000000..b1fc9fee3056 --- /dev/null +++ b/receiver/huaweicloudlogsreceiver/testdata/golden/logs_golden.yaml @@ -0,0 +1,29 @@ +resourceLogs: + - resource: + attributes: + - key: cloud.provider + value: + stringValue: huawei_cloud + - key: project.id + value: + stringValue: my-project + - key: region.id + value: + stringValue: us-east-2 + - key: group.id + value: + stringValue: group-1 + - key: stream.id + value: + stringValue: stream-1 + scopeLogs: + - scope: + name: huawei_cloud_lts + version: v2 + - logRecords: + - body: 2020-07-25/14:40:00 this log is Error NO 2 + attrivutes: + - key: hostName + value: + stringValue: ecs-kwxtest + observedTimestamp: 2020-07-25/14:40:00 diff --git a/versions.yaml b/versions.yaml index 201f0e269d3e..7319f9e5ddfb 100644 --- a/versions.yaml +++ b/versions.yaml @@ -127,6 +127,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics - github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter - github.com/open-telemetry/opentelemetry-collector-contrib/internal/grpcutil + - github.com/open-telemetry/opentelemetry-collector-contrib/internal/huawei - github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig - github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest - github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka