diff --git a/README.md b/README.md index a4744f679..f2334ec2a 100644 --- a/README.md +++ b/README.md @@ -66,79 +66,80 @@ Current OTEL Collector Version: `v0.121.0` This section lists the components that are included in the Observe Distribution of the OpenTelemetry Collector. -| Receivers | Processors | Exporters | Extensions | Connectors | -|----------------------------------------------------------|-------------------------------------------------------|--------------------------------------------------------|--------------------------------------|-------------------------------------| -| [awsecscontainermetrics][awsecscontainermetricsreceiver] | [attributes][attributesprocessor] | [debug][debugexporter] | [file_storage][filestorage] | [count][countconnector] | -| [docker_stats][dockerstatsreceiver] | [batch][batchprocessor] | [file][fileexporter] | [health_check][healthcheckextension] | [forward][forwardconnector] | -| [elasticsearch][elasticsearchreceiver] | [cumulativetodelta][cumulativetodeltaprocessor] | [loadbalancing][loadbalancingexporter] | [pprof][pprofextension] | [spanmetrics][spanmetricsconnector] | -| [filelog][filelogreceiver] | [deltatocumulative][deltatocumulativeprocessor] | [otlphttp][otlphttpexporter] | [zpages][zpagesextension] | | -| [filestats][filestatsreceiver] | [filter][filterprocessor] | [prometheusremotewrite][prometheusremotewriteexporter] | | | -| [hostmetrics][hostmetricsreceiver] | [k8sattributes][k8sattributesprocessor] | | | | -| [httpcheck][httpcheckreceiver] | [memory_limiter][memorylimiterprocessor] | | | | -| [iis][iisreceiver] | [observek8sattributes][observek8sattributesprocessor] | | | | -| [journald][journaldreceiver] | [probabilisticsampler][probabilisticsamplerprocessor] | | | | -| [k8s_cluster][k8sclusterreceiver] | [redaction][redactionprocessor] | | | | -| [k8sobjects][k8sobjectsreceiver] | [resource][resourceprocessor] | | | | -| [kafkametrics][kafkametricsreceiver] | [resourcedetection][resourcedetectionprocessor] | | | | -| [kafka][kafkareceiver] | [span][spanprocessor] | | | | -| [kubeletstats][kubeletstatsreceiver] | [tailsampling][tailsamplingprocessor] | | | | -| [mongodb][mongodbreceiver] | [transform][transformprocessor] | | | | -| [otlp][otlpreceiver] | | | | | -| [prometheus][prometheusreceiver] | | | | | -| [redis][redisreceiver] | | | | | -| [snmp][snmpreceiver] | | | | | -| [statsd][statsdreceiver] | | | | | -| [tcplog][tcplogreceiver] | | | | | -| [udplog][udplogreceiver] | | | | | -| [windowseventlog][windowseventlogreceiver] | | | | | - -[awsecscontainermetricsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/awsecscontainermetricsreceiver -[dockerstatsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/dockerstatsreceiver -[elasticsearchreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/elasticsearchreceiver -[filelogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/filelogreceiver -[filestatsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/filestatsreceiver -[hostmetricsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/hostmetricsreceiver -[httpcheckreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/httpcheckreceiver -[iisreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/iisreceiver -[journaldreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/journaldreceiver -[k8sclusterreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/k8sclusterreceiver -[k8sobjectsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/k8sobjectsreceiver -[kafkametricsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/kafkametricsreceiver -[kafkareceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/kafkareceiver -[kubeletstatsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/kubeletstatsreceiver -[mongodbreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/mongodbreceiver -[otlpreceiver]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/receiver/otlpreceiver -[prometheusreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/prometheusreceiver -[redisreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/redisreceiver -[snmpreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/snmpreceiver -[statsdreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/statsdreceiver -[tcplogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/tcplogreceiver -[udplogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/udplogreceiver -[windowseventlogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/receiver/windowseventlogreceiver -[attributesprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/attributesprocessor -[batchprocessor]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/processor/batchprocessor -[cumulativetodeltaprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/cumulativetodeltaprocessor -[deltatocumulativeprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/deltatocumulativeprocessor -[filterprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/filterprocessor -[k8sattributesprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/k8sattributesprocessor -[memorylimiterprocessor]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/processor/memorylimiterprocessor +| Receivers | Processors | Exporters | Extensions | Connectors | +|----------------------------------------------------------|-------------------------------------------------------|--------------------------------------------------------|-----------------------------------------|-------------------------------------| +| [awsecscontainermetrics][awsecscontainermetricsreceiver] | [attributes][attributesprocessor] | [debug][debugexporter] | [cgroupruntime][cgroupruntimeextension] | [count][countconnector] | +| [docker_stats][dockerstatsreceiver] | [batch][batchprocessor] | [file][fileexporter] | [file_storage][filestorage] | [forward][forwardconnector] | +| [elasticsearch][elasticsearchreceiver] | [cumulativetodelta][cumulativetodeltaprocessor] | [loadbalancing][loadbalancingexporter] | [health_check][healthcheckextension] | [spanmetrics][spanmetricsconnector] | +| [filelog][filelogreceiver] | [deltatocumulative][deltatocumulativeprocessor] | [otlphttp][otlphttpexporter] | [pprof][pprofextension] | | +| [filestats][filestatsreceiver] | [filter][filterprocessor] | [prometheusremotewrite][prometheusremotewriteexporter] | [zpages][zpagesextension] | | +| [hostmetrics][hostmetricsreceiver] | [k8sattributes][k8sattributesprocessor] | | | | +| [httpcheck][httpcheckreceiver] | [memory_limiter][memorylimiterprocessor] | | | | +| [iis][iisreceiver] | [observek8sattributes][observek8sattributesprocessor] | | | | +| [journald][journaldreceiver] | [probabilisticsampler][probabilisticsamplerprocessor] | | | | +| [k8s_cluster][k8sclusterreceiver] | [redaction][redactionprocessor] | | | | +| [k8sobjects][k8sobjectsreceiver] | [resource][resourceprocessor] | | | | +| [kafkametrics][kafkametricsreceiver] | [resourcedetection][resourcedetectionprocessor] | | | | +| [kafka][kafkareceiver] | [span][spanprocessor] | | | | +| [kubeletstats][kubeletstatsreceiver] | [tailsampling][tailsamplingprocessor] | | | | +| [mongodb][mongodbreceiver] | [transform][transformprocessor] | | | | +| [otlp][otlpreceiver] | | | | | +| [prometheus][prometheusreceiver] | | | | | +| [redis][redisreceiver] | | | | | +| [snmp][snmpreceiver] | | | | | +| [statsd][statsdreceiver] | | | | | +| [tcplog][tcplogreceiver] | | | | | +| [udplog][udplogreceiver] | | | | | +| [windowseventlog][windowseventlogreceiver] | | | | | + +[awsecscontainermetricsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/awsecscontainermetricsreceiver +[dockerstatsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/dockerstatsreceiver +[elasticsearchreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/elasticsearchreceiver +[filelogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/filelogreceiver +[filestatsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/filestatsreceiver +[hostmetricsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/hostmetricsreceiver +[httpcheckreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/httpcheckreceiver +[iisreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/iisreceiver +[journaldreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/journaldreceiver +[k8sclusterreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/k8sclusterreceiver +[k8sobjectsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/k8sobjectsreceiver +[kafkametricsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/kafkametricsreceiver +[kafkareceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/kafkareceiver +[kubeletstatsreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/kubeletstatsreceiver +[mongodbreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/mongodbreceiver +[otlpreceiver]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/receiver/otlpreceiver +[prometheusreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/prometheusreceiver +[redisreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/redisreceiver +[snmpreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/snmpreceiver +[statsdreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/statsdreceiver +[tcplogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/tcplogreceiver +[udplogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/udplogreceiver +[windowseventlogreceiver]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/receiver/windowseventlogreceiver +[attributesprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/attributesprocessor +[batchprocessor]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/processor/batchprocessor +[cumulativetodeltaprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/cumulativetodeltaprocessor +[deltatocumulativeprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/deltatocumulativeprocessor +[filterprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/filterprocessor +[k8sattributesprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/k8sattributesprocessor +[memorylimiterprocessor]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/processor/memorylimiterprocessor [observek8sattributesprocessor]: ./components/processors/observek8sattributesprocessor -[probabilisticsamplerprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/probabilisticsamplerprocessor -[redactionprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/redactionprocessor -[resourceprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/resourceprocessor -[resourcedetectionprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/resourcedetectionprocessor -[spanprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/spanprocessor -[tailsamplingprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/tailsamplingprocessor -[transformprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/processor/transformprocessor -[debugexporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/exporter/debugexporter -[fileexporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/exporter/fileexporter -[loadbalancingexporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/exporter/loadbalancingexporter -[otlphttpexporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/exporter/otlphttpexporter -[prometheusremotewriteexporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/exporter/prometheusremotewriteexporter -[countconnector]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/connector/countconnector -[forwardconnector]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/connector/forwardconnector -[spanmetricsconnector]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/connector/spanmetricsconnector -[filestorage]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/extension/storage/filestorage -[healthcheckextension]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/extension/healthcheckextension -[pprofextension]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.119.0/extension/pprofextension -[zpagesextension]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.119.0/extension/zpagesextension +[probabilisticsamplerprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/probabilisticsamplerprocessor +[redactionprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/redactionprocessor +[resourceprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/resourceprocessor +[resourcedetectionprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/resourcedetectionprocessor +[spanprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/spanprocessor +[tailsamplingprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/tailsamplingprocessor +[transformprocessor]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/processor/transformprocessor +[debugexporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/exporter/debugexporter +[fileexporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/exporter/fileexporter +[loadbalancingexporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/exporter/loadbalancingexporter +[otlphttpexporter]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/exporter/otlphttpexporter +[prometheusremotewriteexporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/exporter/prometheusremotewriteexporter +[countconnector]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/connector/countconnector +[forwardconnector]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/connector/forwardconnector +[spanmetricsconnector]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/connector/spanmetricsconnector +[filestorage]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/extension/storage/filestorage +[cgroupruntimeextension]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/extension/cgroupruntimeextension +[healthcheckextension]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/extension/healthcheckextension +[pprofextension]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.121.0/extension/pprofextension +[zpagesextension]: https://github.com/open-telemetry/opentelemetry-collector/tree/v0.121.0/extension/zpagesextension diff --git a/builder-config.yaml b/builder-config.yaml index 6a279532d..a2502ce6c 100644 --- a/builder-config.yaml +++ b/builder-config.yaml @@ -62,6 +62,7 @@ receivers: extensions: - gomod: go.opentelemetry.io/collector/extension/zpagesextension v0.121.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension v0.121.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.121.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.121.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.121.0 diff --git a/go.mod b/go.mod index e7620a05e..1c5c30050 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/Code-Hex/go-generics-cache v1.5.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect github.com/IBM/sarama v1.45.0 // indirect + github.com/KimMachineGun/automemlimit v0.7.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Showmax/go-fqdn v1.0.0 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect @@ -179,6 +180,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.121.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter v0.121.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter v0.121.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension v0.121.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.121.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.121.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.121.0 // indirect @@ -249,6 +251,7 @@ require ( github.com/openshift/client-go v0.0.0-20210521082421-73d9475a9142 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/ovh/go-ovh v1.6.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect @@ -262,6 +265,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/prometheus v0.300.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rdforte/gomaxecs v1.1.1 // indirect github.com/redis/go-redis/v9 v9.7.1 // indirect github.com/relvacode/iso8601 v1.6.0 // indirect github.com/rs/cors v1.11.1 // indirect @@ -391,6 +395,7 @@ require ( go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/atomic v1.11.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect golang.org/x/crypto v0.35.0 // indirect diff --git a/observecol/components.go b/observecol/components.go index 27872f24c..cb67c595b 100644 --- a/observecol/components.go +++ b/observecol/components.go @@ -19,6 +19,7 @@ import ( loadbalancingexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter" prometheusremotewriteexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter" zpagesextension "go.opentelemetry.io/collector/extension/zpagesextension" + cgroupruntimeextension "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension" healthcheckextension "github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension" filestorage "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage" pprofextension "github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension" @@ -68,6 +69,7 @@ func components() (otelcol.Factories, error) { factories.Extensions, err = otelcol.MakeFactoryMap[extension.Factory]( zpagesextension.NewFactory(), + cgroupruntimeextension.NewFactory(), healthcheckextension.NewFactory(), filestorage.NewFactory(), pprofextension.NewFactory(), @@ -77,6 +79,7 @@ func components() (otelcol.Factories, error) { } factories.ExtensionModules = make(map[component.Type]string, len(factories.Extensions)) factories.ExtensionModules[zpagesextension.NewFactory().Type()] = "go.opentelemetry.io/collector/extension/zpagesextension v0.121.0" + factories.ExtensionModules[cgroupruntimeextension.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension v0.121.0" factories.ExtensionModules[healthcheckextension.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.121.0" factories.ExtensionModules[filestorage.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.121.0" factories.ExtensionModules[pprofextension.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.121.0" diff --git a/observecol/go.mod b/observecol/go.mod index c18382b5d..7e22cbed1 100644 --- a/observecol/go.mod +++ b/observecol/go.mod @@ -12,6 +12,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.121.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter v0.121.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter v0.121.0 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension v0.121.0 github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.121.0 github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.121.0 github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.121.0 @@ -85,6 +86,7 @@ require ( github.com/Code-Hex/go-generics-cache v1.5.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect github.com/IBM/sarama v1.45.0 // indirect + github.com/KimMachineGun/automemlimit v0.7.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Showmax/go-fqdn v1.0.0 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect @@ -260,6 +262,7 @@ require ( github.com/openshift/client-go v0.0.0-20210521082421-73d9475a9142 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/ovh/go-ovh v1.6.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect @@ -274,6 +277,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/prometheus v0.300.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rdforte/gomaxecs v1.1.1 // indirect github.com/redis/go-redis/v9 v9.7.1 // indirect github.com/relvacode/iso8601 v1.6.0 // indirect github.com/rs/cors v1.11.1 // indirect @@ -379,6 +383,7 @@ require ( go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/atomic v1.11.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect diff --git a/vendor/github.com/KimMachineGun/automemlimit/LICENSE b/vendor/github.com/KimMachineGun/automemlimit/LICENSE new file mode 100644 index 000000000..1f5b8f6b3 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Geon Kim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go new file mode 100644 index 000000000..73a57c3eb --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go @@ -0,0 +1,410 @@ +package memlimit + +import ( + "bufio" + "errors" + "fmt" + "io" + "math" + "os" + "path/filepath" + "slices" + "strconv" + "strings" +) + +var ( + // ErrNoCgroup is returned when the process is not in cgroup. + ErrNoCgroup = errors.New("process is not in cgroup") + // ErrCgroupsNotSupported is returned when the system does not support cgroups. + ErrCgroupsNotSupported = errors.New("cgroups is not supported on this system") +) + +// fromCgroup retrieves the memory limit from the cgroup. +// The versionDetector function is used to detect the cgroup version from the mountinfo. +func fromCgroup(versionDetector func(mis []mountInfo) (bool, bool)) (uint64, error) { + mf, err := os.Open("/proc/self/mountinfo") + if err != nil { + return 0, fmt.Errorf("failed to open /proc/self/mountinfo: %w", err) + } + defer mf.Close() + + mis, err := parseMountInfo(mf) + if err != nil { + return 0, fmt.Errorf("failed to parse mountinfo: %w", err) + } + + v1, v2 := versionDetector(mis) + if !(v1 || v2) { + return 0, ErrNoCgroup + } + + cf, err := os.Open("/proc/self/cgroup") + if err != nil { + return 0, fmt.Errorf("failed to open /proc/self/cgroup: %w", err) + } + defer cf.Close() + + chs, err := parseCgroupFile(cf) + if err != nil { + return 0, fmt.Errorf("failed to parse cgroup file: %w", err) + } + + if v2 { + limit, err := getMemoryLimitV2(chs, mis) + if err == nil { + return limit, nil + } else if !v1 { + return 0, err + } + } + + return getMemoryLimitV1(chs, mis) +} + +// detectCgroupVersion detects the cgroup version from the mountinfo. +func detectCgroupVersion(mis []mountInfo) (bool, bool) { + var v1, v2 bool + for _, mi := range mis { + switch mi.FilesystemType { + case "cgroup": + v1 = true + case "cgroup2": + v2 = true + } + } + return v1, v2 +} + +// getMemoryLimitV2 retrieves the memory limit from the cgroup v2 controller. +func getMemoryLimitV2(chs []cgroupHierarchy, mis []mountInfo) (uint64, error) { + // find the cgroup v2 path for the memory controller. + // in cgroup v2, the paths are unified and the controller list is empty. + idx := slices.IndexFunc(chs, func(ch cgroupHierarchy) bool { + return ch.HierarchyID == "0" && ch.ControllerList == "" + }) + if idx == -1 { + return 0, errors.New("cgroup v2 path not found") + } + relPath := chs[idx].CgroupPath + + // find the mountpoint for the cgroup v2 controller. + idx = slices.IndexFunc(mis, func(mi mountInfo) bool { + return mi.FilesystemType == "cgroup2" + }) + if idx == -1 { + return 0, errors.New("cgroup v2 mountpoint not found") + } + root, mountPoint := mis[idx].Root, mis[idx].MountPoint + + // resolve the actual cgroup path + cgroupPath, err := resolveCgroupPath(mountPoint, root, relPath) + if err != nil { + return 0, err + } + + // retrieve the memory limit from the memory.max file + return readMemoryLimitV2FromPath(filepath.Join(cgroupPath, "memory.max")) +} + +// readMemoryLimitV2FromPath reads the memory limit for cgroup v2 from the given path. +// this function expects the path to be memory.max file. +func readMemoryLimitV2FromPath(path string) (uint64, error) { + b, err := os.ReadFile(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return 0, ErrNoLimit + } + return 0, fmt.Errorf("failed to read memory.max: %w", err) + } + + slimit := strings.TrimSpace(string(b)) + if slimit == "max" { + return 0, ErrNoLimit + } + + limit, err := strconv.ParseUint(slimit, 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse memory.max value: %w", err) + } + + return limit, nil +} + +// getMemoryLimitV1 retrieves the memory limit from the cgroup v1 controller. +func getMemoryLimitV1(chs []cgroupHierarchy, mis []mountInfo) (uint64, error) { + // find the cgroup v1 path for the memory controller. + idx := slices.IndexFunc(chs, func(ch cgroupHierarchy) bool { + return slices.Contains(strings.Split(ch.ControllerList, ","), "memory") + }) + if idx == -1 { + return 0, errors.New("cgroup v1 path for memory controller not found") + } + relPath := chs[idx].CgroupPath + + // find the mountpoint for the cgroup v1 controller. + idx = slices.IndexFunc(mis, func(mi mountInfo) bool { + return mi.FilesystemType == "cgroup" && slices.Contains(strings.Split(mi.SuperOptions, ","), "memory") + }) + if idx == -1 { + return 0, errors.New("cgroup v1 mountpoint for memory controller not found") + } + root, mountPoint := mis[idx].Root, mis[idx].MountPoint + + // resolve the actual cgroup path + cgroupPath, err := resolveCgroupPath(mountPoint, root, relPath) + if err != nil { + return 0, err + } + + // retrieve the memory limit from the memory.stats and memory.limit_in_bytes files. + return readMemoryLimitV1FromPath(cgroupPath) +} + +// getCgroupV1NoLimit returns the maximum value that is used to represent no limit in cgroup v1. +// the max memory limit is max int64, but it should be multiple of the page size. +func getCgroupV1NoLimit() uint64 { + ps := uint64(os.Getpagesize()) + return math.MaxInt64 / ps * ps +} + +// readMemoryLimitV1FromPath reads the memory limit for cgroup v1 from the given path. +// this function expects the path to be the cgroup directory. +func readMemoryLimitV1FromPath(cgroupPath string) (uint64, error) { + // read hierarchical_memory_limit and memory.limit_in_bytes files. + // but if hierarchical_memory_limit is not available, then use the max value as a fallback. + hml, err := readHierarchicalMemoryLimit(filepath.Join(cgroupPath, "memory.stats")) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return 0, fmt.Errorf("failed to read hierarchical_memory_limit: %w", err) + } else if hml == 0 { + hml = math.MaxUint64 + } + + // read memory.limit_in_bytes file. + b, err := os.ReadFile(filepath.Join(cgroupPath, "memory.limit_in_bytes")) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return 0, fmt.Errorf("failed to read memory.limit_in_bytes: %w", err) + } + lib, err := strconv.ParseUint(strings.TrimSpace(string(b)), 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse memory.limit_in_bytes value: %w", err) + } else if lib == 0 { + hml = math.MaxUint64 + } + + // use the minimum value between hierarchical_memory_limit and memory.limit_in_bytes. + // if the limit is the maximum value, then it is considered as no limit. + limit := min(hml, lib) + if limit >= getCgroupV1NoLimit() { + return 0, ErrNoLimit + } + + return limit, nil +} + +// readHierarchicalMemoryLimit extracts hierarchical_memory_limit from memory.stats. +// this function expects the path to be memory.stats file. +func readHierarchicalMemoryLimit(path string) (uint64, error) { + file, err := os.Open(path) + if err != nil { + return 0, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + + fields := strings.Split(line, " ") + if len(fields) < 2 { + return 0, fmt.Errorf("failed to parse memory.stats %q: not enough fields", line) + } + + if fields[0] == "hierarchical_memory_limit" { + if len(fields) > 2 { + return 0, fmt.Errorf("failed to parse memory.stats %q: too many fields for hierarchical_memory_limit", line) + } + return strconv.ParseUint(fields[1], 10, 64) + } + } + if err := scanner.Err(); err != nil { + return 0, err + } + + return 0, nil +} + +// https://www.man7.org/linux/man-pages/man5/proc_pid_mountinfo.5.html +// 731 771 0:59 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +// +// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue +// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) +// +// (1) mount ID: a unique ID for the mount (may be reused after umount(2)). +// (2) parent ID: the ID of the parent mount (or of self for the root of this mount namespace's mount tree). +// (3) major:minor: the value of st_dev for files on this filesystem (see stat(2)). +// (4) root: the pathname of the directory in the filesystem which forms the root of this mount. +// (5) mount point: the pathname of the mount point relative to the process's root directory. +// (6) mount options: per-mount options (see mount(2)). +// (7) optional fields: zero or more fields of the form "tag[:value]"; see below. +// (8) separator: the end of the optional fields is marked by a single hyphen. +// (9) filesystem type: the filesystem type in the form "type[.subtype]". +// (10) mount source: filesystem-specific information or "none". +// (11) super options: per-superblock options (see mount(2)). +type mountInfo struct { + Root string + MountPoint string + FilesystemType string + SuperOptions string +} + +// parseMountInfoLine parses a line from the mountinfo file. +func parseMountInfoLine(line string) (mountInfo, error) { + if line == "" { + return mountInfo{}, errors.New("empty line") + } + + fieldss := strings.SplitN(line, " - ", 2) + if len(fieldss) != 2 { + return mountInfo{}, fmt.Errorf("invalid separator") + } + + fields1 := strings.SplitN(fieldss[0], " ", 7) + if len(fields1) < 6 { + return mountInfo{}, fmt.Errorf("not enough fields before separator: %v", fields1) + } else if len(fields1) == 6 { + fields1 = append(fields1, "") + } + + fields2 := strings.Split(fieldss[1], " ") + if len(fields2) < 3 { + return mountInfo{}, fmt.Errorf("not enough fields after separator: %v", fields2) + } else if len(fields2) > 3 { + return mountInfo{}, fmt.Errorf("too many fields after separator: %v", fields2) + } + + return mountInfo{ + Root: fields1[3], + MountPoint: fields1[4], + FilesystemType: fields2[0], + SuperOptions: fields2[2], + }, nil +} + +// parseMountInfo parses the mountinfo file. +func parseMountInfo(r io.Reader) ([]mountInfo, error) { + var ( + s = bufio.NewScanner(r) + mis []mountInfo + ) + for s.Scan() { + line := s.Text() + + mi, err := parseMountInfoLine(line) + if err != nil { + return nil, fmt.Errorf("failed to parse mountinfo file %q: %w", line, err) + } + + mis = append(mis, mi) + } + if err := s.Err(); err != nil { + return nil, err + } + + return mis, nil +} + +// https://www.man7.org/linux/man-pages/man7/cgroups.7.html +// +// 5:cpuacct,cpu,cpuset:/daemons +// (1) (2) (3) +// +// (1) hierarchy ID: +// +// cgroups version 1 hierarchies, this field +// contains a unique hierarchy ID number that can be +// matched to a hierarchy ID in /proc/cgroups. For the +// cgroups version 2 hierarchy, this field contains the +// value 0. +// +// (2) controller list: +// +// For cgroups version 1 hierarchies, this field +// contains a comma-separated list of the controllers +// bound to the hierarchy. For the cgroups version 2 +// hierarchy, this field is empty. +// +// (3) cgroup path: +// +// This field contains the pathname of the control group +// in the hierarchy to which the process belongs. This +// pathname is relative to the mount point of the +// hierarchy. +type cgroupHierarchy struct { + HierarchyID string + ControllerList string + CgroupPath string +} + +// parseCgroupHierarchyLine parses a line from the cgroup file. +func parseCgroupHierarchyLine(line string) (cgroupHierarchy, error) { + if line == "" { + return cgroupHierarchy{}, errors.New("empty line") + } + + fields := strings.Split(line, ":") + if len(fields) < 3 { + return cgroupHierarchy{}, fmt.Errorf("not enough fields: %v", fields) + } else if len(fields) > 3 { + return cgroupHierarchy{}, fmt.Errorf("too many fields: %v", fields) + } + + return cgroupHierarchy{ + HierarchyID: fields[0], + ControllerList: fields[1], + CgroupPath: fields[2], + }, nil +} + +// parseCgroupFile parses the cgroup file. +func parseCgroupFile(r io.Reader) ([]cgroupHierarchy, error) { + var ( + s = bufio.NewScanner(r) + chs []cgroupHierarchy + ) + for s.Scan() { + line := s.Text() + + ch, err := parseCgroupHierarchyLine(line) + if err != nil { + return nil, fmt.Errorf("failed to parse cgroup file %q: %w", line, err) + } + + chs = append(chs, ch) + } + if err := s.Err(); err != nil { + return nil, err + } + + return chs, nil +} + +// resolveCgroupPath resolves the actual cgroup path from the mountpoint, root, and cgroupRelPath. +func resolveCgroupPath(mountpoint, root, cgroupRelPath string) (string, error) { + rel, err := filepath.Rel(root, cgroupRelPath) + if err != nil { + return "", err + } + + // if the relative path is ".", then the cgroupRelPath is the root itself. + if rel == "." { + return mountpoint, nil + } + + // if the relative path starts with "..", then it is outside the root. + if strings.HasPrefix(rel, "..") { + return "", fmt.Errorf("invalid cgroup path: %s is not under root %s", cgroupRelPath, root) + } + + return filepath.Join(mountpoint, rel), nil +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_linux.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_linux.go new file mode 100644 index 000000000..fd2c7e497 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_linux.go @@ -0,0 +1,32 @@ +//go:build linux +// +build linux + +package memlimit + +// FromCgroup retrieves the memory limit from the cgroup. +func FromCgroup() (uint64, error) { + return fromCgroup(detectCgroupVersion) +} + +// FromCgroupV1 retrieves the memory limit from the cgroup v1 controller. +// After v1.0.0, this function could be removed and FromCgroup should be used instead. +func FromCgroupV1() (uint64, error) { + return fromCgroup(func(_ []mountInfo) (bool, bool) { + return true, false + }) +} + +// FromCgroupHybrid retrieves the memory limit from the cgroup v2 and v1 controller sequentially, +// basically, it is equivalent to FromCgroup. +// After v1.0.0, this function could be removed and FromCgroup should be used instead. +func FromCgroupHybrid() (uint64, error) { + return FromCgroup() +} + +// FromCgroupV2 retrieves the memory limit from the cgroup v2 controller. +// After v1.0.0, this function could be removed and FromCgroup should be used instead. +func FromCgroupV2() (uint64, error) { + return fromCgroup(func(_ []mountInfo) (bool, bool) { + return false, true + }) +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_unsupported.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_unsupported.go new file mode 100644 index 000000000..9feca81a5 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_unsupported.go @@ -0,0 +1,20 @@ +//go:build !linux +// +build !linux + +package memlimit + +func FromCgroup() (uint64, error) { + return 0, ErrCgroupsNotSupported +} + +func FromCgroupV1() (uint64, error) { + return 0, ErrCgroupsNotSupported +} + +func FromCgroupHybrid() (uint64, error) { + return 0, ErrCgroupsNotSupported +} + +func FromCgroupV2() (uint64, error) { + return 0, ErrCgroupsNotSupported +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/exp_system.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/exp_system.go new file mode 100644 index 000000000..dee95f520 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/exp_system.go @@ -0,0 +1,14 @@ +package memlimit + +import ( + "github.com/pbnjay/memory" +) + +// FromSystem returns the total memory of the system. +func FromSystem() (uint64, error) { + limit := memory.TotalMemory() + if limit == 0 { + return 0, ErrNoLimit + } + return limit, nil +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/experiment.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/experiment.go new file mode 100644 index 000000000..2a7c320ed --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/experiment.go @@ -0,0 +1,59 @@ +package memlimit + +import ( + "fmt" + "os" + "reflect" + "strings" +) + +const ( + envAUTOMEMLIMIT_EXPERIMENT = "AUTOMEMLIMIT_EXPERIMENT" +) + +// Experiments is a set of experiment flags. +// It is used to enable experimental features. +// +// You can set the flags by setting the environment variable AUTOMEMLIMIT_EXPERIMENT. +// The value of the environment variable is a comma-separated list of experiment names. +// +// The following experiment names are known: +// +// - none: disable all experiments +// - system: enable fallback to system memory limit +type Experiments struct { + // System enables fallback to system memory limit. + System bool +} + +func parseExperiments() (Experiments, error) { + var exp Experiments + + // Create a map of known experiment names. + names := make(map[string]func(bool)) + rv := reflect.ValueOf(&exp).Elem() + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + field := rv.Field(i) + names[strings.ToLower(rt.Field(i).Name)] = field.SetBool + } + + // Parse names. + for _, f := range strings.Split(os.Getenv(envAUTOMEMLIMIT_EXPERIMENT), ",") { + if f == "" { + continue + } + if f == "none" { + exp = Experiments{} + continue + } + val := true + set, ok := names[f] + if !ok { + return Experiments{}, fmt.Errorf("unknown AUTOMEMLIMIT_EXPERIMENT %s", f) + } + set(val) + } + + return exp, nil +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/logger.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/logger.go new file mode 100644 index 000000000..4cf0b589d --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/logger.go @@ -0,0 +1,13 @@ +package memlimit + +import ( + "context" + "log/slog" +) + +type noopLogger struct{} + +func (noopLogger) Enabled(context.Context, slog.Level) bool { return false } +func (noopLogger) Handle(context.Context, slog.Record) error { return nil } +func (d noopLogger) WithAttrs([]slog.Attr) slog.Handler { return d } +func (d noopLogger) WithGroup(string) slog.Handler { return d } diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go new file mode 100644 index 000000000..cbd53ce3a --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go @@ -0,0 +1,283 @@ +package memlimit + +import ( + "errors" + "fmt" + "log/slog" + "math" + "os" + "runtime/debug" + "strconv" + "time" +) + +const ( + envGOMEMLIMIT = "GOMEMLIMIT" + envAUTOMEMLIMIT = "AUTOMEMLIMIT" + // Deprecated: use memlimit.WithLogger instead + envAUTOMEMLIMIT_DEBUG = "AUTOMEMLIMIT_DEBUG" + + defaultAUTOMEMLIMIT = 0.9 +) + +// ErrNoLimit is returned when the memory limit is not set. +var ErrNoLimit = errors.New("memory is not limited") + +type config struct { + logger *slog.Logger + ratio float64 + provider Provider + refresh time.Duration +} + +// Option is a function that configures the behavior of SetGoMemLimitWithOptions. +type Option func(cfg *config) + +// WithRatio configures the ratio of the memory limit to set as GOMEMLIMIT. +// +// Default: 0.9 +func WithRatio(ratio float64) Option { + return func(cfg *config) { + cfg.ratio = ratio + } +} + +// WithProvider configures the provider. +// +// Default: FromCgroup +func WithProvider(provider Provider) Option { + return func(cfg *config) { + cfg.provider = provider + } +} + +// WithLogger configures the logger. +// It automatically attaches the "package" attribute to the logs. +// +// Default: slog.New(noopLogger{}) +func WithLogger(logger *slog.Logger) Option { + return func(cfg *config) { + cfg.logger = memlimitLogger(logger) + } +} + +// WithRefreshInterval configures the refresh interval for automemlimit. +// If a refresh interval is greater than 0, automemlimit periodically fetches +// the memory limit from the provider and reapplies it if it has changed. +// If the provider returns an error, it logs the error and continues. +// ErrNoLimit is treated as math.MaxInt64. +// +// Default: 0 (no refresh) +func WithRefreshInterval(refresh time.Duration) Option { + return func(cfg *config) { + cfg.refresh = refresh + } +} + +// WithEnv configures whether to use environment variables. +// +// Default: false +// +// Deprecated: currently this does nothing. +func WithEnv() Option { + return func(cfg *config) {} +} + +func memlimitLogger(logger *slog.Logger) *slog.Logger { + if logger == nil { + return slog.New(noopLogger{}) + } + return logger.With(slog.String("package", "github.com/KimMachineGun/automemlimit/memlimit")) +} + +// SetGoMemLimitWithOpts sets GOMEMLIMIT with options and environment variables. +// +// You can configure how much memory of the cgroup's memory limit to set as GOMEMLIMIT +// through AUTOMEMLIMIT environment variable in the half-open range (0.0,1.0]. +// +// If AUTOMEMLIMIT is not set, it defaults to 0.9. (10% is the headroom for memory sources the Go runtime is unaware of.) +// If GOMEMLIMIT is already set or AUTOMEMLIMIT=off, this function does nothing. +// +// If AUTOMEMLIMIT_EXPERIMENT is set, it enables experimental features. +// Please see the documentation of Experiments for more details. +// +// Options: +// - WithRatio +// - WithProvider +// - WithLogger +func SetGoMemLimitWithOpts(opts ...Option) (_ int64, _err error) { + // init config + cfg := &config{ + logger: slog.New(noopLogger{}), + ratio: defaultAUTOMEMLIMIT, + provider: FromCgroup, + } + // TODO: remove this + if debug, ok := os.LookupEnv(envAUTOMEMLIMIT_DEBUG); ok { + defaultLogger := memlimitLogger(slog.Default()) + defaultLogger.Warn("AUTOMEMLIMIT_DEBUG is deprecated, use memlimit.WithLogger instead") + if debug == "true" { + cfg.logger = defaultLogger + } + } + for _, opt := range opts { + opt(cfg) + } + + // log error if any on return + defer func() { + if _err != nil { + cfg.logger.Error("failed to set GOMEMLIMIT", slog.Any("error", _err)) + } + }() + + // parse experiments + exps, err := parseExperiments() + if err != nil { + return 0, fmt.Errorf("failed to parse experiments: %w", err) + } + if exps.System { + cfg.logger.Info("system experiment is enabled: using system memory limit as a fallback") + cfg.provider = ApplyFallback(cfg.provider, FromSystem) + } + + // rollback to previous memory limit on panic + snapshot := debug.SetMemoryLimit(-1) + defer rollbackOnPanic(cfg.logger, snapshot, &_err) + + // check if GOMEMLIMIT is already set + if val, ok := os.LookupEnv(envGOMEMLIMIT); ok { + cfg.logger.Info("GOMEMLIMIT is already set, skipping", slog.String(envGOMEMLIMIT, val)) + return 0, nil + } + + // parse AUTOMEMLIMIT + ratio := cfg.ratio + if val, ok := os.LookupEnv(envAUTOMEMLIMIT); ok { + if val == "off" { + cfg.logger.Info("AUTOMEMLIMIT is set to off, skipping") + return 0, nil + } + ratio, err = strconv.ParseFloat(val, 64) + if err != nil { + return 0, fmt.Errorf("cannot parse AUTOMEMLIMIT: %s", val) + } + } + + // apply ratio to the provider + provider := capProvider(ApplyRatio(cfg.provider, ratio)) + + // set the memory limit and start refresh + limit, err := updateGoMemLimit(uint64(snapshot), provider, cfg.logger) + go refresh(provider, cfg.logger, cfg.refresh) + if err != nil { + if errors.Is(err, ErrNoLimit) { + cfg.logger.Info("memory is not limited, skipping") + // TODO: consider returning the snapshot + return 0, nil + } + return 0, fmt.Errorf("failed to set GOMEMLIMIT: %w", err) + } + + return int64(limit), nil +} + +// updateGoMemLimit updates the Go's memory limit, if it has changed. +func updateGoMemLimit(currLimit uint64, provider Provider, logger *slog.Logger) (uint64, error) { + newLimit, err := provider() + if err != nil { + return 0, err + } + + if newLimit == currLimit { + logger.Debug("GOMEMLIMIT is not changed, skipping", slog.Uint64(envGOMEMLIMIT, newLimit)) + return newLimit, nil + } + + debug.SetMemoryLimit(int64(newLimit)) + logger.Info("GOMEMLIMIT is updated", slog.Uint64(envGOMEMLIMIT, newLimit), slog.Uint64("previous", currLimit)) + + return newLimit, nil +} + +// refresh periodically fetches the memory limit from the provider and reapplies it if it has changed. +// See more details in the documentation of WithRefreshInterval. +func refresh(provider Provider, logger *slog.Logger, refresh time.Duration) { + if refresh == 0 { + return + } + + provider = noErrNoLimitProvider(provider) + + t := time.NewTicker(refresh) + for range t.C { + err := func() (_err error) { + snapshot := debug.SetMemoryLimit(-1) + defer rollbackOnPanic(logger, snapshot, &_err) + + _, err := updateGoMemLimit(uint64(snapshot), provider, logger) + if err != nil { + return err + } + + return nil + }() + if err != nil { + logger.Error("failed to refresh GOMEMLIMIT", slog.Any("error", err)) + } + } +} + +// rollbackOnPanic rollbacks to the snapshot on panic. +// Since it uses recover, it should be called in a deferred function. +func rollbackOnPanic(logger *slog.Logger, snapshot int64, err *error) { + panicErr := recover() + if panicErr != nil { + if *err != nil { + logger.Error("failed to set GOMEMLIMIT", slog.Any("error", *err)) + } + *err = fmt.Errorf("panic during setting the Go's memory limit, rolling back to previous limit %d: %v", + snapshot, panicErr, + ) + debug.SetMemoryLimit(snapshot) + } +} + +// SetGoMemLimitWithEnv sets GOMEMLIMIT with the value from the environment variables. +// Since WithEnv is deprecated, this function is equivalent to SetGoMemLimitWithOpts(). +// Deprecated: use SetGoMemLimitWithOpts instead. +func SetGoMemLimitWithEnv() { + _, _ = SetGoMemLimitWithOpts() +} + +// SetGoMemLimit sets GOMEMLIMIT with the value from the cgroup's memory limit and given ratio. +func SetGoMemLimit(ratio float64) (int64, error) { + return SetGoMemLimitWithOpts(WithRatio(ratio)) +} + +// SetGoMemLimitWithProvider sets GOMEMLIMIT with the value from the given provider and ratio. +func SetGoMemLimitWithProvider(provider Provider, ratio float64) (int64, error) { + return SetGoMemLimitWithOpts(WithProvider(provider), WithRatio(ratio)) +} + +func noErrNoLimitProvider(provider Provider) Provider { + return func() (uint64, error) { + limit, err := provider() + if errors.Is(err, ErrNoLimit) { + return math.MaxInt64, nil + } + return limit, err + } +} + +func capProvider(provider Provider) Provider { + return func() (uint64, error) { + limit, err := provider() + if err != nil { + return 0, err + } else if limit > math.MaxInt64 { + return math.MaxInt64, nil + } + return limit, nil + } +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/provider.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/provider.go new file mode 100644 index 000000000..4f83770d1 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/provider.go @@ -0,0 +1,43 @@ +package memlimit + +import ( + "fmt" +) + +// Provider is a function that returns the memory limit. +type Provider func() (uint64, error) + +// Limit is a helper Provider function that returns the given limit. +func Limit(limit uint64) func() (uint64, error) { + return func() (uint64, error) { + return limit, nil + } +} + +// ApplyRationA is a helper Provider function that applies the given ratio to the given provider. +func ApplyRatio(provider Provider, ratio float64) Provider { + if ratio == 1 { + return provider + } + return func() (uint64, error) { + if ratio <= 0 || ratio > 1 { + return 0, fmt.Errorf("invalid ratio: %f, ratio should be in the range (0.0,1.0]", ratio) + } + limit, err := provider() + if err != nil { + return 0, err + } + return uint64(float64(limit) * ratio), nil + } +} + +// ApplyFallback is a helper Provider function that sets the fallback provider. +func ApplyFallback(provider Provider, fallback Provider) Provider { + return func() (uint64, error) { + limit, err := provider() + if err != nil { + return fallback() + } + return limit, nil + } +} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/CONTRIBUTING.md b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/CONTRIBUTING.md new file mode 100644 index 000000000..a5fbbc80d --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing to the Cgroup Go runtime extension + +In order to contribute to this extension, it might be useful to have a working local setup. + +## Testing + +Some Linux distributions don't run systemd under cgroupv2, to run the integration tests locally for this extension you can follow these steps. + +Inside the extension folder, start a privileged docker container and share the code with the container + +```bash +cd extension/cgroupruntimeextension +docker run -ti --privileged --cgroupns=host -v $(pwd):/workspace -w /workspace debian:bookworm-slim +``` + +Install the [Go version](https://go.dev/dl/) specified in the extension's [go.mod](./go.mod) and the GCC compiler to run the integration test. The following is an example command for Go `1.23.4` in and `amd64` system: + +```bash +apt update && apt install -y wget sudo gcc && wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz && export PATH=$PATH:/usr/local/go/bin && go version && rm go1.23.4.linux-amd64.tar.gz +``` + +Run the integration test + +```bash +CGO_ENABLED=1 go test -v -exec sudo -race -timeout 360s -parallel 4 -tags=integration,"" +``` diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/LICENSE b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/Makefile b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/Makefile new file mode 100644 index 000000000..ded7a3609 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/README.md b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/README.md new file mode 100644 index 000000000..8af1a28d8 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/README.md @@ -0,0 +1,46 @@ +# Cgroup Go runtime extension + + + +| Status | | +| ------------- |-----------| +| Stability | [development] | +| Distributions | [contrib] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aextension%2Fcgroupruntime%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aextension%2Fcgroupruntime) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aextension%2Fcgroupruntime%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aextension%2Fcgroupruntime) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@mx-psi](https://www.github.com/mx-psi), [@rogercoll](https://www.github.com/rogercoll) | + +[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib + + +## Overview + +The OpenTelemetry Cgroup Auto-Config Extension is designed to optimize Go runtime performance in containerized environments by automatically configuring GOMAXPROCS and GOMEMLIMIT based on the Linux cgroup filesystem. This extension leverages [automaxprocs](https://github.com/uber-go/automaxprocs) or [gomaxecs](https://github.com/rdforte/gomaxecs) for AWS ECS Tasks and [automemlimit](https://github.com/KimMachineGun/automemlimit) packages to dynamically adjust Go runtime variables, ensuring efficient resource usage aligned with container limits. + +## Configuration + +The following settings can be configured: + +- `gomaxprocs`: Configures the behavior of setting `GOMAXPROCS`, the maximum number of CPUs for Go runtime. Options: + - `enabled`: A boolean value to enable or disable automatic configuration of `GOMAXPROCS` based on the system’s cgroup settings (default: true). + +- `gomemlimit`: Configures the behavior of setting `GOMEMLIMIT`, the maximum memory limit for Go runtime. Options: + - `enabled`: A boolean value to enable or disable automatic configuration of `GOMEMLIMIT` (default: true). + - `ratio`: A floating-point value between 0 and 1 that represents the fraction of the detected memory limit to allocate for the Go runtime (default: 0.9). + +## Examples + +```yaml +extension: + # processor name: cgroupruntime + cgroupruntime: + gomaxprocs: + enabled: true + gomemlimit: + enabled: true + ratio: 0.8 +``` + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for information on how to contribute to this extension. diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/config.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/config.go new file mode 100644 index 000000000..b9f3807b0 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/config.go @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package cgroupruntimeextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension" + +import "errors" // Config contains the configuration for the cgroup runtime extension. + +type Config struct { + GoMaxProcs GoMaxProcsConfig `mapstructure:"gomaxprocs"` + GoMemLimit GoMemLimitConfig `mapstructure:"gomemlimit"` +} + +type GoMaxProcsConfig struct { + Enabled bool `mapstructure:"enabled"` +} + +type GoMemLimitConfig struct { + Enabled bool `mapstructure:"enabled"` + Ratio float64 `mapstructure:"ratio"` +} + +// Validate checks if the extension configuration is valid +func (cfg *Config) Validate() error { + if cfg.GoMemLimit.Ratio <= 0 || cfg.GoMemLimit.Ratio > 1 { + return errors.New("gomemlimit ratio must be in the (0.0,1.0] range") + } + return nil +} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/doc.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/doc.go new file mode 100644 index 000000000..5832bf5a0 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +package cgroupruntimeextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension" diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/extension.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/extension.go new file mode 100644 index 000000000..78fc2c370 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/extension.go @@ -0,0 +1,77 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package cgroupruntimeextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension" + +import ( + "context" + "runtime" + "runtime/debug" + + "go.opentelemetry.io/collector/component" + "go.uber.org/zap" +) + +type ( + undoFunc func() + maxProcsFn func() (undoFunc, error) + memLimitWithRatioFn func(float64) (undoFunc, error) +) + +type cgroupRuntimeExtension struct { + config *Config + logger *zap.Logger + + // runtime modifiers + maxProcsFn + undoMaxProcsFn undoFunc + + memLimitWithRatioFn + undoMemLimitFn undoFunc +} + +func newCgroupRuntime(cfg *Config, logger *zap.Logger, maxProcsFn maxProcsFn, memLimitFn memLimitWithRatioFn) *cgroupRuntimeExtension { + return &cgroupRuntimeExtension{ + config: cfg, + logger: logger, + maxProcsFn: maxProcsFn, + memLimitWithRatioFn: memLimitFn, + } +} + +func (c *cgroupRuntimeExtension) Start(_ context.Context, _ component.Host) error { + var err error + if c.config.GoMaxProcs.Enabled { + c.undoMaxProcsFn, err = c.maxProcsFn() + if err != nil { + return err + } + + c.logger.Info("GOMAXPROCS has been set", + zap.Int("GOMAXPROCS", runtime.GOMAXPROCS(-1)), + ) + } + + if c.config.GoMemLimit.Enabled { + c.undoMemLimitFn, err = c.memLimitWithRatioFn(c.config.GoMemLimit.Ratio) + if err != nil { + return err + } + + c.logger.Info("GOMEMLIMIT has been set", + zap.Int64("GOMEMLIMIT", debug.SetMemoryLimit(-1)), + ) + } + return nil +} + +func (c *cgroupRuntimeExtension) Shutdown(_ context.Context) error { + if c.undoMaxProcsFn != nil { + c.undoMaxProcsFn() + } + if c.undoMemLimitFn != nil { + c.undoMemLimitFn() + } + + return nil +} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/factory.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/factory.go new file mode 100644 index 000000000..fdc6fca6f --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/factory.go @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package cgroupruntimeextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension" + +import ( + "context" + "fmt" + "runtime/debug" + + "github.com/KimMachineGun/automemlimit/memlimit" + gomaxecs "github.com/rdforte/gomaxecs/maxprocs" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/extension" + "go.uber.org/automaxprocs/maxprocs" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/internal/metadata" +) + +func NewFactory() extension.Factory { + return extension.NewFactory( + metadata.Type, + createDefaultConfig, + createExtension, + metadata.ExtensionStability, + ) +} + +func createDefaultConfig() component.Config { + return &Config{ + GoMaxProcs: GoMaxProcsConfig{ + Enabled: true, + }, + GoMemLimit: GoMemLimitConfig{ + Enabled: true, + // By default, it sets `GOMEMLIMIT` to 90% of cgroup's memory limit. + Ratio: 0.9, + }, + } +} + +func createExtension(_ context.Context, set extension.Settings, cfg component.Config) (extension.Extension, error) { + cgroupConfig := cfg.(*Config) + return newCgroupRuntime(cgroupConfig, set.Logger, + func() (undoFunc, error) { + if gomaxecs.IsECS() { + return gomaxecs.Set(gomaxecs.WithLogger(func(str string, params ...any) { + set.Logger.Debug(fmt.Sprintf(str, params)) + })) + } + return maxprocs.Set(maxprocs.Logger(func(str string, params ...any) { + set.Logger.Debug(fmt.Sprintf(str, params)) + })) + }, + func(ratio float64) (undoFunc, error) { + initial, err := memlimit.SetGoMemLimitWithOpts(memlimit.WithRatio(ratio)) + return func() { debug.SetMemoryLimit(initial) }, err + }), nil +} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/internal/metadata/generated_status.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/internal/metadata/generated_status.go new file mode 100644 index 000000000..172087ad8 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/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("cgroupruntime") + ScopeName = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension" +) + +const ( + ExtensionStability = component.StabilityLevelDevelopment +) diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/metadata.yaml b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/metadata.yaml new file mode 100644 index 000000000..6f1e7b998 --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/metadata.yaml @@ -0,0 +1,12 @@ +type: cgroupruntime + +status: + class: extension + stability: + development: [extension] + distributions: [contrib] + codeowners: + active: [mx-psi, rogercoll] + +tests: + config: diff --git a/vendor/github.com/pbnjay/memory/LICENSE b/vendor/github.com/pbnjay/memory/LICENSE new file mode 100644 index 000000000..63ca4a6d2 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, Jeremy Jay +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pbnjay/memory/README.md b/vendor/github.com/pbnjay/memory/README.md new file mode 100644 index 000000000..e98f261a0 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/README.md @@ -0,0 +1,41 @@ +# memory + +Package `memory` provides two methods reporting total physical system memory +accessible to the kernel, and free memory available to the running application. + +This package has no external dependency besides the standard library and default operating system tools. + +Documentation: +[![GoDoc](https://godoc.org/github.com/pbnjay/memory?status.svg)](https://godoc.org/github.com/pbnjay/memory) + +This is useful for dynamic code to minimize thrashing and other contention, similar to the stdlib `runtime.NumCPU` +See some history of the proposal at https://github.com/golang/go/issues/21816 + + +## Example + +```go +fmt.Printf("Total system memory: %d\n", memory.TotalMemory()) +fmt.Printf("Free memory: %d\n", memory.FreeMemory()) +``` + + +## Testing + +Tested/working on: + - macOS 10.12.6 (16G29), 10.15.7 (19H2) + - Windows 10 1511 (10586.1045) + - Linux RHEL (3.10.0-327.3.1.el7.x86_64) + - Raspberry Pi 3 (ARMv8) on Raspbian, ODROID-C1+ (ARMv7) on Ubuntu, C.H.I.P + (ARMv7). + - Amazon Linux 2 aarch64 (m6a.large, 4.14.203-156.332.amzn2.aarch64) + +Tested on virtual machines: + - Windows 7 SP1 386 + - Debian stretch 386 + - NetBSD 7.1 amd64 + 386 + - OpenBSD 6.1 amd64 + 386 + - FreeBSD 11.1 amd64 + 386 + - DragonFly BSD 4.8.1 amd64 + +If you have access to untested systems please test and report success/bugs. diff --git a/vendor/github.com/pbnjay/memory/doc.go b/vendor/github.com/pbnjay/memory/doc.go new file mode 100644 index 000000000..4e4f984c0 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/doc.go @@ -0,0 +1,24 @@ +// Package memory provides a single method reporting total system memory +// accessible to the kernel. +package memory + +// TotalMemory returns the total accessible system memory in bytes. +// +// The total accessible memory is installed physical memory size minus reserved +// areas for the kernel and hardware, if such reservations are reported by +// the operating system. +// +// If accessible memory size could not be determined, then 0 is returned. +func TotalMemory() uint64 { + return sysTotalMemory() +} + +// FreeMemory returns the total free system memory in bytes. +// +// The total free memory is installed physical memory size minus reserved +// areas for other applications running on the same system. +// +// If free memory size could not be determined, then 0 is returned. +func FreeMemory() uint64 { + return sysFreeMemory() +} diff --git a/vendor/github.com/pbnjay/memory/memory_bsd.go b/vendor/github.com/pbnjay/memory/memory_bsd.go new file mode 100644 index 000000000..49d808a9e --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_bsd.go @@ -0,0 +1,19 @@ +// +build freebsd openbsd dragonfly netbsd + +package memory + +func sysTotalMemory() uint64 { + s, err := sysctlUint64("hw.physmem") + if err != nil { + return 0 + } + return s +} + +func sysFreeMemory() uint64 { + s, err := sysctlUint64("hw.usermem") + if err != nil { + return 0 + } + return s +} diff --git a/vendor/github.com/pbnjay/memory/memory_darwin.go b/vendor/github.com/pbnjay/memory/memory_darwin.go new file mode 100644 index 000000000..a3f457699 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_darwin.go @@ -0,0 +1,49 @@ +// +build darwin + +package memory + +import ( + "os/exec" + "regexp" + "strconv" +) + +func sysTotalMemory() uint64 { + s, err := sysctlUint64("hw.memsize") + if err != nil { + return 0 + } + return s +} + +func sysFreeMemory() uint64 { + cmd := exec.Command("vm_stat") + outBytes, err := cmd.Output() + if err != nil { + return 0 + } + + rePageSize := regexp.MustCompile("page size of ([0-9]*) bytes") + reFreePages := regexp.MustCompile("Pages free: *([0-9]*)\\.") + + // default: page size of 4096 bytes + matches := rePageSize.FindSubmatchIndex(outBytes) + pageSize := uint64(4096) + if len(matches) == 4 { + pageSize, err = strconv.ParseUint(string(outBytes[matches[2]:matches[3]]), 10, 64) + if err != nil { + return 0 + } + } + + // ex: Pages free: 1126961. + matches = reFreePages.FindSubmatchIndex(outBytes) + freePages := uint64(0) + if len(matches) == 4 { + freePages, err = strconv.ParseUint(string(outBytes[matches[2]:matches[3]]), 10, 64) + if err != nil { + return 0 + } + } + return freePages * pageSize +} diff --git a/vendor/github.com/pbnjay/memory/memory_linux.go b/vendor/github.com/pbnjay/memory/memory_linux.go new file mode 100644 index 000000000..3d07711ce --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_linux.go @@ -0,0 +1,29 @@ +// +build linux + +package memory + +import "syscall" + +func sysTotalMemory() uint64 { + in := &syscall.Sysinfo_t{} + err := syscall.Sysinfo(in) + if err != nil { + return 0 + } + // If this is a 32-bit system, then these fields are + // uint32 instead of uint64. + // So we always convert to uint64 to match signature. + return uint64(in.Totalram) * uint64(in.Unit) +} + +func sysFreeMemory() uint64 { + in := &syscall.Sysinfo_t{} + err := syscall.Sysinfo(in) + if err != nil { + return 0 + } + // If this is a 32-bit system, then these fields are + // uint32 instead of uint64. + // So we always convert to uint64 to match signature. + return uint64(in.Freeram) * uint64(in.Unit) +} diff --git a/vendor/github.com/pbnjay/memory/memory_windows.go b/vendor/github.com/pbnjay/memory/memory_windows.go new file mode 100644 index 000000000..c8500cc6f --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_windows.go @@ -0,0 +1,60 @@ +// +build windows + +package memory + +import ( + "syscall" + "unsafe" +) + +// omitting a few fields for brevity... +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx +type memStatusEx struct { + dwLength uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 + ullAvailPhys uint64 + unused [5]uint64 +} + +func sysTotalMemory() uint64 { + kernel32, err := syscall.LoadDLL("kernel32.dll") + if err != nil { + return 0 + } + // GetPhysicallyInstalledSystemMemory is simpler, but broken on + // older versions of windows (and uses this under the hood anyway). + globalMemoryStatusEx, err := kernel32.FindProc("GlobalMemoryStatusEx") + if err != nil { + return 0 + } + msx := &memStatusEx{ + dwLength: 64, + } + r, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msx))) + if r == 0 { + return 0 + } + return msx.ullTotalPhys +} + +func sysFreeMemory() uint64 { + kernel32, err := syscall.LoadDLL("kernel32.dll") + if err != nil { + return 0 + } + // GetPhysicallyInstalledSystemMemory is simpler, but broken on + // older versions of windows (and uses this under the hood anyway). + globalMemoryStatusEx, err := kernel32.FindProc("GlobalMemoryStatusEx") + if err != nil { + return 0 + } + msx := &memStatusEx{ + dwLength: 64, + } + r, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msx))) + if r == 0 { + return 0 + } + return msx.ullAvailPhys +} diff --git a/vendor/github.com/pbnjay/memory/memsysctl.go b/vendor/github.com/pbnjay/memory/memsysctl.go new file mode 100644 index 000000000..438d9eff8 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memsysctl.go @@ -0,0 +1,21 @@ +// +build darwin freebsd openbsd dragonfly netbsd + +package memory + +import ( + "syscall" + "unsafe" +) + +func sysctlUint64(name string) (uint64, error) { + s, err := syscall.Sysctl(name) + if err != nil { + return 0, err + } + // hack because the string conversion above drops a \0 + b := []byte(s) + if len(b) < 8 { + b = append(b, 0) + } + return *(*uint64)(unsafe.Pointer(&b[0])), nil +} diff --git a/vendor/github.com/pbnjay/memory/stub.go b/vendor/github.com/pbnjay/memory/stub.go new file mode 100644 index 000000000..f29473ba0 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/stub.go @@ -0,0 +1,10 @@ +// +build !linux,!darwin,!windows,!freebsd,!dragonfly,!netbsd,!openbsd + +package memory + +func sysTotalMemory() uint64 { + return 0 +} +func sysFreeMemory() uint64 { + return 0 +} diff --git a/vendor/github.com/rdforte/gomaxecs/LICENSE b/vendor/github.com/rdforte/gomaxecs/LICENSE new file mode 100644 index 000000000..4c584e927 --- /dev/null +++ b/vendor/github.com/rdforte/gomaxecs/LICENSE @@ -0,0 +1,19 @@ +Copyright 2004 Ryan Forte + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/rdforte/gomaxecs/internal/client/client.go b/vendor/github.com/rdforte/gomaxecs/internal/client/client.go new file mode 100644 index 000000000..2628f564e --- /dev/null +++ b/vendor/github.com/rdforte/gomaxecs/internal/client/client.go @@ -0,0 +1,83 @@ +// Copyright 2004 Ryan Forte +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package client provides an HTTP client. +package client + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + + "github.com/rdforte/gomaxecs/internal/config" +) + +// New returns a new Client. +func New(cfg config.Client) *Client { + return &Client{ + client: &http.Client{ + Timeout: cfg.HTTPTimeout, + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: cfg.DialTimeout, + }).DialContext, + MaxIdleConns: cfg.MaxIdleConns, + MaxIdleConnsPerHost: cfg.MaxIdleConnsPerHost, + DisableKeepAlives: cfg.DisableKeepAlives, + IdleConnTimeout: cfg.IdleConnTimeout, + TLSHandshakeTimeout: cfg.TLSHandshakeTimeout, + ResponseHeaderTimeout: cfg.ResponseHeaderTimeout, + }, + }, + } +} + +// Client is an HTTP client. +type Client struct { + client *http.Client +} + +// Get performs an HTTP GET request. +func (c *Client) Get(ctx context.Context, url string) (*Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("failed to create HTTP request: %w", err) + } + + res, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to perform HTTP GET request: %w", err) + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + + return &Response{res.StatusCode, body}, nil +} + +type Response struct { + StatusCode int + Body []byte +} diff --git a/vendor/github.com/rdforte/gomaxecs/internal/config/config.go b/vendor/github.com/rdforte/gomaxecs/internal/config/config.go new file mode 100644 index 000000000..a405fc210 --- /dev/null +++ b/vendor/github.com/rdforte/gomaxecs/internal/config/config.go @@ -0,0 +1,103 @@ +// Copyright 2004 Ryan Forte +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package config provides the package configuration. +package config + +import ( + "os" + "strings" + "time" +) + +const ( + metaURIEnv = "ECS_CONTAINER_METADATA_URI_V4" + taskPath = "/task" + httpTimeout = 5 +) + +func New(opts ...Option) Config { + uri := GetECSMetadataURI() + + cfg := Config{ + TaskMetadataURI: uri + taskPath, + ContainerMetadataURI: uri, + Client: Client{ + HTTPTimeout: time.Second * httpTimeout, + DialTimeout: time.Second, + MaxIdleConns: 1, + MaxIdleConnsPerHost: 1, + DisableKeepAlives: false, // keep connection alive for subsequent requests. + IdleConnTimeout: time.Second, + TLSHandshakeTimeout: time.Second, + ResponseHeaderTimeout: time.Second, + }, + } + + for _, opt := range opts { + opt(&cfg) + } + + return cfg +} + +// GetECSMetadataURI returns the ECS metadata URI. +func GetECSMetadataURI() string { + uri := os.Getenv(metaURIEnv) + return strings.TrimRight(uri, "/") +} + +// Config represents the package configuration. +type Config struct { + ContainerMetadataURI string + TaskMetadataURI string + Client Client + log logger +} + +type logger func(format string, args ...any) + +// Client represents the HTTP client configuration. +type Client struct { + HTTPTimeout time.Duration + DialTimeout time.Duration + MaxIdleConns int + MaxIdleConnsPerHost int + DisableKeepAlives bool + IdleConnTimeout time.Duration + TLSHandshakeTimeout time.Duration + ResponseHeaderTimeout time.Duration +} + +func (c Config) Log(format string, args ...any) { + if c.log != nil { + c.log(format, args...) + } +} + +// WithLogger sets the logger for the config. +func WithLogger(logger logger) Option { + return func(cfg *Config) { + cfg.log = logger + } +} + +// Option represents a configuration option for the config. +type Option func(*Config) diff --git a/vendor/github.com/rdforte/gomaxecs/internal/task/meta.go b/vendor/github.com/rdforte/gomaxecs/internal/task/meta.go new file mode 100644 index 000000000..d15a7b646 --- /dev/null +++ b/vendor/github.com/rdforte/gomaxecs/internal/task/meta.go @@ -0,0 +1,94 @@ +// Copyright 2004 Ryan Forte +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package task + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/rdforte/gomaxecs/internal/client" +) + +// taskMeta represents the ECS Task Metadata. +type taskMeta struct { + Containers []container `json:"Containers"` + Limits limit `json:"Limits"` // this is optional in the response +} + +// container represents the ECS Container Metadata. +type container struct { + //nolint:tagliatelle // ECS Agent inconsistency. All fields adhere to goPascal but this one. + DockerID string `json:"DockerId"` + Limits limit `json:"Limits"` +} + +// limit contains the CPU limit. +type limit struct { + CPU float64 `json:"CPU"` +} + +// Grab the container metadata from the ECS Metadata endpoint. +// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4-examples.html +func (t *Task) getContainerMeta(ctx context.Context) (container, error) { + return getMeta[container](ctx, t.client, t.containerMetadataURI) +} + +// Grab the task metadata from the ECS Metadata endpoint + `/task` +// This will also include the container metadata. +// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4-examples.html +// #task-metadata-endpoint-v4-example-task-metadata-response. +func (t *Task) getTaskMeta(ctx context.Context) (taskMeta, error) { + return getMeta[taskMeta](ctx, t.client, t.taskMetadataURI) +} + +func getMeta[T any](ctx context.Context, client *client.Client, url string) (T, error) { + var res T + + resp, err := client.Get(ctx, url) + if err != nil { + return res, fmt.Errorf("request failed: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return res, newStatusError(resp.StatusCode) + } + + err = json.Unmarshal(resp.Body, &res) + if err != nil { + return res, fmt.Errorf("unmarshal failed: %w", err) + } + + return res, nil +} + +func newStatusError(status int) error { + return &statusError{status} +} + +type statusError struct { + status int +} + +func (e *statusError) Error() string { + return fmt.Sprintf("request failed, status code: %d", e.status) +} diff --git a/vendor/github.com/rdforte/gomaxecs/internal/task/task.go b/vendor/github.com/rdforte/gomaxecs/internal/task/task.go new file mode 100644 index 000000000..8240ecc46 --- /dev/null +++ b/vendor/github.com/rdforte/gomaxecs/internal/task/task.go @@ -0,0 +1,99 @@ +// Copyright 2004 Ryan Forte +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package task provides functionality for getting the max number of processors +// for an ECS task. +package task + +import ( + "context" + "errors" + "fmt" + + "github.com/rdforte/gomaxecs/internal/client" + "github.com/rdforte/gomaxecs/internal/config" +) + +const ( + cpuUnits = 10 + minCPU = 1 +) + +var errNoCPULimit = errors.New("no CPU limit found for task or container") + +// Task represents a task. +type Task struct { + taskMetadataURI string + containerMetadataURI string + client *client.Client +} + +// New returns a new Task. +func New(cfg config.Config) *Task { + return &Task{ + cfg.TaskMetadataURI, + cfg.ContainerMetadataURI, + client.New(cfg.Client), + } +} + +// GetMaxProcs is responsible for getting the max number of processors, or +// /sched/gomaxprocs:threads based on the CPU limit of the container and the task. +// The container vCPU can not be greater than Task CPU limit, therefore if +// Task CPU limit is less than 1, the max threads returned is 1. +// If no CPU limit is found for the container, then the max number of threads +// returned is the number of CPU's for the ECS Task. +func (t *Task) GetMaxProcs(ctx context.Context) (int, error) { + container, err := t.getContainerMeta(ctx) + if err != nil { + return 0, fmt.Errorf("failed to get ECS container meta: %w", err) + } + + task, err := t.getTaskMeta(ctx) + if err != nil { + return 0, fmt.Errorf("failed to get ECS task meta: %w", err) + } + + // Either the container limit or the task limit must be set + if container.Limits.CPU == 0 && task.Limits.CPU == 0 { + return 0, errNoCPULimit + } + + var containerCPULimit float64 + + for _, taskContainer := range task.Containers { + if container.DockerID == taskContainer.DockerID { + containerCPULimit = taskContainer.Limits.CPU + } + } + + if containerCPULimit == 0 { + return max(int(task.Limits.CPU), minCPU), nil + } + + cpu := max(int(containerCPULimit)>>cpuUnits, minCPU) + + taskCPULimit := int(task.Limits.CPU) + if taskCPULimit > 0 { + return min(taskCPULimit, cpu), nil + } + + return cpu, nil +} diff --git a/vendor/github.com/rdforte/gomaxecs/maxprocs/maxprocs.go b/vendor/github.com/rdforte/gomaxecs/maxprocs/maxprocs.go new file mode 100644 index 000000000..cf7bea57b --- /dev/null +++ b/vendor/github.com/rdforte/gomaxecs/maxprocs/maxprocs.go @@ -0,0 +1,71 @@ +package maxprocs + +import ( + "context" + "fmt" + "os" + "runtime" + + "github.com/rdforte/gomaxecs/internal/config" + ecstask "github.com/rdforte/gomaxecs/internal/task" +) + +const maxProcsKey = "GOMAXPROCS" + +// Set sets GOMAXPROCS based on the CPU limit of the container and the task. +// returns a function to reset GOMAXPROCS to its previous value and an error if one occurred. +// If the GOMAXPROCS environment variable is set, it will honor that value. +func Set(opts ...config.Option) (func(), error) { + cfg := config.New(opts...) + task := ecstask.New(cfg) + + undoNoop := func() { + cfg.Log("maxprocs: No GOMAXPROCS change to reset") + } + + if procs, ok := shouldHonorGOMAXPROCSEnv(); ok { + cfg.Log("maxprocs: Honoring GOMAXPROCS=%q as set in environment", procs) + return undoNoop, nil + } + + prevProcs := prevMaxProcs() + undo := func() { + cfg.Log("maxprocs: Resetting GOMAXPROCS to %v", prevProcs) + setMaxProcs(prevProcs) + } + + procs, err := task.GetMaxProcs(context.Background()) + if err != nil { + cfg.Log("maxprocs: Failed to set GOMAXPROCS:", err) + return undo, fmt.Errorf("failed to set GOMAXPROCS: %w", err) + } + + setMaxProcs(procs) + cfg.Log("maxprocs: Updated GOMAXPROCS=%v", procs) + + return undo, nil +} + +// shouldHonorGOMAXPROCSEnv returns the GOMAXPROCS environment variable if present +// and a boolean indicating if it should be honored. +func shouldHonorGOMAXPROCSEnv() (string, bool) { + return os.LookupEnv(maxProcsKey) +} + +func prevMaxProcs() int { + return runtime.GOMAXPROCS(0) +} + +func setMaxProcs(procs int) { + runtime.GOMAXPROCS(procs) +} + +// WithLogger sets the logger. By default, no logger is set. +func WithLogger(printf func(format string, args ...any)) config.Option { + return config.WithLogger(printf) +} + +// IsECS returns true if detected ECS environment. +func IsECS() bool { + return len(config.GetECSMetadataURI()) > 0 +} diff --git a/vendor/go.uber.org/automaxprocs/LICENSE b/vendor/go.uber.org/automaxprocs/LICENSE new file mode 100644 index 000000000..20dcf51d9 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go new file mode 100644 index 000000000..fe4ecf561 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go @@ -0,0 +1,79 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "io" + "os" + "path/filepath" + "strconv" +) + +// CGroup represents the data structure for a Linux control group. +type CGroup struct { + path string +} + +// NewCGroup returns a new *CGroup from a given path. +func NewCGroup(path string) *CGroup { + return &CGroup{path: path} +} + +// Path returns the path of the CGroup*. +func (cg *CGroup) Path() string { + return cg.path +} + +// ParamPath returns the path of the given cgroup param under itself. +func (cg *CGroup) ParamPath(param string) string { + return filepath.Join(cg.path, param) +} + +// readFirstLine reads the first line from a cgroup param file. +func (cg *CGroup) readFirstLine(param string) (string, error) { + paramFile, err := os.Open(cg.ParamPath(param)) + if err != nil { + return "", err + } + defer paramFile.Close() + + scanner := bufio.NewScanner(paramFile) + if scanner.Scan() { + return scanner.Text(), nil + } + if err := scanner.Err(); err != nil { + return "", err + } + return "", io.ErrUnexpectedEOF +} + +// readInt parses the first line from a cgroup param file as int. +func (cg *CGroup) readInt(param string) (int, error) { + text, err := cg.readFirstLine(param) + if err != nil { + return 0, err + } + return strconv.Atoi(text) +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go new file mode 100644 index 000000000..e89f54360 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go @@ -0,0 +1,118 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +const ( + // _cgroupFSType is the Linux CGroup file system type used in + // `/proc/$PID/mountinfo`. + _cgroupFSType = "cgroup" + // _cgroupSubsysCPU is the CPU CGroup subsystem. + _cgroupSubsysCPU = "cpu" + // _cgroupSubsysCPUAcct is the CPU accounting CGroup subsystem. + _cgroupSubsysCPUAcct = "cpuacct" + // _cgroupSubsysCPUSet is the CPUSet CGroup subsystem. + _cgroupSubsysCPUSet = "cpuset" + // _cgroupSubsysMemory is the Memory CGroup subsystem. + _cgroupSubsysMemory = "memory" + + // _cgroupCPUCFSQuotaUsParam is the file name for the CGroup CFS quota + // parameter. + _cgroupCPUCFSQuotaUsParam = "cpu.cfs_quota_us" + // _cgroupCPUCFSPeriodUsParam is the file name for the CGroup CFS period + // parameter. + _cgroupCPUCFSPeriodUsParam = "cpu.cfs_period_us" +) + +const ( + _procPathCGroup = "/proc/self/cgroup" + _procPathMountInfo = "/proc/self/mountinfo" +) + +// CGroups is a map that associates each CGroup with its subsystem name. +type CGroups map[string]*CGroup + +// NewCGroups returns a new *CGroups from given `mountinfo` and `cgroup` files +// under for some process under `/proc` file system (see also proc(5) for more +// information). +func NewCGroups(procPathMountInfo, procPathCGroup string) (CGroups, error) { + cgroupSubsystems, err := parseCGroupSubsystems(procPathCGroup) + if err != nil { + return nil, err + } + + cgroups := make(CGroups) + newMountPoint := func(mp *MountPoint) error { + if mp.FSType != _cgroupFSType { + return nil + } + + for _, opt := range mp.SuperOptions { + subsys, exists := cgroupSubsystems[opt] + if !exists { + continue + } + + cgroupPath, err := mp.Translate(subsys.Name) + if err != nil { + return err + } + cgroups[opt] = NewCGroup(cgroupPath) + } + + return nil + } + + if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { + return nil, err + } + return cgroups, nil +} + +// NewCGroupsForCurrentProcess returns a new *CGroups instance for the current +// process. +func NewCGroupsForCurrentProcess() (CGroups, error) { + return NewCGroups(_procPathMountInfo, _procPathCGroup) +} + +// CPUQuota returns the CPU quota applied with the CPU cgroup controller. +// It is a result of `cpu.cfs_quota_us / cpu.cfs_period_us`. If the value of +// `cpu.cfs_quota_us` was not set (-1), the method returns `(-1, nil)`. +func (cg CGroups) CPUQuota() (float64, bool, error) { + cpuCGroup, exists := cg[_cgroupSubsysCPU] + if !exists { + return -1, false, nil + } + + cfsQuotaUs, err := cpuCGroup.readInt(_cgroupCPUCFSQuotaUsParam) + if defined := cfsQuotaUs > 0; err != nil || !defined { + return -1, defined, err + } + + cfsPeriodUs, err := cpuCGroup.readInt(_cgroupCPUCFSPeriodUsParam) + if defined := cfsPeriodUs > 0; err != nil || !defined { + return -1, defined, err + } + + return float64(cfsQuotaUs) / float64(cfsPeriodUs), true, nil +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go new file mode 100644 index 000000000..78556062f --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go @@ -0,0 +1,176 @@ +// Copyright (c) 2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "path" + "strconv" + "strings" +) + +const ( + // _cgroupv2CPUMax is the file name for the CGroup-V2 CPU max and period + // parameter. + _cgroupv2CPUMax = "cpu.max" + // _cgroupFSType is the Linux CGroup-V2 file system type used in + // `/proc/$PID/mountinfo`. + _cgroupv2FSType = "cgroup2" + + _cgroupv2MountPoint = "/sys/fs/cgroup" + + _cgroupV2CPUMaxDefaultPeriod = 100000 + _cgroupV2CPUMaxQuotaMax = "max" +) + +const ( + _cgroupv2CPUMaxQuotaIndex = iota + _cgroupv2CPUMaxPeriodIndex +) + +// ErrNotV2 indicates that the system is not using cgroups2. +var ErrNotV2 = errors.New("not using cgroups2") + +// CGroups2 provides access to cgroups data for systems using cgroups2. +type CGroups2 struct { + mountPoint string + groupPath string + cpuMaxFile string +} + +// NewCGroups2ForCurrentProcess builds a CGroups2 for the current process. +// +// This returns ErrNotV2 if the system is not using cgroups2. +func NewCGroups2ForCurrentProcess() (*CGroups2, error) { + return newCGroups2From(_procPathMountInfo, _procPathCGroup) +} + +func newCGroups2From(mountInfoPath, procPathCGroup string) (*CGroups2, error) { + isV2, err := isCGroupV2(mountInfoPath) + if err != nil { + return nil, err + } + + if !isV2 { + return nil, ErrNotV2 + } + + subsystems, err := parseCGroupSubsystems(procPathCGroup) + if err != nil { + return nil, err + } + + // Find v2 subsystem by looking for the `0` id + var v2subsys *CGroupSubsys + for _, subsys := range subsystems { + if subsys.ID == 0 { + v2subsys = subsys + break + } + } + + if v2subsys == nil { + return nil, ErrNotV2 + } + + return &CGroups2{ + mountPoint: _cgroupv2MountPoint, + groupPath: v2subsys.Name, + cpuMaxFile: _cgroupv2CPUMax, + }, nil +} + +func isCGroupV2(procPathMountInfo string) (bool, error) { + var ( + isV2 bool + newMountPoint = func(mp *MountPoint) error { + isV2 = isV2 || (mp.FSType == _cgroupv2FSType && mp.MountPoint == _cgroupv2MountPoint) + return nil + } + ) + + if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { + return false, err + } + + return isV2, nil +} + +// CPUQuota returns the CPU quota applied with the CPU cgroup2 controller. +// It is a result of reading cpu quota and period from cpu.max file. +// It will return `cpu.max / cpu.period`. If cpu.max is set to max, it returns +// (-1, false, nil) +func (cg *CGroups2) CPUQuota() (float64, bool, error) { + cpuMaxParams, err := os.Open(path.Join(cg.mountPoint, cg.groupPath, cg.cpuMaxFile)) + if err != nil { + if os.IsNotExist(err) { + return -1, false, nil + } + return -1, false, err + } + defer cpuMaxParams.Close() + + scanner := bufio.NewScanner(cpuMaxParams) + if scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) == 0 || len(fields) > 2 { + return -1, false, fmt.Errorf("invalid format") + } + + if fields[_cgroupv2CPUMaxQuotaIndex] == _cgroupV2CPUMaxQuotaMax { + return -1, false, nil + } + + max, err := strconv.Atoi(fields[_cgroupv2CPUMaxQuotaIndex]) + if err != nil { + return -1, false, err + } + + var period int + if len(fields) == 1 { + period = _cgroupV2CPUMaxDefaultPeriod + } else { + period, err = strconv.Atoi(fields[_cgroupv2CPUMaxPeriodIndex]) + if err != nil { + return -1, false, err + } + + if period == 0 { + return -1, false, errors.New("zero value for period is not allowed") + } + } + + return float64(max) / float64(period), true, nil + } + + if err := scanner.Err(); err != nil { + return -1, false, err + } + + return 0, false, io.ErrUnexpectedEOF +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go new file mode 100644 index 000000000..113555f63 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package cgroups provides utilities to access Linux control group (CGroups) +// parameters (CPU quota, for example) for a given process. +package cgroups diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go new file mode 100644 index 000000000..94ac75a46 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go @@ -0,0 +1,52 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import "fmt" + +type cgroupSubsysFormatInvalidError struct { + line string +} + +type mountPointFormatInvalidError struct { + line string +} + +type pathNotExposedFromMountPointError struct { + mountPoint string + root string + path string +} + +func (err cgroupSubsysFormatInvalidError) Error() string { + return fmt.Sprintf("invalid format for CGroupSubsys: %q", err.line) +} + +func (err mountPointFormatInvalidError) Error() string { + return fmt.Sprintf("invalid format for MountPoint: %q", err.line) +} + +func (err pathNotExposedFromMountPointError) Error() string { + return fmt.Sprintf("path %q is not a descendant of mount point root %q and cannot be exposed from %q", err.path, err.root, err.mountPoint) +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go new file mode 100644 index 000000000..f3877f78a --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go @@ -0,0 +1,171 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "os" + "path/filepath" + "strconv" + "strings" +) + +const ( + _mountInfoSep = " " + _mountInfoOptsSep = "," + _mountInfoOptionalFieldsSep = "-" +) + +const ( + _miFieldIDMountID = iota + _miFieldIDParentID + _miFieldIDDeviceID + _miFieldIDRoot + _miFieldIDMountPoint + _miFieldIDOptions + _miFieldIDOptionalFields + + _miFieldCountFirstHalf +) + +const ( + _miFieldOffsetFSType = iota + _miFieldOffsetMountSource + _miFieldOffsetSuperOptions + + _miFieldCountSecondHalf +) + +const _miFieldCountMin = _miFieldCountFirstHalf + _miFieldCountSecondHalf + +// MountPoint is the data structure for the mount points in +// `/proc/$PID/mountinfo`. See also proc(5) for more information. +type MountPoint struct { + MountID int + ParentID int + DeviceID string + Root string + MountPoint string + Options []string + OptionalFields []string + FSType string + MountSource string + SuperOptions []string +} + +// NewMountPointFromLine parses a line read from `/proc/$PID/mountinfo` and +// returns a new *MountPoint. +func NewMountPointFromLine(line string) (*MountPoint, error) { + fields := strings.Split(line, _mountInfoSep) + + if len(fields) < _miFieldCountMin { + return nil, mountPointFormatInvalidError{line} + } + + mountID, err := strconv.Atoi(fields[_miFieldIDMountID]) + if err != nil { + return nil, err + } + + parentID, err := strconv.Atoi(fields[_miFieldIDParentID]) + if err != nil { + return nil, err + } + + for i, field := range fields[_miFieldIDOptionalFields:] { + if field == _mountInfoOptionalFieldsSep { + // End of optional fields. + fsTypeStart := _miFieldIDOptionalFields + i + 1 + + // Now we know where the optional fields end, split the line again with a + // limit to avoid issues with spaces in super options as present on WSL. + fields = strings.SplitN(line, _mountInfoSep, fsTypeStart+_miFieldCountSecondHalf) + if len(fields) != fsTypeStart+_miFieldCountSecondHalf { + return nil, mountPointFormatInvalidError{line} + } + + miFieldIDFSType := _miFieldOffsetFSType + fsTypeStart + miFieldIDMountSource := _miFieldOffsetMountSource + fsTypeStart + miFieldIDSuperOptions := _miFieldOffsetSuperOptions + fsTypeStart + + return &MountPoint{ + MountID: mountID, + ParentID: parentID, + DeviceID: fields[_miFieldIDDeviceID], + Root: fields[_miFieldIDRoot], + MountPoint: fields[_miFieldIDMountPoint], + Options: strings.Split(fields[_miFieldIDOptions], _mountInfoOptsSep), + OptionalFields: fields[_miFieldIDOptionalFields:(fsTypeStart - 1)], + FSType: fields[miFieldIDFSType], + MountSource: fields[miFieldIDMountSource], + SuperOptions: strings.Split(fields[miFieldIDSuperOptions], _mountInfoOptsSep), + }, nil + } + } + + return nil, mountPointFormatInvalidError{line} +} + +// Translate converts an absolute path inside the *MountPoint's file system to +// the host file system path in the mount namespace the *MountPoint belongs to. +func (mp *MountPoint) Translate(absPath string) (string, error) { + relPath, err := filepath.Rel(mp.Root, absPath) + + if err != nil { + return "", err + } + if relPath == ".." || strings.HasPrefix(relPath, "../") { + return "", pathNotExposedFromMountPointError{ + mountPoint: mp.MountPoint, + root: mp.Root, + path: absPath, + } + } + + return filepath.Join(mp.MountPoint, relPath), nil +} + +// parseMountInfo parses procPathMountInfo (usually at `/proc/$PID/mountinfo`) +// and yields parsed *MountPoint into newMountPoint. +func parseMountInfo(procPathMountInfo string, newMountPoint func(*MountPoint) error) error { + mountInfoFile, err := os.Open(procPathMountInfo) + if err != nil { + return err + } + defer mountInfoFile.Close() + + scanner := bufio.NewScanner(mountInfoFile) + + for scanner.Scan() { + mountPoint, err := NewMountPointFromLine(scanner.Text()) + if err != nil { + return err + } + if err := newMountPoint(mountPoint); err != nil { + return err + } + } + + return scanner.Err() +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go new file mode 100644 index 000000000..cddc3eaec --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go @@ -0,0 +1,103 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "os" + "strconv" + "strings" +) + +const ( + _cgroupSep = ":" + _cgroupSubsysSep = "," +) + +const ( + _csFieldIDID = iota + _csFieldIDSubsystems + _csFieldIDName + _csFieldCount +) + +// CGroupSubsys represents the data structure for entities in +// `/proc/$PID/cgroup`. See also proc(5) for more information. +type CGroupSubsys struct { + ID int + Subsystems []string + Name string +} + +// NewCGroupSubsysFromLine returns a new *CGroupSubsys by parsing a string in +// the format of `/proc/$PID/cgroup` +func NewCGroupSubsysFromLine(line string) (*CGroupSubsys, error) { + fields := strings.SplitN(line, _cgroupSep, _csFieldCount) + + if len(fields) != _csFieldCount { + return nil, cgroupSubsysFormatInvalidError{line} + } + + id, err := strconv.Atoi(fields[_csFieldIDID]) + if err != nil { + return nil, err + } + + cgroup := &CGroupSubsys{ + ID: id, + Subsystems: strings.Split(fields[_csFieldIDSubsystems], _cgroupSubsysSep), + Name: fields[_csFieldIDName], + } + + return cgroup, nil +} + +// parseCGroupSubsystems parses procPathCGroup (usually at `/proc/$PID/cgroup`) +// and returns a new map[string]*CGroupSubsys. +func parseCGroupSubsystems(procPathCGroup string) (map[string]*CGroupSubsys, error) { + cgroupFile, err := os.Open(procPathCGroup) + if err != nil { + return nil, err + } + defer cgroupFile.Close() + + scanner := bufio.NewScanner(cgroupFile) + subsystems := make(map[string]*CGroupSubsys) + + for scanner.Scan() { + cgroup, err := NewCGroupSubsysFromLine(scanner.Text()) + if err != nil { + return nil, err + } + for _, subsys := range cgroup.Subsystems { + subsystems[subsys] = cgroup + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return subsystems, nil +} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go new file mode 100644 index 000000000..f9057fd27 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go @@ -0,0 +1,75 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package runtime + +import ( + "errors" + + cg "go.uber.org/automaxprocs/internal/cgroups" +) + +// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process +// to a valid GOMAXPROCS value. The quota is converted from float to int using round. +// If round == nil, DefaultRoundFunc is used. +func CPUQuotaToGOMAXPROCS(minValue int, round func(v float64) int) (int, CPUQuotaStatus, error) { + if round == nil { + round = DefaultRoundFunc + } + cgroups, err := _newQueryer() + if err != nil { + return -1, CPUQuotaUndefined, err + } + + quota, defined, err := cgroups.CPUQuota() + if !defined || err != nil { + return -1, CPUQuotaUndefined, err + } + + maxProcs := round(quota) + if minValue > 0 && maxProcs < minValue { + return minValue, CPUQuotaMinUsed, nil + } + return maxProcs, CPUQuotaUsed, nil +} + +type queryer interface { + CPUQuota() (float64, bool, error) +} + +var ( + _newCgroups2 = cg.NewCGroups2ForCurrentProcess + _newCgroups = cg.NewCGroupsForCurrentProcess + _newQueryer = newQueryer +) + +func newQueryer() (queryer, error) { + cgroups, err := _newCgroups2() + if err == nil { + return cgroups, nil + } + if errors.Is(err, cg.ErrNotV2) { + return _newCgroups() + } + return nil, err +} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go new file mode 100644 index 000000000..e74701508 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go @@ -0,0 +1,31 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build !linux +// +build !linux + +package runtime + +// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process +// to a valid GOMAXPROCS value. This is Linux-specific and not supported in the +// current OS. +func CPUQuotaToGOMAXPROCS(_ int, _ func(v float64) int) (int, CPUQuotaStatus, error) { + return -1, CPUQuotaUndefined, nil +} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go b/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go new file mode 100644 index 000000000..f8a2834ac --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package runtime + +import "math" + +// CPUQuotaStatus presents the status of how CPU quota is used +type CPUQuotaStatus int + +const ( + // CPUQuotaUndefined is returned when CPU quota is undefined + CPUQuotaUndefined CPUQuotaStatus = iota + // CPUQuotaUsed is returned when a valid CPU quota can be used + CPUQuotaUsed + // CPUQuotaMinUsed is returned when CPU quota is smaller than the min value + CPUQuotaMinUsed +) + +// DefaultRoundFunc is the default function to convert CPU quota from float to int. It rounds the value down (floor). +func DefaultRoundFunc(v float64) int { + return int(math.Floor(v)) +} diff --git a/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go b/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go new file mode 100644 index 000000000..e561fe60b --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go @@ -0,0 +1,139 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package maxprocs lets Go programs easily configure runtime.GOMAXPROCS to +// match the configured Linux CPU quota. Unlike the top-level automaxprocs +// package, it lets the caller configure logging and handle errors. +package maxprocs // import "go.uber.org/automaxprocs/maxprocs" + +import ( + "os" + "runtime" + + iruntime "go.uber.org/automaxprocs/internal/runtime" +) + +const _maxProcsKey = "GOMAXPROCS" + +func currentMaxProcs() int { + return runtime.GOMAXPROCS(0) +} + +type config struct { + printf func(string, ...interface{}) + procs func(int, func(v float64) int) (int, iruntime.CPUQuotaStatus, error) + minGOMAXPROCS int + roundQuotaFunc func(v float64) int +} + +func (c *config) log(fmt string, args ...interface{}) { + if c.printf != nil { + c.printf(fmt, args...) + } +} + +// An Option alters the behavior of Set. +type Option interface { + apply(*config) +} + +// Logger uses the supplied printf implementation for log output. By default, +// Set doesn't log anything. +func Logger(printf func(string, ...interface{})) Option { + return optionFunc(func(cfg *config) { + cfg.printf = printf + }) +} + +// Min sets the minimum GOMAXPROCS value that will be used. +// Any value below 1 is ignored. +func Min(n int) Option { + return optionFunc(func(cfg *config) { + if n >= 1 { + cfg.minGOMAXPROCS = n + } + }) +} + +// RoundQuotaFunc sets the function that will be used to covert the CPU quota from float to int. +func RoundQuotaFunc(rf func(v float64) int) Option { + return optionFunc(func(cfg *config) { + cfg.roundQuotaFunc = rf + }) +} + +type optionFunc func(*config) + +func (of optionFunc) apply(cfg *config) { of(cfg) } + +// Set GOMAXPROCS to match the Linux container CPU quota (if any), returning +// any error encountered and an undo function. +// +// Set is a no-op on non-Linux systems and in Linux environments without a +// configured CPU quota. +func Set(opts ...Option) (func(), error) { + cfg := &config{ + procs: iruntime.CPUQuotaToGOMAXPROCS, + roundQuotaFunc: iruntime.DefaultRoundFunc, + minGOMAXPROCS: 1, + } + for _, o := range opts { + o.apply(cfg) + } + + undoNoop := func() { + cfg.log("maxprocs: No GOMAXPROCS change to reset") + } + + // Honor the GOMAXPROCS environment variable if present. Otherwise, amend + // `runtime.GOMAXPROCS()` with the current process' CPU quota if the OS is + // Linux, and guarantee a minimum value of 1. The minimum guaranteed value + // can be overridden using `maxprocs.Min()`. + if max, exists := os.LookupEnv(_maxProcsKey); exists { + cfg.log("maxprocs: Honoring GOMAXPROCS=%q as set in environment", max) + return undoNoop, nil + } + + maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS, cfg.roundQuotaFunc) + if err != nil { + return undoNoop, err + } + + if status == iruntime.CPUQuotaUndefined { + cfg.log("maxprocs: Leaving GOMAXPROCS=%v: CPU quota undefined", currentMaxProcs()) + return undoNoop, nil + } + + prev := currentMaxProcs() + undo := func() { + cfg.log("maxprocs: Resetting GOMAXPROCS to %v", prev) + runtime.GOMAXPROCS(prev) + } + + switch status { + case iruntime.CPUQuotaMinUsed: + cfg.log("maxprocs: Updating GOMAXPROCS=%v: using minimum allowed GOMAXPROCS", maxProcs) + case iruntime.CPUQuotaUsed: + cfg.log("maxprocs: Updating GOMAXPROCS=%v: determined from CPU quota", maxProcs) + } + + runtime.GOMAXPROCS(maxProcs) + return undo, nil +} diff --git a/vendor/go.uber.org/automaxprocs/maxprocs/version.go b/vendor/go.uber.org/automaxprocs/maxprocs/version.go new file mode 100644 index 000000000..cc7fc5aee --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/maxprocs/version.go @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package maxprocs + +// Version is the current package version. +const Version = "1.6.0" diff --git a/vendor/modules.txt b/vendor/modules.txt index 304f4e45b..b6c6b3290 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -101,6 +101,9 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp # github.com/IBM/sarama v1.45.0 ## explicit; go 1.21 github.com/IBM/sarama +# github.com/KimMachineGun/automemlimit v0.7.1 +## explicit; go 1.22.0 +github.com/KimMachineGun/automemlimit/memlimit # github.com/Microsoft/go-winio v0.6.2 ## explicit; go 1.21 github.com/Microsoft/go-winio @@ -877,6 +880,10 @@ github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancing ## explicit; go 1.23.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter/internal/metadata +# github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension v0.121.0 +## explicit; go 1.23.0 +github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension +github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension/internal/metadata # github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.121.0 ## explicit; go 1.23.0 github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension @@ -1352,6 +1359,9 @@ github.com/openzipkin/zipkin-go/reporter # github.com/ovh/go-ovh v1.6.0 ## explicit; go 1.18 github.com/ovh/go-ovh/ovh +# github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 +## explicit; go 1.16 +github.com/pbnjay/memory # github.com/pelletier/go-toml/v2 v2.2.2 ## explicit; go 1.16 github.com/pelletier/go-toml/v2 @@ -1476,6 +1486,12 @@ github.com/prometheus/prometheus/util/treecache # github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 ## explicit github.com/rcrowley/go-metrics +# github.com/rdforte/gomaxecs v1.1.1 +## explicit; go 1.22.4 +github.com/rdforte/gomaxecs/internal/client +github.com/rdforte/gomaxecs/internal/config +github.com/rdforte/gomaxecs/internal/task +github.com/rdforte/gomaxecs/maxprocs # github.com/redis/go-redis/v9 v9.7.1 ## explicit; go 1.18 github.com/redis/go-redis/v9 @@ -2192,6 +2208,11 @@ go.opentelemetry.io/proto/otlp/trace/v1 # go.uber.org/atomic v1.11.0 ## explicit; go 1.18 go.uber.org/atomic +# go.uber.org/automaxprocs v1.6.0 +## explicit; go 1.20 +go.uber.org/automaxprocs/internal/cgroups +go.uber.org/automaxprocs/internal/runtime +go.uber.org/automaxprocs/maxprocs # go.uber.org/goleak v1.3.0 ## explicit; go 1.20 go.uber.org/goleak