Merge pull request #6257 from olagacek/master
Update kubernetes dependencies to 1.29.0-alpha.3.
This commit is contained in:
		
						commit
						d4c6e2fc51
					
				| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
module k8s.io/autoscaler/cluster-autoscaler
 | 
			
		||||
 | 
			
		||||
go 1.21
 | 
			
		||||
go 1.21.3
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cloud.google.com/go/compute/metadata v0.2.3
 | 
			
		||||
| 
						 | 
				
			
			@ -18,48 +18,48 @@ require (
 | 
			
		|||
	github.com/gogo/protobuf v1.3.2
 | 
			
		||||
	github.com/golang/mock v1.6.0
 | 
			
		||||
	github.com/golang/protobuf v1.5.3
 | 
			
		||||
	github.com/google/go-cmp v0.5.9
 | 
			
		||||
	github.com/google/go-cmp v0.6.0
 | 
			
		||||
	github.com/google/go-querystring v1.0.0
 | 
			
		||||
	github.com/google/uuid v1.3.0
 | 
			
		||||
	github.com/jmespath/go-jmespath v0.4.0
 | 
			
		||||
	github.com/json-iterator/go v1.1.12
 | 
			
		||||
	github.com/onsi/ginkgo/v2 v2.11.0
 | 
			
		||||
	github.com/onsi/gomega v1.27.10
 | 
			
		||||
	github.com/onsi/ginkgo/v2 v2.13.0
 | 
			
		||||
	github.com/onsi/gomega v1.29.0
 | 
			
		||||
	github.com/pkg/errors v0.9.1
 | 
			
		||||
	github.com/prometheus/client_golang v1.16.0
 | 
			
		||||
	github.com/satori/go.uuid v1.2.0
 | 
			
		||||
	github.com/spf13/pflag v1.0.5
 | 
			
		||||
	github.com/stretchr/testify v1.8.4
 | 
			
		||||
	golang.org/x/crypto v0.13.0
 | 
			
		||||
	golang.org/x/net v0.15.0
 | 
			
		||||
	golang.org/x/oauth2 v0.8.0
 | 
			
		||||
	golang.org/x/sys v0.12.0
 | 
			
		||||
	google.golang.org/api v0.114.0
 | 
			
		||||
	google.golang.org/grpc v1.54.0
 | 
			
		||||
	golang.org/x/crypto v0.14.0
 | 
			
		||||
	golang.org/x/net v0.17.0
 | 
			
		||||
	golang.org/x/oauth2 v0.10.0
 | 
			
		||||
	golang.org/x/sys v0.13.0
 | 
			
		||||
	google.golang.org/api v0.126.0
 | 
			
		||||
	google.golang.org/grpc v1.58.3
 | 
			
		||||
	google.golang.org/protobuf v1.31.0
 | 
			
		||||
	gopkg.in/gcfg.v1 v1.2.3
 | 
			
		||||
	gopkg.in/yaml.v2 v2.4.0
 | 
			
		||||
	k8s.io/api v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/apimachinery v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/apiserver v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/client-go v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/cloud-provider v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/api v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/apimachinery v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/apiserver v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/client-go v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/cloud-provider v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/cloud-provider-aws v1.27.0
 | 
			
		||||
	k8s.io/code-generator v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/component-base v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/component-helpers v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/klog/v2 v2.100.1
 | 
			
		||||
	k8s.io/kubelet v0.29.0-alpha.1
 | 
			
		||||
	k8s.io/kubernetes v1.29.0-alpha.1
 | 
			
		||||
	k8s.io/code-generator v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/component-base v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/component-helpers v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/klog/v2 v2.110.1
 | 
			
		||||
	k8s.io/kubelet v0.29.0-alpha.3
 | 
			
		||||
	k8s.io/kubernetes v1.29.0-alpha.3
 | 
			
		||||
	k8s.io/legacy-cloud-providers v0.0.0
 | 
			
		||||
	k8s.io/utils v0.0.0-20230726121419-3b25d923346b
 | 
			
		||||
	sigs.k8s.io/cloud-provider-azure v1.28.0
 | 
			
		||||
	sigs.k8s.io/structured-merge-diff/v4 v4.3.0
 | 
			
		||||
	sigs.k8s.io/structured-merge-diff/v4 v4.4.1
 | 
			
		||||
	sigs.k8s.io/yaml v1.3.0
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cloud.google.com/go/compute v1.19.0 // indirect
 | 
			
		||||
	cloud.google.com/go/compute v1.23.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/mocks v0.4.2 // indirect
 | 
			
		||||
| 
						 | 
				
			
			@ -85,48 +85,46 @@ require (
 | 
			
		|||
	github.com/containerd/ttrpc v1.2.2 // indirect
 | 
			
		||||
	github.com/coreos/go-semver v0.3.1 // indirect
 | 
			
		||||
	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
 | 
			
		||||
	github.com/cyphar/filepath-securejoin v0.2.3 // indirect
 | 
			
		||||
	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
			
		||||
	github.com/dimchansky/utfbom v1.1.1 // indirect
 | 
			
		||||
	github.com/distribution/reference v0.5.0 // indirect
 | 
			
		||||
	github.com/docker/distribution v2.8.2+incompatible // indirect
 | 
			
		||||
	github.com/docker/go-units v0.5.0 // indirect
 | 
			
		||||
	github.com/emicklei/go-restful/v3 v3.10.2 // indirect
 | 
			
		||||
	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 | 
			
		||||
	github.com/euank/go-kmsg-parser v2.0.0+incompatible // indirect
 | 
			
		||||
	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
 | 
			
		||||
	github.com/felixge/httpsnoop v1.0.3 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.6.0 // indirect
 | 
			
		||||
	github.com/go-logr/logr v1.2.4 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.7.0 // indirect
 | 
			
		||||
	github.com/go-logr/logr v1.3.0 // indirect
 | 
			
		||||
	github.com/go-logr/stdr v1.2.2 // indirect
 | 
			
		||||
	github.com/go-logr/zapr v1.2.3 // indirect
 | 
			
		||||
	github.com/go-openapi/jsonpointer v0.19.6 // indirect
 | 
			
		||||
	github.com/go-openapi/jsonreference v0.20.2 // indirect
 | 
			
		||||
	github.com/go-openapi/swag v0.22.3 // indirect
 | 
			
		||||
	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
 | 
			
		||||
	github.com/godbus/dbus/v5 v5.0.6 // indirect
 | 
			
		||||
	github.com/godbus/dbus/v5 v5.1.0 // indirect
 | 
			
		||||
	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
 | 
			
		||||
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
			
		||||
	github.com/google/cadvisor v0.47.3 // indirect
 | 
			
		||||
	github.com/google/cel-go v0.17.6 // indirect
 | 
			
		||||
	github.com/google/cadvisor v0.48.1 // indirect
 | 
			
		||||
	github.com/google/cel-go v0.17.7 // indirect
 | 
			
		||||
	github.com/google/gnostic-models v0.6.8 // indirect
 | 
			
		||||
	github.com/google/gofuzz v1.2.0 // indirect
 | 
			
		||||
	github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
 | 
			
		||||
	github.com/google/s2a-go v0.1.7 // indirect
 | 
			
		||||
	github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
 | 
			
		||||
	github.com/googleapis/gax-go/v2 v2.7.1 // indirect
 | 
			
		||||
	github.com/googleapis/gax-go/v2 v2.11.0 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.5.0 // indirect
 | 
			
		||||
	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
 | 
			
		||||
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
 | 
			
		||||
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
 | 
			
		||||
	github.com/imdario/mergo v0.3.15 // indirect
 | 
			
		||||
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 | 
			
		||||
	github.com/josharian/intern v1.0.0 // indirect
 | 
			
		||||
	github.com/karrick/godirwalk v1.17.0 // indirect
 | 
			
		||||
	github.com/libopenstorage/openstorage v1.0.0 // indirect
 | 
			
		||||
	github.com/lithammer/dedent v1.1.0 // indirect
 | 
			
		||||
	github.com/mailru/easyjson v0.7.7 // indirect
 | 
			
		||||
	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 | 
			
		||||
	github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0 // indirect
 | 
			
		||||
	github.com/moby/ipvs v1.1.0 // indirect
 | 
			
		||||
	github.com/moby/spdystream v0.2.0 // indirect
 | 
			
		||||
	github.com/moby/sys/mountinfo v0.6.2 // indirect
 | 
			
		||||
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
			
		||||
| 
						 | 
				
			
			@ -153,47 +151,46 @@ require (
 | 
			
		|||
	github.com/vishvananda/netlink v1.1.0 // indirect
 | 
			
		||||
	github.com/vishvananda/netns v0.0.4 // indirect
 | 
			
		||||
	github.com/vmware/govmomi v0.30.6 // indirect
 | 
			
		||||
	go.etcd.io/etcd/api/v3 v3.5.9 // indirect
 | 
			
		||||
	go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
 | 
			
		||||
	go.etcd.io/etcd/client/v3 v3.5.9 // indirect
 | 
			
		||||
	go.etcd.io/etcd/api/v3 v3.5.10 // indirect
 | 
			
		||||
	go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
 | 
			
		||||
	go.etcd.io/etcd/client/v3 v3.5.10 // indirect
 | 
			
		||||
	go.opencensus.io v0.24.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel v1.14.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/metric v0.37.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/sdk v1.14.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/trace v1.14.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/proto/otlp v0.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.42.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel v1.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/metric v1.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/sdk v1.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/trace v1.19.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 | 
			
		||||
	go.uber.org/atomic v1.10.0 // indirect
 | 
			
		||||
	go.uber.org/multierr v1.11.0 // indirect
 | 
			
		||||
	go.uber.org/zap v1.24.0 // indirect
 | 
			
		||||
	golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
 | 
			
		||||
	golang.org/x/mod v0.10.0 // indirect
 | 
			
		||||
	golang.org/x/mod v0.12.0 // indirect
 | 
			
		||||
	golang.org/x/sync v0.3.0 // indirect
 | 
			
		||||
	golang.org/x/term v0.12.0 // indirect
 | 
			
		||||
	golang.org/x/term v0.13.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.13.0 // indirect
 | 
			
		||||
	golang.org/x/time v0.3.0 // indirect
 | 
			
		||||
	golang.org/x/tools v0.9.3 // indirect
 | 
			
		||||
	golang.org/x/tools v0.12.0 // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
 | 
			
		||||
	gopkg.in/inf.v0 v0.9.1 // indirect
 | 
			
		||||
	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 | 
			
		||||
	gopkg.in/warnings.v0 v0.1.2 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
	k8s.io/apiextensions-apiserver v0.0.0 // indirect
 | 
			
		||||
	k8s.io/controller-manager v0.29.0-alpha.1 // indirect
 | 
			
		||||
	k8s.io/cri-api v0.29.0-alpha.1 // indirect
 | 
			
		||||
	k8s.io/controller-manager v0.29.0-alpha.3 // indirect
 | 
			
		||||
	k8s.io/cri-api v0.29.0-alpha.3 // indirect
 | 
			
		||||
	k8s.io/csi-translation-lib v0.27.0 // indirect
 | 
			
		||||
	k8s.io/dynamic-resource-allocation v0.0.0 // indirect
 | 
			
		||||
	k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect
 | 
			
		||||
	k8s.io/kms v0.29.0-alpha.1 // indirect
 | 
			
		||||
	k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f // indirect
 | 
			
		||||
	k8s.io/kms v0.29.0-alpha.3 // indirect
 | 
			
		||||
	k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
 | 
			
		||||
	k8s.io/kube-scheduler v0.0.0 // indirect
 | 
			
		||||
	k8s.io/kubectl v0.0.0 // indirect
 | 
			
		||||
	k8s.io/mount-utils v0.26.0-alpha.0 // indirect
 | 
			
		||||
| 
						 | 
				
			
			@ -207,62 +204,62 @@ replace github.com/digitalocean/godo => github.com/digitalocean/godo v1.27.0
 | 
			
		|||
 | 
			
		||||
replace github.com/rancher/go-rancher => github.com/rancher/go-rancher v0.1.0
 | 
			
		||||
 | 
			
		||||
replace k8s.io/api => k8s.io/api v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/api => k8s.io/api v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/apimachinery => k8s.io/apimachinery v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/apimachinery => k8s.io/apimachinery v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/apiserver => k8s.io/apiserver v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/apiserver => k8s.io/apiserver v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/cli-runtime => k8s.io/cli-runtime v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/cli-runtime => k8s.io/cli-runtime v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/client-go => k8s.io/client-go v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/client-go => k8s.io/client-go v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/cloud-provider => k8s.io/cloud-provider v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/cloud-provider => k8s.io/cloud-provider v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/code-generator => k8s.io/code-generator v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/code-generator => k8s.io/code-generator v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/component-base => k8s.io/component-base v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/component-base => k8s.io/component-base v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/component-helpers => k8s.io/component-helpers v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/component-helpers => k8s.io/component-helpers v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/controller-manager => k8s.io/controller-manager v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/controller-manager => k8s.io/controller-manager v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/cri-api => k8s.io/cri-api v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/cri-api => k8s.io/cri-api v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kube-proxy => k8s.io/kube-proxy v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kube-proxy => k8s.io/kube-proxy v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kubectl => k8s.io/kubectl v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kubectl => k8s.io/kubectl v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kubelet => k8s.io/kubelet v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kubelet => k8s.io/kubelet v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/metrics => k8s.io/metrics v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/metrics => k8s.io/metrics v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/mount-utils => k8s.io/mount-utils v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/mount-utils => k8s.io/mount-utils v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/sample-controller => k8s.io/sample-controller v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/sample-controller => k8s.io/sample-controller v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/kms => k8s.io/kms v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/kms => k8s.io/kms v0.29.0-alpha.3
 | 
			
		||||
 | 
			
		||||
replace k8s.io/endpointslice => k8s.io/endpointslice v0.29.0-alpha.1
 | 
			
		||||
replace k8s.io/endpointslice => k8s.io/endpointslice v0.29.0-alpha.3
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,21 +26,18 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD
 | 
			
		|||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 | 
			
		||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
 | 
			
		||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
 | 
			
		||||
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
 | 
			
		||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 | 
			
		||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 | 
			
		||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 | 
			
		||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
 | 
			
		||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
 | 
			
		||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
 | 
			
		||||
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
 | 
			
		||||
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
 | 
			
		||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
 | 
			
		||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
 | 
			
		||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
 | 
			
		||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 | 
			
		||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 | 
			
		||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 | 
			
		||||
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
 | 
			
		||||
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
 | 
			
		||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 | 
			
		||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 | 
			
		||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +51,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
 | 
			
		|||
github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 | 
			
		||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
 | 
			
		||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 | 
			
		||||
github.com/Azure/go-autorest/autorest v0.11.4/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +92,6 @@ github.com/GoogleCloudPlatform/k8s-cloud-provider v1.18.1-0.20220218231025-f1181
 | 
			
		|||
github.com/GoogleCloudPlatform/k8s-cloud-provider v1.18.1-0.20220218231025-f11817397a1b/go.mod h1:FNj4KYEAAHfYu68kRYolGoxkaJn+6mdEsaM12VTwuI0=
 | 
			
		||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1/xq4SEg5z+VpTgjmNeHwPGRQl7takDI=
 | 
			
		||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
 | 
			
		||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
 | 
			
		||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
 | 
			
		||||
| 
						 | 
				
			
			@ -106,10 +101,7 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
 | 
			
		|||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 | 
			
		||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 | 
			
		||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
 | 
			
		||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +112,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
 | 
			
		|||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.241 h1:D3KycZq3HjhmjYGzvTcmX/Ztf/KNmsfTmdDuKdnzZKo=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.241/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 | 
			
		||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 | 
			
		||||
| 
						 | 
				
			
			@ -129,8 +120,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
 | 
			
		|||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
			
		||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
			
		||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
 | 
			
		||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 | 
			
		||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
 | 
			
		||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
 | 
			
		||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 | 
			
		||||
| 
						 | 
				
			
			@ -138,8 +127,6 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy
 | 
			
		|||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 | 
			
		||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
			
		||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8=
 | 
			
		||||
| 
						 | 
				
			
			@ -148,19 +135,15 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 | 
			
		|||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
			
		||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
			
		||||
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
 | 
			
		||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
 | 
			
		||||
github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4=
 | 
			
		||||
github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/container-storage-interface/spec v1.8.0 h1:D0vhF3PLIZwlwZEf2eNbpujGCNwspwTYf2idJRJx4xI=
 | 
			
		||||
github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0=
 | 
			
		||||
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
 | 
			
		||||
| 
						 | 
				
			
			@ -186,7 +169,6 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
 | 
			
		|||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 | 
			
		||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 | 
			
		||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
 | 
			
		||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
| 
						 | 
				
			
			@ -194,9 +176,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
 | 
			
		|||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 | 
			
		||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
			
		||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
			
		||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 | 
			
		||||
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
 | 
			
		||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 | 
			
		||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
 | 
			
		||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
| 
						 | 
				
			
			@ -211,11 +192,10 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK
 | 
			
		|||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 | 
			
		||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
 | 
			
		||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
 | 
			
		||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
			
		||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
 | 
			
		||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
			
		||||
github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog=
 | 
			
		||||
github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
			
		||||
github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE=
 | 
			
		||||
github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
			
		||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
			
		||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
			
		||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
| 
						 | 
				
			
			@ -224,8 +204,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
 | 
			
		|||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
			
		||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 | 
			
		||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 | 
			
		||||
github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
 | 
			
		||||
github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 | 
			
		||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
 | 
			
		||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 | 
			
		||||
| 
						 | 
				
			
			@ -233,9 +213,9 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
 | 
			
		|||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 | 
			
		||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
			
		||||
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
 | 
			
		||||
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
 | 
			
		||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible h1:cHD53+PLQuuQyLZeriD1V/esuG4MuU0Pjs5y6iknohY=
 | 
			
		||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
 | 
			
		||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
 | 
			
		||||
| 
						 | 
				
			
			@ -247,26 +227,20 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
 | 
			
		|||
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
 | 
			
		||||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 | 
			
		||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
			
		||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
 | 
			
		||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 | 
			
		||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 | 
			
		||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 | 
			
		||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 | 
			
		||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 | 
			
		||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 | 
			
		||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 | 
			
		||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
 | 
			
		||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 | 
			
		||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 | 
			
		||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 | 
			
		||||
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
 | 
			
		||||
| 
						 | 
				
			
			@ -282,11 +256,10 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
 | 
			
		|||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
 | 
			
		||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 | 
			
		||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 | 
			
		||||
| 
						 | 
				
			
			@ -295,8 +268,8 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
 | 
			
		|||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 | 
			
		||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
 | 
			
		||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
 | 
			
		||||
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
 | 
			
		||||
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
| 
						 | 
				
			
			@ -337,10 +310,10 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
 | 
			
		|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
 | 
			
		||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
 | 
			
		||||
github.com/google/cadvisor v0.47.3 h1:5XKTHBduWlBjmgw07uwEiC+Xa/FRd0MZI37oqlTagO0=
 | 
			
		||||
github.com/google/cadvisor v0.47.3/go.mod h1:iJdTjcjyKHjLCf7OSTzwP5GxdfrkPusw2x5bwGvuLUw=
 | 
			
		||||
github.com/google/cel-go v0.17.6 h1:QDvHTIJunIsbgN8yVukx0HGnsqVLSY6xGqo+17IjIyM=
 | 
			
		||||
github.com/google/cel-go v0.17.6/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
 | 
			
		||||
github.com/google/cadvisor v0.48.1 h1:eyYTxKBd+KxI1kh6rst4JSTLUhfHQM34qGpp+0AMlSg=
 | 
			
		||||
github.com/google/cadvisor v0.48.1/go.mod h1:ZkYbiiVdyoqBmI2ahZI8GlmirT78OAOER0z4EQugkxQ=
 | 
			
		||||
github.com/google/cel-go v0.17.7 h1:6ebJFzu1xO2n7TLtN+UBqShGBhlD85bhvglh5DpcfqQ=
 | 
			
		||||
github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
 | 
			
		||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
 | 
			
		||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
| 
						 | 
				
			
			@ -355,9 +328,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 | 
			
		|||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
			
		||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
			
		||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 | 
			
		||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
| 
						 | 
				
			
			@ -384,6 +357,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
 | 
			
		|||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
 | 
			
		||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
 | 
			
		||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
 | 
			
		||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
			
		||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
| 
						 | 
				
			
			@ -393,8 +368,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
 | 
			
		|||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
			
		||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 | 
			
		||||
| 
						 | 
				
			
			@ -407,9 +382,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
 | 
			
		|||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
| 
						 | 
				
			
			@ -429,23 +403,17 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q
 | 
			
		|||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
 | 
			
		||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 | 
			
		||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 | 
			
		||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
			
		||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 | 
			
		||||
github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
 | 
			
		||||
github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 | 
			
		||||
| 
						 | 
				
			
			@ -464,7 +432,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
 | 
			
		|||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 | 
			
		||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 | 
			
		||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
 | 
			
		||||
| 
						 | 
				
			
			@ -472,50 +439,39 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go
 | 
			
		|||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/moby/ipvs v1.1.0 h1:ONN4pGaZQgAx+1Scz5RvWV4Q7Gb+mvfRh3NsPS+1XQQ=
 | 
			
		||||
github.com/moby/ipvs v1.1.0/go.mod h1:4VJMWuf098bsUMmZEiD4Tjk/O7mOn3l1PTD3s4OoYAs=
 | 
			
		||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
 | 
			
		||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
 | 
			
		||||
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
 | 
			
		||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
 | 
			
		||||
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
 | 
			
		||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 | 
			
		||||
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb h1:e+l77LJOEqXTIQihQJVkA6ZxPOUmfPM5e4H7rcpgtSk=
 | 
			
		||||
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 | 
			
		||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 | 
			
		||||
github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
 | 
			
		||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
 | 
			
		||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 | 
			
		||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
 | 
			
		||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 | 
			
		||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
 | 
			
		||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
 | 
			
		||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
 | 
			
		||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
 | 
			
		||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
 | 
			
		||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
 | 
			
		||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 | 
			
		||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 | 
			
		||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
 | 
			
		||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 | 
			
		||||
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
 | 
			
		||||
github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM=
 | 
			
		||||
github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78 h1:R5M2qXZiK/mWPMT4VldCOiSL9HIAMuxQZWdG0CSM5+4=
 | 
			
		||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 | 
			
		||||
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
 | 
			
		||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
 | 
			
		||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
| 
						 | 
				
			
			@ -527,36 +483,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 | 
			
		|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
			
		||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 | 
			
		||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 | 
			
		||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 | 
			
		||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 | 
			
		||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
 | 
			
		||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
 | 
			
		||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
 | 
			
		||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
 | 
			
		||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 | 
			
		||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
 | 
			
		||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
 | 
			
		||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
 | 
			
		||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
 | 
			
		||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 | 
			
		||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 | 
			
		||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 | 
			
		||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
 | 
			
		||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
 | 
			
		||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
 | 
			
		||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
			
		||||
| 
						 | 
				
			
			@ -571,14 +511,10 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
 | 
			
		|||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
			
		||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
 | 
			
		||||
github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY=
 | 
			
		||||
github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
 | 
			
		||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 | 
			
		||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
			
		||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
			
		||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 | 
			
		||||
| 
						 | 
				
			
			@ -588,7 +524,6 @@ github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
 | 
			
		|||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
 | 
			
		||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 | 
			
		||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
 | 
			
		||||
| 
						 | 
				
			
			@ -623,7 +558,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
 | 
			
		|||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
 | 
			
		||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
			
		||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 | 
			
		||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 | 
			
		||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
 | 
			
		||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 | 
			
		||||
| 
						 | 
				
			
			@ -642,22 +576,22 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
 | 
			
		|||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
			
		||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
 | 
			
		||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
 | 
			
		||||
go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs=
 | 
			
		||||
go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k=
 | 
			
		||||
go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE=
 | 
			
		||||
go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4=
 | 
			
		||||
go.etcd.io/etcd/client/v2 v2.305.9 h1:YZ2OLi0OvR0H75AcgSUajjd5uqKDKocQUqROTG11jIo=
 | 
			
		||||
go.etcd.io/etcd/client/v2 v2.305.9/go.mod h1:0NBdNx9wbxtEQLwAQtrDHwx58m02vXpDcgSYI2seohQ=
 | 
			
		||||
go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E=
 | 
			
		||||
go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
 | 
			
		||||
go.etcd.io/etcd/pkg/v3 v3.5.9 h1:6R2jg/aWd/zB9+9JxmijDKStGJAPFsX3e6BeJkMi6eQ=
 | 
			
		||||
go.etcd.io/etcd/pkg/v3 v3.5.9/go.mod h1:BZl0SAShQFk0IpLWR78T/+pyt8AruMHhTNNX73hkNVY=
 | 
			
		||||
go.etcd.io/etcd/raft/v3 v3.5.9 h1:ZZ1GIHoUlHsn0QVqiRysAm3/81Xx7+i2d7nSdWxlOiI=
 | 
			
		||||
go.etcd.io/etcd/raft/v3 v3.5.9/go.mod h1:WnFkqzFdZua4LVlVXQEGhmooLeyS7mqzS4Pf4BCVqXg=
 | 
			
		||||
go.etcd.io/etcd/server/v3 v3.5.9 h1:vomEmmxeztLtS5OEH7d0hBAg4cjVIu9wXuNzUZx2ZA0=
 | 
			
		||||
go.etcd.io/etcd/server/v3 v3.5.9/go.mod h1:GgI1fQClQCFIzuVjlvdbMxNbnISt90gdfYyqiAIt65g=
 | 
			
		||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
 | 
			
		||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
 | 
			
		||||
go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
 | 
			
		||||
go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI=
 | 
			
		||||
go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0=
 | 
			
		||||
go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U=
 | 
			
		||||
go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4=
 | 
			
		||||
go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA=
 | 
			
		||||
go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao=
 | 
			
		||||
go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc=
 | 
			
		||||
go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM=
 | 
			
		||||
go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs=
 | 
			
		||||
go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA=
 | 
			
		||||
go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc=
 | 
			
		||||
go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg=
 | 
			
		||||
go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo=
 | 
			
		||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
			
		||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
			
		||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 | 
			
		||||
| 
						 | 
				
			
			@ -667,31 +601,29 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 | 
			
		|||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 | 
			
		||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 | 
			
		||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.35.0 h1:KQjX0qQ8H21oBUAvFp4ZLKJMMLIluONvSPDAFIGmX58=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.35.0/go.mod h1:DQYkU9srMFqLUTVA/7/WlRHdnYDB7wyMMlle2ktMjfI=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0/go.mod h1:UMklln0+MRhZC4e3PwmN3pCtq4DyIadWw4yikh6bNrw=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI=
 | 
			
		||||
go.opentelemetry.io/contrib/propagators/b3 v1.10.0 h1:6AD2VV8edRdEYNaD8cNckpzgdMLU2kbV9OYyxt2kvCg=
 | 
			
		||||
go.opentelemetry.io/contrib/propagators/b3 v1.10.0/go.mod h1:oxvamQ/mTDFQVugml/uFS59+aEUnFLhmd1wsG+n5MOE=
 | 
			
		||||
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
 | 
			
		||||
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBMRdXyFstOwH028U0sVf+AvukSGhF0g8+dmNG8=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0/go.mod h1:HrbCVv40OOLTABmOn1ZWty6CHXkU8DK/Urc43tHug70=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM=
 | 
			
		||||
go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs=
 | 
			
		||||
go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.42.0 h1:Z6SbqeRZAl2OczfkFOqLx1BeYBDYehNjEnqluD7581Y=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.42.0/go.mod h1:XiglO+8SPMqM3Mqh5/rtxR1VHc63o8tb38QrU6tm4mU=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48=
 | 
			
		||||
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo=
 | 
			
		||||
go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc=
 | 
			
		||||
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
 | 
			
		||||
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
 | 
			
		||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 | 
			
		||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
 | 
			
		||||
| 
						 | 
				
			
			@ -719,8 +651,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
 | 
			
		|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
			
		||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
 | 
			
		||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
 | 
			
		||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
 | 
			
		||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
 | 
			
		||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
| 
						 | 
				
			
			@ -759,8 +691,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		|||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
			
		||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
 | 
			
		||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
			
		||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
 | 
			
		||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
| 
						 | 
				
			
			@ -773,7 +705,6 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
 | 
			
		|||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
| 
						 | 
				
			
			@ -795,22 +726,17 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
 | 
			
		|||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
			
		||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
			
		||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 | 
			
		||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
			
		||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 | 
			
		||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 | 
			
		||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 | 
			
		||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
 | 
			
		||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
 | 
			
		||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 | 
			
		||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
| 
						 | 
				
			
			@ -828,10 +754,8 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
 | 
			
		|||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 | 
			
		||||
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
 | 
			
		||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
 | 
			
		||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
 | 
			
		||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
| 
						 | 
				
			
			@ -843,7 +767,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 | 
			
		|||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
 | 
			
		||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 | 
			
		||||
| 
						 | 
				
			
			@ -854,20 +777,16 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
 | 
			
		|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
| 
						 | 
				
			
			@ -880,8 +799,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
			
		|||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
| 
						 | 
				
			
			@ -900,35 +817,27 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
			
		|||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 | 
			
		||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
 | 
			
		||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
 | 
			
		||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 | 
			
		||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
 | 
			
		||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
 | 
			
		||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 | 
			
		||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 | 
			
		||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
| 
						 | 
				
			
			@ -939,7 +848,6 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		|||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
			
		||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
			
		||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
			
		||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
			
		||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 | 
			
		||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 | 
			
		||||
| 
						 | 
				
			
			@ -1006,8 +914,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		|||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
			
		||||
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
 | 
			
		||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
 | 
			
		||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
 | 
			
		||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
| 
						 | 
				
			
			@ -1042,8 +950,8 @@ google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv
 | 
			
		|||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 | 
			
		||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
 | 
			
		||||
google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4=
 | 
			
		||||
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
 | 
			
		||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
 | 
			
		||||
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
 | 
			
		||||
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
| 
						 | 
				
			
			@ -1109,14 +1017,12 @@ google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEc
 | 
			
		|||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
			
		||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
| 
						 | 
				
			
			@ -1143,11 +1049,8 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
 | 
			
		|||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 | 
			
		||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 | 
			
		||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 | 
			
		||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 | 
			
		||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
			
		||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
 | 
			
		||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
 | 
			
		||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
 | 
			
		||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
 | 
			
		||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
 | 
			
		||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 | 
			
		||||
| 
						 | 
				
			
			@ -1162,8 +1065,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
 | 
			
		|||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
			
		||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
			
		||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
			
		||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
			
		||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
			
		||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
 | 
			
		||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
| 
						 | 
				
			
			@ -1186,17 +1087,13 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
 | 
			
		|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 | 
			
		||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 | 
			
		||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
| 
						 | 
				
			
			@ -1205,58 +1102,56 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 | 
			
		|||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
			
		||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 | 
			
		||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 | 
			
		||||
k8s.io/api v0.29.0-alpha.1 h1:2L/HVdzNvB6cEKWWzdz6ocGqUsWvxcnmzuacEhhs0Fk=
 | 
			
		||||
k8s.io/api v0.29.0-alpha.1/go.mod h1:eYxbep9dvaZTFXRWrbVTJWC/F3Y41J7K8xODAOnBYIg=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.29.0-alpha.1 h1:FW9gaevczuiNMa8/Njj2DQucyAlkBjarX457z1/taJg=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.29.0-alpha.1/go.mod h1:CAvUfnG5iRasgdu6N/MpuPLOBPh5T/j6ZSuRjs9L0Pk=
 | 
			
		||||
k8s.io/apimachinery v0.29.0-alpha.1 h1:d7cj9SSTDXLZxiAJ1g6Oib4Ya+2XTlNOePNYVuATjg4=
 | 
			
		||||
k8s.io/apimachinery v0.29.0-alpha.1/go.mod h1:ITRsvhyE2eLGBxgwRxs79z49RNNQh7HUqBvHCNIgEZc=
 | 
			
		||||
k8s.io/apiserver v0.29.0-alpha.1 h1:vgUBmwYy+abAVaO7yw7qWwH54QA2ZJ4FEye8FSirmC8=
 | 
			
		||||
k8s.io/apiserver v0.29.0-alpha.1/go.mod h1:VdI6MYOEzDjiJmtie0ZCCBiuFZ5gp7YBbcJXspCVkZU=
 | 
			
		||||
k8s.io/client-go v0.29.0-alpha.1 h1:V3iWjzFQSHcs4AOeBV3fL379SjEVfyU2KnNW0Q6ACII=
 | 
			
		||||
k8s.io/client-go v0.29.0-alpha.1/go.mod h1:5CnPkSLo3JBTEka4x0g46Lh06l08UnJf+1x7O+dqtEM=
 | 
			
		||||
k8s.io/cloud-provider v0.29.0-alpha.1 h1:tR/ujvDVQ48nxb2J/BpBoN4j2C6sIIO2i9i85Ns6TJc=
 | 
			
		||||
k8s.io/cloud-provider v0.29.0-alpha.1/go.mod h1:eQ39dx211gSMPmOffP2l1Emr/5xKYcicVHWX96/Yg6g=
 | 
			
		||||
k8s.io/api v0.29.0-alpha.3 h1:6QllqDvVn1jBNDXtFKzJg7mNQYYHYVANNBfP4z6Fu7Q=
 | 
			
		||||
k8s.io/api v0.29.0-alpha.3/go.mod h1:9zVQmGyL++Ki1RnuKUQ65LVgPP7WPq6pJwoQPfz9QPk=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.29.0-alpha.3 h1:D5JZVBDknOYpWTbK1+d5XQm0Y3sodG0Yxz1Mr0Wc44o=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.29.0-alpha.3/go.mod h1:bERujWZam5M3KWzrUWc2ujAQG1vxa3HVaSHO/OxIJjs=
 | 
			
		||||
k8s.io/apimachinery v0.29.0-alpha.3 h1:Y/VavRd57V5fliXV8M2Zr1Xyzi+raIhkDemWdGuuw6w=
 | 
			
		||||
k8s.io/apimachinery v0.29.0-alpha.3/go.mod h1:yFk3nwBh/jXlkMvRKH7BKtX7saT1lRmmGV6Ru0cTSUA=
 | 
			
		||||
k8s.io/apiserver v0.29.0-alpha.3 h1:xQlrw/AhtwGxMB7ox5sCItG63Njd8sXsHY701LizHdQ=
 | 
			
		||||
k8s.io/apiserver v0.29.0-alpha.3/go.mod h1:zsWRaMlVjUKc2a4Eyt+mlWwRwGg/2bXYwwqyBQ9C9Xk=
 | 
			
		||||
k8s.io/client-go v0.29.0-alpha.3 h1:934TEVX3ThG6yP/UWUM/1kBT+PzJQ4cxzyhXhA49fds=
 | 
			
		||||
k8s.io/client-go v0.29.0-alpha.3/go.mod h1:ZJK8Lomw8XdKVnmmvRWSSsJEWHv5RyWf8Hfnnv+p+AA=
 | 
			
		||||
k8s.io/cloud-provider v0.29.0-alpha.3 h1:BJvm/I9SB2TacWDZK6W4yByLBckeeEcJqL2btEQg3iE=
 | 
			
		||||
k8s.io/cloud-provider v0.29.0-alpha.3/go.mod h1:8SDiYEAyqr3HLS7gRTBGUpRvAGztXFGKJTK5nmXlLRg=
 | 
			
		||||
k8s.io/cloud-provider-aws v1.27.0 h1:PF8YrH8QcN6JoXB3Xxlaz84SBDYMPunJuCc0cPuCWXA=
 | 
			
		||||
k8s.io/cloud-provider-aws v1.27.0/go.mod h1:9vUb5mnVnReSRDBWcBxB1b0HOeEc472iOPmrnwpN9SA=
 | 
			
		||||
k8s.io/code-generator v0.29.0-alpha.1 h1:sfbxSLrwdLtpu2NOHcP3yMQ/bdBAZ76P2m2qN5iqzaM=
 | 
			
		||||
k8s.io/code-generator v0.29.0-alpha.1/go.mod h1:QS2putemLRnTUwX2Wljb8/qGw8zfABufWxcGY2EsY1c=
 | 
			
		||||
k8s.io/component-base v0.29.0-alpha.1 h1:MbCLImc1x7DyzfbGI2SyZfFfWT7Oyjuf3A1L36Rk1Rk=
 | 
			
		||||
k8s.io/component-base v0.29.0-alpha.1/go.mod h1:S5VlgkBTvlKvSI4g24CfDmZB+03FJ7aEqoxxKYSVQhU=
 | 
			
		||||
k8s.io/component-helpers v0.29.0-alpha.1 h1:yuPH1fSqWywOOc6ftqpXhmsqrE73xlbz7xGb2QPxEOY=
 | 
			
		||||
k8s.io/component-helpers v0.29.0-alpha.1/go.mod h1:MdRjCozF35nI3M6OCl7LBkSud/Et3m4vsLsmsL2kG3M=
 | 
			
		||||
k8s.io/controller-manager v0.29.0-alpha.1 h1:ZA6ayqClQCIenm7kNcj5DF3xQs/CdsgUtydFBgDRZ1w=
 | 
			
		||||
k8s.io/controller-manager v0.29.0-alpha.1/go.mod h1:qRztwHnGqF930MNAXejQzo6aebBv6wFtIgYlH/+deZ8=
 | 
			
		||||
k8s.io/cri-api v0.29.0-alpha.1 h1:XkxDGYHUSeRDj6ozr6SFaS4HDSmLM8C8fb1wMIB7amA=
 | 
			
		||||
k8s.io/cri-api v0.29.0-alpha.1/go.mod h1:PoSGzactM57YPD9hDk0Fsz3SdrMQV31ktb7QdnqXtCo=
 | 
			
		||||
k8s.io/csi-translation-lib v0.29.0-alpha.1 h1:dd7L6mBEUSe7DM3VmSskJyqRLvBVwaqWx71qmGwHe7g=
 | 
			
		||||
k8s.io/csi-translation-lib v0.29.0-alpha.1/go.mod h1:EZ3Jx2rDxoAANwvm2z0NE1PgHf8jwvnBUmi84G7nL14=
 | 
			
		||||
k8s.io/dynamic-resource-allocation v0.29.0-alpha.1 h1:ZpPumTIM51aY1w1obOQHIHNkVhlGBj4+DCa/lDYmesQ=
 | 
			
		||||
k8s.io/dynamic-resource-allocation v0.29.0-alpha.1/go.mod h1:As+JCGvjfiasaDPHbOg7MDfj7ANDHEIH93//cNeCovY=
 | 
			
		||||
k8s.io/code-generator v0.29.0-alpha.3 h1:OQeRh2S47GTJOZauTA3igT1wChmx3MPeObz5bhV9x8s=
 | 
			
		||||
k8s.io/code-generator v0.29.0-alpha.3/go.mod h1:C1oDIDCuN+hZsr8bZVFUp6dsOKvvMZ6jcmE4SFQn//8=
 | 
			
		||||
k8s.io/component-base v0.29.0-alpha.3 h1:R0Au5IIQaog5gx7P7M9hNQASA3Ca61e9r3WJmndZdsY=
 | 
			
		||||
k8s.io/component-base v0.29.0-alpha.3/go.mod h1:2ViYQzIf+qZRQFQxpcvz6xvOJnHtsj9CM6PSoYXg1HU=
 | 
			
		||||
k8s.io/component-helpers v0.29.0-alpha.3 h1:bc0Q654mJ/ULgVMPVajb+rCbTcee/55RJNNR38GHnXI=
 | 
			
		||||
k8s.io/component-helpers v0.29.0-alpha.3/go.mod h1:8vqwVQKAjWaFkqdOgY0no/CyypHmU+HWB3YhCOwDDAE=
 | 
			
		||||
k8s.io/controller-manager v0.29.0-alpha.3 h1:QLBQ3droXBoGwj202c0/we7qvynENVxcVIEJJ66cGhM=
 | 
			
		||||
k8s.io/controller-manager v0.29.0-alpha.3/go.mod h1:xgVCSzbzX9tBHflkUB3MI4GANd7ePkTgTQgDD+Mw6kw=
 | 
			
		||||
k8s.io/cri-api v0.29.0-alpha.3 h1:CS/RuCK+YL6sgGefbDX4rlznYASL8yAsqvLq3M5SvJY=
 | 
			
		||||
k8s.io/cri-api v0.29.0-alpha.3/go.mod h1:3a2YQ+dzaWb6S8NuTJnkldEbFCYOdfE9tVqUSlKbw4E=
 | 
			
		||||
k8s.io/csi-translation-lib v0.29.0-alpha.3 h1:u27f2djNHteJSstmnZLAEi0RQJwilgv4T4fYcaJR7eQ=
 | 
			
		||||
k8s.io/csi-translation-lib v0.29.0-alpha.3/go.mod h1:qkAWFUkeNUo8cBlTo46kTpJzHRPunSzErTyzCY7gh4w=
 | 
			
		||||
k8s.io/dynamic-resource-allocation v0.29.0-alpha.3 h1:ZR0VEFW1NJP7zfq/D1vULSTisWHWJSrtkMlvppBeODc=
 | 
			
		||||
k8s.io/dynamic-resource-allocation v0.29.0-alpha.3/go.mod h1:YTJQ0UDRrrNFWn7Zbu3bbUFvy6jhg5w5/m/RTsFT0Lk=
 | 
			
		||||
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks=
 | 
			
		||||
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 | 
			
		||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
 | 
			
		||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 | 
			
		||||
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 | 
			
		||||
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
 | 
			
		||||
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
 | 
			
		||||
k8s.io/kms v0.29.0-alpha.1 h1:jIbt/B48SHg5EJ0XxTsMTyoI0yrQkA8SLqBAWcImMic=
 | 
			
		||||
k8s.io/kms v0.29.0-alpha.1/go.mod h1:Yv5MtAfuOkfGy+iov2HxqOWDTSGVSv+sBVchzevpFMM=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f h1:eeEUOoGYWhOz7EyXqhlR2zHKNw2mNJ9vzJmub6YN6kk=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
 | 
			
		||||
k8s.io/kube-scheduler v0.29.0-alpha.1 h1:XnPSEAl/aFSBPkHt3u4UgJ/R124YBRPMKHAKdikrxus=
 | 
			
		||||
k8s.io/kube-scheduler v0.29.0-alpha.1/go.mod h1:okXX4sToeOtriI64yvAubxChfJcYaRId7Sal6nxfh3o=
 | 
			
		||||
k8s.io/kubectl v0.29.0-alpha.1 h1:mMfChxVG5qpC3tbqpurq8w33jlX7niat9YWYaDonuDU=
 | 
			
		||||
k8s.io/kubectl v0.29.0-alpha.1/go.mod h1:2woriKYJccld2smvsZujHnzbjaI2gGpK2NfcIbQcWwo=
 | 
			
		||||
k8s.io/kubelet v0.29.0-alpha.1 h1:hA5DgE9IvzmXmVHnmeAehud/4GnhNCZc5u3EqtlpF9o=
 | 
			
		||||
k8s.io/kubelet v0.29.0-alpha.1/go.mod h1:emA5bLiHT8K+CAa5dqjz7fxTOrdbhNXIoBzZVRtsIlw=
 | 
			
		||||
k8s.io/kubernetes v1.29.0-alpha.1 h1:6xRN0z/ftaJO1jD8H86pvNJoDenT66zFt/FhMNdkGsY=
 | 
			
		||||
k8s.io/kubernetes v1.29.0-alpha.1/go.mod h1:YqGcjUoL8mgiUc4rnyvXFZuCKQaD1/03JrKuFEzrv70=
 | 
			
		||||
k8s.io/legacy-cloud-providers v0.29.0-alpha.1 h1:nqQWr6E47uZMFUfePFhas1TSc4nV0tqJ1/T5ZLyOYjE=
 | 
			
		||||
k8s.io/legacy-cloud-providers v0.29.0-alpha.1/go.mod h1:zZ4Ww11LNvrguh19v9KFM4S/ehxyAmv78dfEaAVTIDA=
 | 
			
		||||
k8s.io/mount-utils v0.29.0-alpha.1 h1:W1wx2RtOybrZn29PSNqrqlq4xGJ8kGcZMlWDPuW0nV4=
 | 
			
		||||
k8s.io/mount-utils v0.29.0-alpha.1/go.mod h1:M+wkKv/SGLol4JME67gjJL2XN46YpQiAyCnMhhCBuEc=
 | 
			
		||||
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 | 
			
		||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
 | 
			
		||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
 | 
			
		||||
k8s.io/kms v0.29.0-alpha.3 h1:MxOaeXnE50OEqb6CgbE1TAGZop387NF6/yeFghANsFc=
 | 
			
		||||
k8s.io/kms v0.29.0-alpha.3/go.mod h1:FqOEKmJMCoy6bPYdLRXYxPOdR9hQtM1/QUabKQIXCv8=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
 | 
			
		||||
k8s.io/kube-scheduler v0.29.0-alpha.3 h1:7nX3uMwD/k5FQSsiKlQ/bxnKh0/3i0HP6SZEqusyaF4=
 | 
			
		||||
k8s.io/kube-scheduler v0.29.0-alpha.3/go.mod h1:hH7ItZdOFsP7V+eNqxIiU19YuOqXWYll71SoS4u0o68=
 | 
			
		||||
k8s.io/kubectl v0.29.0-alpha.3 h1:Q9TkGB5HHJNjX9+sdzNeRNDashB7EF1aDOkwqcs0SlI=
 | 
			
		||||
k8s.io/kubectl v0.29.0-alpha.3/go.mod h1:tG9srWZBHIZHifk1ERUAmRyXLrR4hGeXzuAVU2Sz48I=
 | 
			
		||||
k8s.io/kubelet v0.29.0-alpha.3 h1:wiVt06zqSypK5WNWIuVs1LIyEOTuI0P4rTaRi/AdZSE=
 | 
			
		||||
k8s.io/kubelet v0.29.0-alpha.3/go.mod h1:zO7ssCmP+3Yg9M/bCdTJWkrLKTbs99Oie2MxwpZYkxo=
 | 
			
		||||
k8s.io/kubernetes v1.29.0-alpha.3 h1:EpsTUPqSynUEJCO5sZaq9pBxk/3y6mjTPMcG5fz089g=
 | 
			
		||||
k8s.io/kubernetes v1.29.0-alpha.3/go.mod h1:zBvSiD31nd9pX6tvl2+/Ds+cNJ8nAx/JSvzuXNpq/3A=
 | 
			
		||||
k8s.io/legacy-cloud-providers v0.29.0-alpha.3 h1:hXzZ5V84gNOmJzGIOJBOkWG7VMiye1b3njEs5zsrXEI=
 | 
			
		||||
k8s.io/legacy-cloud-providers v0.29.0-alpha.3/go.mod h1:4JrNQQiSo66qFzUi9bqdFVnQZ8l0QLiJ2LOmcZFVJ+U=
 | 
			
		||||
k8s.io/mount-utils v0.29.0-alpha.3 h1:DnGJaEEZe73bvBu5zhNhveh1UHC0zDlAAFNPRtTcCu4=
 | 
			
		||||
k8s.io/mount-utils v0.29.0-alpha.3/go.mod h1:rfxftInoYqE82uC/7UeqNYqgW71N0J64ftvgOkzUmew=
 | 
			
		||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
 | 
			
		||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 | 
			
		||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
			
		||||
| 
						 | 
				
			
			@ -1268,8 +1163,8 @@ sigs.k8s.io/cloud-provider-azure v1.28.0 h1:LkvvDQ2u0rCr1lhFBoyjvKhYazhpYnAohOqQ
 | 
			
		|||
sigs.k8s.io/cloud-provider-azure v1.28.0/go.mod h1:ubvg4F58jePO4Z7C4XfgJkFFGpqhVeogpzOdc1X4dyk=
 | 
			
		||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 | 
			
		||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
 | 
			
		||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 | 
			
		||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
 | 
			
		||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	apiv1 "k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/klog/v2"
 | 
			
		||||
	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +127,7 @@ func newInternalBasicSnapshotData() *internalBasicSnapshotData {
 | 
			
		|||
func (data *internalBasicSnapshotData) clone() *internalBasicSnapshotData {
 | 
			
		||||
	clonedNodeInfoMap := make(map[string]*schedulerframework.NodeInfo)
 | 
			
		||||
	for k, v := range data.nodeInfoMap {
 | 
			
		||||
		clonedNodeInfoMap[k] = v.Clone()
 | 
			
		||||
		clonedNodeInfoMap[k] = v.Snapshot()
 | 
			
		||||
	}
 | 
			
		||||
	clonedPvcNamespaceNodeMap := make(map[string]map[string]bool)
 | 
			
		||||
	for k, v := range data.pvcNamespacePodMap {
 | 
			
		||||
| 
						 | 
				
			
			@ -185,10 +186,11 @@ func (data *internalBasicSnapshotData) removePod(namespace, podName, nodeName st
 | 
			
		|||
	if !found {
 | 
			
		||||
		return ErrNodeNotFound
 | 
			
		||||
	}
 | 
			
		||||
	logger := klog.Background()
 | 
			
		||||
	for _, podInfo := range nodeInfo.Pods {
 | 
			
		||||
		if podInfo.Pod.Namespace == namespace && podInfo.Pod.Name == podName {
 | 
			
		||||
			data.removePvcUsedByPod(podInfo.Pod)
 | 
			
		||||
			err := nodeInfo.RemovePod(podInfo.Pod)
 | 
			
		||||
			err := nodeInfo.RemovePod(logger, podInfo.Pod)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				data.addPvcUsedByPod(podInfo.Pod)
 | 
			
		||||
				return fmt.Errorf("cannot remove pod; %v", err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	apiv1 "k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/klog/v2"
 | 
			
		||||
	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -228,7 +229,7 @@ func (data *internalDeltaSnapshotData) nodeInfoToModify(nodeName string) (*sched
 | 
			
		|||
		if !found {
 | 
			
		||||
			return nil, false
 | 
			
		||||
		}
 | 
			
		||||
		dni = bni.Clone()
 | 
			
		||||
		dni = bni.Snapshot()
 | 
			
		||||
		data.modifiedNodeInfoMap[nodeName] = dni
 | 
			
		||||
		data.clearCaches()
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -258,9 +259,10 @@ func (data *internalDeltaSnapshotData) removePod(namespace, name, nodeName strin
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	podFound := false
 | 
			
		||||
	logger := klog.Background()
 | 
			
		||||
	for _, podInfo := range ni.Pods {
 | 
			
		||||
		if podInfo.Pod.Namespace == namespace && podInfo.Pod.Name == name {
 | 
			
		||||
			if err := ni.RemovePod(podInfo.Pod); err != nil {
 | 
			
		||||
			if err := ni.RemovePod(logger, podInfo.Pod); err != nil {
 | 
			
		||||
				return fmt.Errorf("cannot remove pod; %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			podFound = true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ func (rs Rules) Drainable(drainCtx *drainability.DrainContext, pod *apiv1.Pod) d
 | 
			
		|||
		for _, candidate := range candidates {
 | 
			
		||||
			for _, override := range candidate.status.Overrides {
 | 
			
		||||
				if status.Outcome == override {
 | 
			
		||||
					klog.V(5).Info("Overriding pod %s/%s drainability rule %s with rule %s, outcome %v", pod.GetNamespace(), pod.GetName(), r.Name(), candidate.name, candidate.status.Outcome)
 | 
			
		||||
					klog.V(5).Infof("Overriding pod %s/%s drainability rule %s with rule %s, outcome %v", pod.GetNamespace(), pod.GetName(), r.Name(), candidate.name, candidate.status.Outcome)
 | 
			
		||||
					return candidate.status
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,10 +162,10 @@ func (p *SchedulerBasedPredicateChecker) CheckPredicates(clusterSnapshot cluster
 | 
			
		|||
	filterStatus := p.framework.RunFilterPlugins(context.TODO(), state, pod, nodeInfo)
 | 
			
		||||
 | 
			
		||||
	if !filterStatus.IsSuccess() {
 | 
			
		||||
		filterName := filterStatus.FailedPlugin()
 | 
			
		||||
		filterName := filterStatus.Plugin()
 | 
			
		||||
		filterMessage := filterStatus.Message()
 | 
			
		||||
		filterReasons := filterStatus.Reasons()
 | 
			
		||||
		if filterStatus.IsUnschedulable() {
 | 
			
		||||
		if filterStatus.IsRejected() {
 | 
			
		||||
			return NewPredicateError(
 | 
			
		||||
				NotSchedulablePredicateError,
 | 
			
		||||
				filterName,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,4 +15,4 @@
 | 
			
		|||
package internal
 | 
			
		||||
 | 
			
		||||
// Version is the current tagged release of the library.
 | 
			
		||||
const Version = "1.19.0"
 | 
			
		||||
const Version = "1.23.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +0,0 @@
 | 
			
		|||
# Copyright (C) 2017 SUSE LLC. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
language: go
 | 
			
		||||
go:
 | 
			
		||||
    - 1.13.x
 | 
			
		||||
    - 1.16.x
 | 
			
		||||
    - tip
 | 
			
		||||
arch:
 | 
			
		||||
    - AMD64
 | 
			
		||||
    - ppc64le
 | 
			
		||||
os:
 | 
			
		||||
    - linux
 | 
			
		||||
    - osx
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
    - go test -cover -v ./...
 | 
			
		||||
 | 
			
		||||
notifications:
 | 
			
		||||
    email: false
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
## `filepath-securejoin` ##
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/cyphar/filepath-securejoin)
 | 
			
		||||
[](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml)
 | 
			
		||||
 | 
			
		||||
An implementation of `SecureJoin`, a [candidate for inclusion in the Go
 | 
			
		||||
standard library][go#20126]. The purpose of this function is to be a "secure"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
0.2.3
 | 
			
		||||
0.2.4
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,17 +39,27 @@ func IsNotExist(err error) bool {
 | 
			
		|||
// components in the returned string are not modified (in other words are not
 | 
			
		||||
// replaced with symlinks on the filesystem) after this function has returned.
 | 
			
		||||
// Such a symlink race is necessarily out-of-scope of SecureJoin.
 | 
			
		||||
//
 | 
			
		||||
// Volume names in unsafePath are always discarded, regardless if they are
 | 
			
		||||
// provided via direct input or when evaluating symlinks. Therefore:
 | 
			
		||||
//
 | 
			
		||||
// "C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
 | 
			
		||||
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
 | 
			
		||||
	// Use the os.* VFS implementation if none was specified.
 | 
			
		||||
	if vfs == nil {
 | 
			
		||||
		vfs = osVFS{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unsafePath = filepath.FromSlash(unsafePath)
 | 
			
		||||
	var path bytes.Buffer
 | 
			
		||||
	n := 0
 | 
			
		||||
	for unsafePath != "" {
 | 
			
		||||
		if n > 255 {
 | 
			
		||||
			return "", &os.PathError{Op: "SecureJoin", Path: root + "/" + unsafePath, Err: syscall.ELOOP}
 | 
			
		||||
			return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if v := filepath.VolumeName(unsafePath); v != "" {
 | 
			
		||||
			unsafePath = unsafePath[len(v):]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Next path component, p.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,15 @@
 | 
			
		|||
# Change history of go-restful
 | 
			
		||||
 | 
			
		||||
## [v3.10.2] - 2023-03-09
 | 
			
		||||
## [v3.11.0] - 2023-08-19
 | 
			
		||||
 | 
			
		||||
- restored behavior as <= v3.9.0 with option to change path strategy using TrimRightSlashEnabled. 
 | 
			
		||||
 | 
			
		||||
## [v3.10.2] - 2023-03-09 - DO NOT USE
 | 
			
		||||
 | 
			
		||||
- introduced MergePathStrategy to be able to revert behaviour of path concatenation to 3.9.0
 | 
			
		||||
  see comment in Readme how to customize this behaviour.
 | 
			
		||||
 | 
			
		||||
## [v3.10.1] - 2022-11-19
 | 
			
		||||
## [v3.10.1] - 2022-11-19 - DO NOT USE
 | 
			
		||||
 | 
			
		||||
- fix broken 3.10.0 by using path package for joining paths
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
 | 
			
		|||
- Content encoding (gzip,deflate) of request and response payloads
 | 
			
		||||
- Automatic responses on OPTIONS (using a filter)
 | 
			
		||||
- Automatic CORS request handling (using a filter)
 | 
			
		||||
- API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi), see [go-restful-swagger12](https://github.com/emicklei/go-restful-swagger12))
 | 
			
		||||
- API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi))
 | 
			
		||||
- Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
 | 
			
		||||
- Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
 | 
			
		||||
- Configurable (trace) logging
 | 
			
		||||
| 
						 | 
				
			
			@ -96,10 +96,7 @@ There are several hooks to customize the behavior of the go-restful package.
 | 
			
		|||
- Compression
 | 
			
		||||
- Encoders for other serializers
 | 
			
		||||
- Use [jsoniter](https://github.com/json-iterator/go) by building this package using a build tag, e.g. `go build -tags=jsoniter .` 
 | 
			
		||||
- Use the variable `MergePathStrategy` to change the behaviour of composing the Route path given a root path and a local route path	
 | 
			
		||||
	- versions >= 3.10.1 has set the value to `PathJoinStrategy` that fixes a reported [security issue](https://github.com/advisories/GHSA-r48q-9g5r-8q2h) but may cause your services not to work correctly anymore.
 | 
			
		||||
	- versions <= 3.9 had the behaviour that can be restored in newer versions by setting the value to `TrimSlashStrategy`.
 | 
			
		||||
	- you can set value to a custom implementation (must implement MergePathStrategyFunc)
 | 
			
		||||
- Use the package variable `TrimRightSlashEnabled` (default true) to control the behavior of matching routes that end with a slash `/` 
 | 
			
		||||
 | 
			
		||||
## Resources
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -112,4 +109,4 @@ There are several hooks to customize the behavior of the go-restful package.
 | 
			
		|||
 | 
			
		||||
Type ```git shortlog -s``` for a full list of contributors.
 | 
			
		||||
 | 
			
		||||
© 2012 - 2022, http://ernestmicklei.com. MIT License. Contributions are welcome.
 | 
			
		||||
© 2012 - 2023, http://ernestmicklei.com. MIT License. Contributions are welcome.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,8 @@ type Route struct {
 | 
			
		|||
	ParameterDocs           []*Parameter
 | 
			
		||||
	ResponseErrors          map[int]ResponseError
 | 
			
		||||
	DefaultResponse         *ResponseError
 | 
			
		||||
	ReadSample, WriteSample interface{} // structs that model an example request or response payload
 | 
			
		||||
	ReadSample, WriteSample interface{}   // structs that model an example request or response payload
 | 
			
		||||
	WriteSamples            []interface{} // if more than one return types is possible (oneof) then this will contain multiple values
 | 
			
		||||
 | 
			
		||||
	// Extra information used to store custom information about the route.
 | 
			
		||||
	Metadata map[string]interface{}
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +165,13 @@ func tokenizePath(path string) []string {
 | 
			
		|||
	if "/" == path {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Split(strings.TrimLeft(path, "/"), "/")
 | 
			
		||||
	if TrimRightSlashEnabled {
 | 
			
		||||
		// 3.9.0
 | 
			
		||||
		return strings.Split(strings.Trim(path, "/"), "/")
 | 
			
		||||
	} else {
 | 
			
		||||
		// 3.10.2
 | 
			
		||||
		return strings.Split(strings.TrimLeft(path, "/"), "/")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// for debugging
 | 
			
		||||
| 
						 | 
				
			
			@ -177,4 +184,8 @@ func (r *Route) EnableContentEncoding(enabled bool) {
 | 
			
		|||
	r.contentEncodingEnabled = &enabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var TrimRightSlashEnabled = false
 | 
			
		||||
// TrimRightSlashEnabled controls whether
 | 
			
		||||
// - path on route building is using path.Join
 | 
			
		||||
// - the path of the incoming request is trimmed of its slash suffux.
 | 
			
		||||
// Value of true matches the behavior of <= 3.9.0
 | 
			
		||||
var TrimRightSlashEnabled = true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,17 +31,18 @@ type RouteBuilder struct {
 | 
			
		|||
	typeNameHandleFunc TypeNameHandleFunction // required
 | 
			
		||||
 | 
			
		||||
	// documentation
 | 
			
		||||
	doc                     string
 | 
			
		||||
	notes                   string
 | 
			
		||||
	operation               string
 | 
			
		||||
	readSample, writeSample interface{}
 | 
			
		||||
	parameters              []*Parameter
 | 
			
		||||
	errorMap                map[int]ResponseError
 | 
			
		||||
	defaultResponse         *ResponseError
 | 
			
		||||
	metadata                map[string]interface{}
 | 
			
		||||
	extensions              map[string]interface{}
 | 
			
		||||
	deprecated              bool
 | 
			
		||||
	contentEncodingEnabled  *bool
 | 
			
		||||
	doc                    string
 | 
			
		||||
	notes                  string
 | 
			
		||||
	operation              string
 | 
			
		||||
	readSample             interface{}
 | 
			
		||||
	writeSamples           []interface{}
 | 
			
		||||
	parameters             []*Parameter
 | 
			
		||||
	errorMap               map[int]ResponseError
 | 
			
		||||
	defaultResponse        *ResponseError
 | 
			
		||||
	metadata               map[string]interface{}
 | 
			
		||||
	extensions             map[string]interface{}
 | 
			
		||||
	deprecated             bool
 | 
			
		||||
	contentEncodingEnabled *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Do evaluates each argument with the RouteBuilder itself.
 | 
			
		||||
| 
						 | 
				
			
			@ -135,9 +136,9 @@ func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) {
 | 
			
		|||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Writes tells what resource type will be written as the response payload. Optional.
 | 
			
		||||
func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder {
 | 
			
		||||
	b.writeSample = sample
 | 
			
		||||
// Writes tells which one of the resource types will be written as the response payload. Optional.
 | 
			
		||||
func (b *RouteBuilder) Writes(samples ...interface{}) *RouteBuilder {
 | 
			
		||||
	b.writeSamples = samples // oneof
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -342,39 +343,29 @@ func (b *RouteBuilder) Build() Route {
 | 
			
		|||
		ResponseErrors:                   b.errorMap,
 | 
			
		||||
		DefaultResponse:                  b.defaultResponse,
 | 
			
		||||
		ReadSample:                       b.readSample,
 | 
			
		||||
		WriteSample:                      b.writeSample,
 | 
			
		||||
		WriteSamples:                     b.writeSamples,
 | 
			
		||||
		Metadata:                         b.metadata,
 | 
			
		||||
		Deprecated:                       b.deprecated,
 | 
			
		||||
		contentEncodingEnabled:           b.contentEncodingEnabled,
 | 
			
		||||
		allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType,
 | 
			
		||||
	}
 | 
			
		||||
	// set WriteSample if one specified
 | 
			
		||||
	if len(b.writeSamples) == 1 {
 | 
			
		||||
		route.WriteSample = b.writeSamples[0]
 | 
			
		||||
	}
 | 
			
		||||
	route.Extensions = b.extensions
 | 
			
		||||
	route.postBuild()
 | 
			
		||||
	return route
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MergePathStrategyFunc func(rootPath, routePath string) string
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// behavior >= 3.10
 | 
			
		||||
	PathJoinStrategy = func(rootPath, routePath string) string {
 | 
			
		||||
		return path.Join(rootPath, routePath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// behavior <= 3.9
 | 
			
		||||
	TrimSlashStrategy = func(rootPath, routePath string) string {
 | 
			
		||||
		return strings.TrimRight(rootPath, "/") + "/" + strings.TrimLeft(routePath, "/")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// MergePathStrategy is the active strategy for merging a Route path when building the routing of all WebServices.
 | 
			
		||||
	// The value is set to PathJoinStrategy
 | 
			
		||||
	// PathJoinStrategy is a strategy that is more strict [Security - PRISMA-2022-0227]
 | 
			
		||||
	MergePathStrategy = PathJoinStrategy
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// merge two paths using the current (package global) merge path strategy.
 | 
			
		||||
func concatPath(rootPath, routePath string) string {
 | 
			
		||||
	return MergePathStrategy(rootPath, routePath)
 | 
			
		||||
 | 
			
		||||
	if TrimRightSlashEnabled {
 | 
			
		||||
		return strings.TrimRight(rootPath, "/") + "/" + strings.TrimLeft(routePath, "/")
 | 
			
		||||
	} else {
 | 
			
		||||
		return path.Join(rootPath, routePath)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var anonymousFuncCount int32
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								cluster-autoscaler/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										13
									
								
								cluster-autoscaler/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
freebsd_task:
 | 
			
		||||
  name: 'FreeBSD'
 | 
			
		||||
  freebsd_instance:
 | 
			
		||||
    image_family: freebsd-13-2
 | 
			
		||||
  install_script:
 | 
			
		||||
    - pkg update -f
 | 
			
		||||
    - pkg install -y go
 | 
			
		||||
  test_script:
 | 
			
		||||
      # run tests as user "cirrus" instead of root
 | 
			
		||||
    - pw useradd cirrus -m
 | 
			
		||||
    - chown -R cirrus:cirrus .
 | 
			
		||||
    - FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
 | 
			
		||||
    -                      sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
 | 
			
		||||
| 
						 | 
				
			
			@ -4,3 +4,4 @@
 | 
			
		|||
 | 
			
		||||
# Output of go build ./cmd/fsnotify
 | 
			
		||||
/fsnotify
 | 
			
		||||
/fsnotify.exe
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,87 @@
 | 
			
		|||
# Changelog
 | 
			
		||||
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 | 
			
		||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 | 
			
		||||
 | 
			
		||||
## [Unreleased]
 | 
			
		||||
 | 
			
		||||
Unreleased
 | 
			
		||||
----------
 | 
			
		||||
Nothing yet.
 | 
			
		||||
 | 
			
		||||
## [1.6.0] - 2022-10-13
 | 
			
		||||
1.7.0 - 2023-10-22
 | 
			
		||||
------------------
 | 
			
		||||
This version of fsnotify needs Go 1.17.
 | 
			
		||||
 | 
			
		||||
### Additions
 | 
			
		||||
 | 
			
		||||
- illumos: add FEN backend to support illumos and Solaris. ([#371])
 | 
			
		||||
 | 
			
		||||
- all: add `NewBufferedWatcher()` to use a buffered channel, which can be useful
 | 
			
		||||
  in cases where you can't control the kernel buffer and receive a large number
 | 
			
		||||
  of events in bursts. ([#550], [#572])
 | 
			
		||||
 | 
			
		||||
- all: add `AddWith()`, which is identical to `Add()` but allows passing
 | 
			
		||||
  options. ([#521])
 | 
			
		||||
 | 
			
		||||
- windows: allow setting the ReadDirectoryChangesW() buffer size with
 | 
			
		||||
  `fsnotify.WithBufferSize()`; the default of 64K is the highest value that
 | 
			
		||||
  works on all platforms and is enough for most purposes, but in some cases a
 | 
			
		||||
  highest buffer is needed. ([#521])
 | 
			
		||||
 | 
			
		||||
### Changes and fixes
 | 
			
		||||
 | 
			
		||||
- inotify: remove watcher if a watched path is renamed ([#518])
 | 
			
		||||
 | 
			
		||||
  After a rename the reported name wasn't updated, or even an empty string.
 | 
			
		||||
  Inotify doesn't provide any good facilities to update it, so just remove the
 | 
			
		||||
  watcher. This is already how it worked on kqueue and FEN.
 | 
			
		||||
 | 
			
		||||
  On Windows this does work, and remains working.
 | 
			
		||||
 | 
			
		||||
- windows: don't listen for file attribute changes ([#520])
 | 
			
		||||
 | 
			
		||||
  File attribute changes are sent as `FILE_ACTION_MODIFIED` by the Windows API,
 | 
			
		||||
  with no way to see if they're a file write or attribute change, so would show
 | 
			
		||||
  up as a fsnotify.Write event. This is never useful, and could result in many
 | 
			
		||||
  spurious Write events.
 | 
			
		||||
 | 
			
		||||
- windows: return `ErrEventOverflow` if the buffer is full ([#525])
 | 
			
		||||
 | 
			
		||||
  Before it would merely return "short read", making it hard to detect this
 | 
			
		||||
  error.
 | 
			
		||||
 | 
			
		||||
- kqueue: make sure events for all files are delivered properly when removing a
 | 
			
		||||
  watched directory ([#526])
 | 
			
		||||
 | 
			
		||||
  Previously they would get sent with `""` (empty string) or `"."` as the path
 | 
			
		||||
  name.
 | 
			
		||||
 | 
			
		||||
- kqueue: don't emit spurious Create events for symbolic links ([#524])
 | 
			
		||||
 | 
			
		||||
  The link would get resolved but kqueue would "forget" it already saw the link
 | 
			
		||||
  itself, resulting on a Create for every Write event for the directory.
 | 
			
		||||
 | 
			
		||||
- all: return `ErrClosed` on `Add()` when the watcher is closed ([#516])
 | 
			
		||||
 | 
			
		||||
- other: add `Watcher.Errors` and `Watcher.Events` to the no-op `Watcher` in
 | 
			
		||||
  `backend_other.go`, making it easier to use on unsupported platforms such as
 | 
			
		||||
  WASM, AIX, etc. ([#528])
 | 
			
		||||
 | 
			
		||||
- other: use the `backend_other.go` no-op if the `appengine` build tag is set;
 | 
			
		||||
  Google AppEngine forbids usage of the unsafe package so the inotify backend
 | 
			
		||||
  won't compile there.
 | 
			
		||||
 | 
			
		||||
[#371]: https://github.com/fsnotify/fsnotify/pull/371
 | 
			
		||||
[#516]: https://github.com/fsnotify/fsnotify/pull/516
 | 
			
		||||
[#518]: https://github.com/fsnotify/fsnotify/pull/518
 | 
			
		||||
[#520]: https://github.com/fsnotify/fsnotify/pull/520
 | 
			
		||||
[#521]: https://github.com/fsnotify/fsnotify/pull/521
 | 
			
		||||
[#524]: https://github.com/fsnotify/fsnotify/pull/524
 | 
			
		||||
[#525]: https://github.com/fsnotify/fsnotify/pull/525
 | 
			
		||||
[#526]: https://github.com/fsnotify/fsnotify/pull/526
 | 
			
		||||
[#528]: https://github.com/fsnotify/fsnotify/pull/528
 | 
			
		||||
[#537]: https://github.com/fsnotify/fsnotify/pull/537
 | 
			
		||||
[#550]: https://github.com/fsnotify/fsnotify/pull/550
 | 
			
		||||
[#572]: https://github.com/fsnotify/fsnotify/pull/572
 | 
			
		||||
 | 
			
		||||
1.6.0 - 2022-10-13
 | 
			
		||||
------------------
 | 
			
		||||
This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1,
 | 
			
		||||
but not documented). It also increases the minimum Linux version to 2.6.32.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,29 +1,31 @@
 | 
			
		|||
fsnotify is a Go library to provide cross-platform filesystem notifications on
 | 
			
		||||
Windows, Linux, macOS, and BSD systems.
 | 
			
		||||
Windows, Linux, macOS, BSD, and illumos.
 | 
			
		||||
 | 
			
		||||
Go 1.16 or newer is required; the full documentation is at
 | 
			
		||||
Go 1.17 or newer is required; the full documentation is at
 | 
			
		||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
 | 
			
		||||
 | 
			
		||||
**It's best to read the documentation at pkg.go.dev, as it's pinned to the last
 | 
			
		||||
released version, whereas this README is for the last development version which
 | 
			
		||||
may include additions/changes.**
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Platform support:
 | 
			
		||||
 | 
			
		||||
| Adapter               | OS             | Status                                                       |
 | 
			
		||||
| --------------------- | ---------------| -------------------------------------------------------------|
 | 
			
		||||
| inotify               | Linux 2.6.32+  | Supported                                                    |
 | 
			
		||||
| kqueue                | BSD, macOS     | Supported                                                    |
 | 
			
		||||
| ReadDirectoryChangesW | Windows        | Supported                                                    |
 | 
			
		||||
| FSEvents              | macOS          | [Planned](https://github.com/fsnotify/fsnotify/issues/11)    |
 | 
			
		||||
| FEN                   | Solaris 11     | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) |
 | 
			
		||||
| fanotify              | Linux 5.9+     | [Maybe](https://github.com/fsnotify/fsnotify/issues/114)     |
 | 
			
		||||
| USN Journals          | Windows        | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)      |
 | 
			
		||||
| Polling               | *All*          | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)       |
 | 
			
		||||
| Backend               | OS         | Status                                                                    |
 | 
			
		||||
| :-------------------- | :--------- | :------------------------------------------------------------------------ |
 | 
			
		||||
| inotify               | Linux      | Supported                                                                 |
 | 
			
		||||
| kqueue                | BSD, macOS | Supported                                                                 |
 | 
			
		||||
| ReadDirectoryChangesW | Windows    | Supported                                                                 |
 | 
			
		||||
| FEN                   | illumos    | Supported                                                                 |
 | 
			
		||||
| fanotify              | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114)                |
 | 
			
		||||
| AHAFS                 | AIX        | [aix branch]; experimental due to lack of maintainer and test environment |
 | 
			
		||||
| FSEvents              | macOS      | [Needs support in x/sys/unix][fsevents]                                   |
 | 
			
		||||
| USN Journals          | Windows    | [Needs support in x/sys/windows][usn]                                     |
 | 
			
		||||
| Polling               | *All*      | [Not yet](https://github.com/fsnotify/fsnotify/issues/9)                  |
 | 
			
		||||
 | 
			
		||||
Linux and macOS should include Android and iOS, but these are currently untested.
 | 
			
		||||
Linux and illumos should include Android and Solaris, but these are currently
 | 
			
		||||
untested.
 | 
			
		||||
 | 
			
		||||
[fsevents]:   https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
 | 
			
		||||
[usn]:        https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
 | 
			
		||||
[aix branch]: https://github.com/fsnotify/fsnotify/issues/353#issuecomment-1284590129
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
| 
						 | 
				
			
			@ -83,20 +85,23 @@ run with:
 | 
			
		|||
 | 
			
		||||
    % go run ./cmd/fsnotify
 | 
			
		||||
 | 
			
		||||
Further detailed documentation can be found in godoc:
 | 
			
		||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
 | 
			
		||||
 | 
			
		||||
FAQ
 | 
			
		||||
---
 | 
			
		||||
### Will a file still be watched when it's moved to another directory?
 | 
			
		||||
No, not unless you are watching the location it was moved to.
 | 
			
		||||
 | 
			
		||||
### Are subdirectories watched too?
 | 
			
		||||
### Are subdirectories watched?
 | 
			
		||||
No, you must add watches for any directory you want to watch (a recursive
 | 
			
		||||
watcher is on the roadmap: [#18]).
 | 
			
		||||
 | 
			
		||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
 | 
			
		||||
 | 
			
		||||
### Do I have to watch the Error and Event channels in a goroutine?
 | 
			
		||||
As of now, yes (you can read both channels in the same goroutine using `select`,
 | 
			
		||||
you don't need a separate goroutine for both channels; see the example).
 | 
			
		||||
Yes. You can read both channels in the same goroutine using `select` (you don't
 | 
			
		||||
need a separate goroutine for both channels; see the example).
 | 
			
		||||
 | 
			
		||||
### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys?
 | 
			
		||||
fsnotify requires support from underlying OS to work. The current NFS and SMB
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +112,32 @@ This could be fixed with a polling watcher ([#9]), but it's not yet implemented.
 | 
			
		|||
 | 
			
		||||
[#9]: https://github.com/fsnotify/fsnotify/issues/9
 | 
			
		||||
 | 
			
		||||
### Why do I get many Chmod events?
 | 
			
		||||
Some programs may generate a lot of attribute changes; for example Spotlight on
 | 
			
		||||
macOS, anti-virus programs, backup applications, and some others are known to do
 | 
			
		||||
this. As a rule, it's typically best to ignore Chmod events. They're often not
 | 
			
		||||
useful, and tend to cause problems.
 | 
			
		||||
 | 
			
		||||
Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
temporary workaround is to add your folder(s) to the *Spotlight Privacy
 | 
			
		||||
settings* until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
 | 
			
		||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
[#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
 | 
			
		||||
### Watching a file doesn't work well
 | 
			
		||||
Watching individual files (rather than directories) is generally not recommended
 | 
			
		||||
as many programs (especially editors) update files atomically: it will write to
 | 
			
		||||
a temporary file which is then moved to to destination, overwriting the original
 | 
			
		||||
(or some variant thereof). The watcher on the original file is now lost, as that
 | 
			
		||||
no longer exists.
 | 
			
		||||
 | 
			
		||||
The upshot of this is that a power failure or crash won't leave a half-written
 | 
			
		||||
file.
 | 
			
		||||
 | 
			
		||||
Watch the parent directory and use `Event.Name` to filter out files you're not
 | 
			
		||||
interested in. There is an example of this in `cmd/fsnotify/file.go`.
 | 
			
		||||
 | 
			
		||||
Platform-specific notes
 | 
			
		||||
-----------------------
 | 
			
		||||
### Linux
 | 
			
		||||
| 
						 | 
				
			
			@ -151,11 +182,3 @@ these platforms.
 | 
			
		|||
 | 
			
		||||
The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to
 | 
			
		||||
control the maximum number of open files.
 | 
			
		||||
 | 
			
		||||
### macOS
 | 
			
		||||
Spotlight indexing on macOS can result in multiple events (see [#15]). A temporary
 | 
			
		||||
workaround is to add your folder(s) to the *Spotlight Privacy settings* until we
 | 
			
		||||
have a native FSEvents implementation (see [#11]).
 | 
			
		||||
 | 
			
		||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
[#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,19 @@
 | 
			
		|||
//go:build solaris
 | 
			
		||||
// +build solaris
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of paths, delivering events on a channel.
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +26,9 @@ import (
 | 
			
		|||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -33,16 +42,16 @@ import (
 | 
			
		|||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
| 
						 | 
				
			
			@ -58,14 +67,20 @@ import (
 | 
			
		|||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
| 
						 | 
				
			
			@ -92,44 +107,129 @@ type Watcher struct {
 | 
			
		|||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	mu      sync.Mutex
 | 
			
		||||
	port    *unix.EventPort
 | 
			
		||||
	done    chan struct{}       // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
	dirs    map[string]struct{} // Explicitly watched directories
 | 
			
		||||
	watches map[string]struct{} // Explicitly watched non-directories
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
 | 
			
		||||
	return NewBufferedWatcher(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
 | 
			
		||||
	w := &Watcher{
 | 
			
		||||
		Events:  make(chan Event, sz),
 | 
			
		||||
		Errors:  make(chan error),
 | 
			
		||||
		dirs:    make(map[string]struct{}),
 | 
			
		||||
		watches: make(map[string]struct{}),
 | 
			
		||||
		done:    make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	w.port, err = unix.NewEventPort()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("fsnotify.NewWatcher: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go w.readEvents()
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendEvent attempts to send an event to the user, returning true if the event
 | 
			
		||||
// was put in the channel successfully and false if the watcher has been closed.
 | 
			
		||||
func (w *Watcher) sendEvent(name string, op Op) (sent bool) {
 | 
			
		||||
	select {
 | 
			
		||||
	case w.Events <- Event{Name: name, Op: op}:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendError attempts to send an error to the user, returning true if the error
 | 
			
		||||
// was put in the channel successfully and false if the watcher has been closed.
 | 
			
		||||
func (w *Watcher) sendError(err error) (sent bool) {
 | 
			
		||||
	select {
 | 
			
		||||
	case w.Errors <- err:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) isClosed() bool {
 | 
			
		||||
	select {
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
	// Take the lock used by associateFile to prevent lingering events from
 | 
			
		||||
	// being processed after the close
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	close(w.done)
 | 
			
		||||
	return w.port.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
| 
						 | 
				
			
			@ -139,15 +239,63 @@ func (w *Watcher) Close() error {
 | 
			
		|||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
	if w.port.PathIsWatched(name) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_ = getOptions(opts...)
 | 
			
		||||
 | 
			
		||||
	// Currently we resolve symlinks that were explicitly requested to be
 | 
			
		||||
	// watched. Otherwise we would use LStat here.
 | 
			
		||||
	stat, err := os.Stat(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Associate all files in the directory.
 | 
			
		||||
	if stat.IsDir() {
 | 
			
		||||
		err := w.handleDirectory(name, stat, true, w.associateFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.dirs[name] = struct{}{}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.associateFile(name, stat, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.watches[name] = struct{}{}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +305,336 @@ func (w *Watcher) Add(name string) error {
 | 
			
		|||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !w.port.PathIsWatched(name) {
 | 
			
		||||
		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The user has expressed an intent. Immediately remove this name from
 | 
			
		||||
	// whichever watch list it might be in. If it's not in there the delete
 | 
			
		||||
	// doesn't cause harm.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	delete(w.watches, name)
 | 
			
		||||
	delete(w.dirs, name)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	stat, err := os.Stat(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove associations for every file in the directory.
 | 
			
		||||
	if stat.IsDir() {
 | 
			
		||||
		err := w.handleDirectory(name, stat, false, w.dissociateFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = w.port.DissociatePath(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents contains the main loop that runs in a goroutine watching for events.
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	// If this function returns, the watcher has been closed and we can close
 | 
			
		||||
	// these channels
 | 
			
		||||
	defer func() {
 | 
			
		||||
		close(w.Errors)
 | 
			
		||||
		close(w.Events)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	pevents := make([]unix.PortEvent, 8)
 | 
			
		||||
	for {
 | 
			
		||||
		count, err := w.port.Get(pevents, 1, nil)
 | 
			
		||||
		if err != nil && err != unix.ETIME {
 | 
			
		||||
			// Interrupted system call (count should be 0) ignore and continue
 | 
			
		||||
			if errors.Is(err, unix.EINTR) && count == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// Get failed because we called w.Close()
 | 
			
		||||
			if errors.Is(err, unix.EBADF) && w.isClosed() {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			// There was an error not caused by calling w.Close()
 | 
			
		||||
			if !w.sendError(err) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		p := pevents[:count]
 | 
			
		||||
		for _, pevent := range p {
 | 
			
		||||
			if pevent.Source != unix.PORT_SOURCE_FILE {
 | 
			
		||||
				// Event from unexpected source received; should never happen.
 | 
			
		||||
				if !w.sendError(errors.New("Event from unexpected source received")) {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = w.handleEvent(&pevent)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if !w.sendError(err) {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) handleDirectory(path string, stat os.FileInfo, follow bool, handler func(string, os.FileInfo, bool) error) error {
 | 
			
		||||
	files, err := os.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle all children of the directory.
 | 
			
		||||
	for _, entry := range files {
 | 
			
		||||
		finfo, err := entry.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		err = handler(filepath.Join(path, finfo.Name()), finfo, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// And finally handle the directory itself.
 | 
			
		||||
	return handler(path, stat, follow)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleEvent might need to emit more than one fsnotify event if the events
 | 
			
		||||
// bitmap matches more than one event type (e.g. the file was both modified and
 | 
			
		||||
// had the attributes changed between when the association was created and the
 | 
			
		||||
// when event was returned)
 | 
			
		||||
func (w *Watcher) handleEvent(event *unix.PortEvent) error {
 | 
			
		||||
	var (
 | 
			
		||||
		events     = event.Events
 | 
			
		||||
		path       = event.Path
 | 
			
		||||
		fmode      = event.Cookie.(os.FileMode)
 | 
			
		||||
		reRegister = true
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	_, watchedDir := w.dirs[path]
 | 
			
		||||
	_, watchedPath := w.watches[path]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	isWatched := watchedDir || watchedPath
 | 
			
		||||
 | 
			
		||||
	if events&unix.FILE_DELETE != 0 {
 | 
			
		||||
		if !w.sendEvent(path, Remove) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		reRegister = false
 | 
			
		||||
	}
 | 
			
		||||
	if events&unix.FILE_RENAME_FROM != 0 {
 | 
			
		||||
		if !w.sendEvent(path, Rename) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		// Don't keep watching the new file name
 | 
			
		||||
		reRegister = false
 | 
			
		||||
	}
 | 
			
		||||
	if events&unix.FILE_RENAME_TO != 0 {
 | 
			
		||||
		// We don't report a Rename event for this case, because Rename events
 | 
			
		||||
		// are interpreted as referring to the _old_ name of the file, and in
 | 
			
		||||
		// this case the event would refer to the new name of the file. This
 | 
			
		||||
		// type of rename event is not supported by fsnotify.
 | 
			
		||||
 | 
			
		||||
		// inotify reports a Remove event in this case, so we simulate this
 | 
			
		||||
		// here.
 | 
			
		||||
		if !w.sendEvent(path, Remove) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		// Don't keep watching the file that was removed
 | 
			
		||||
		reRegister = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The file is gone, nothing left to do.
 | 
			
		||||
	if !reRegister {
 | 
			
		||||
		if watchedDir {
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			delete(w.dirs, path)
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
		if watchedPath {
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			delete(w.watches, path)
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we didn't get a deletion the file still exists and we're going to have
 | 
			
		||||
	// to watch it again. Let's Stat it now so that we can compare permissions
 | 
			
		||||
	// and have what we need to continue watching the file
 | 
			
		||||
 | 
			
		||||
	stat, err := os.Lstat(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// This is unexpected, but we should still emit an event. This happens
 | 
			
		||||
		// most often on "rm -r" of a subdirectory inside a watched directory We
 | 
			
		||||
		// get a modify event of something happening inside, but by the time we
 | 
			
		||||
		// get here, the sudirectory is already gone. Clearly we were watching
 | 
			
		||||
		// this path but now it is gone. Let's tell the user that it was
 | 
			
		||||
		// removed.
 | 
			
		||||
		if !w.sendEvent(path, Remove) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		// Suppress extra write events on removed directories; they are not
 | 
			
		||||
		// informative and can be confusing.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// resolve symlinks that were explicitly watched as we would have at Add()
 | 
			
		||||
	// time. this helps suppress spurious Chmod events on watched symlinks
 | 
			
		||||
	if isWatched {
 | 
			
		||||
		stat, err = os.Stat(path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// The symlink still exists, but the target is gone. Report the
 | 
			
		||||
			// Remove similar to above.
 | 
			
		||||
			if !w.sendEvent(path, Remove) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			// Don't return the error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if events&unix.FILE_MODIFIED != 0 {
 | 
			
		||||
		if fmode.IsDir() {
 | 
			
		||||
			if watchedDir {
 | 
			
		||||
				if err := w.updateDirectory(path); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if !w.sendEvent(path, Write) {
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if !w.sendEvent(path, Write) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if events&unix.FILE_ATTRIB != 0 && stat != nil {
 | 
			
		||||
		// Only send Chmod if perms changed
 | 
			
		||||
		if stat.Mode().Perm() != fmode.Perm() {
 | 
			
		||||
			if !w.sendEvent(path, Chmod) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if stat != nil {
 | 
			
		||||
		// If we get here, it means we've hit an event above that requires us to
 | 
			
		||||
		// continue watching the file or directory
 | 
			
		||||
		return w.associateFile(path, stat, isWatched)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) updateDirectory(path string) error {
 | 
			
		||||
	// The directory was modified, so we must find unwatched entities and watch
 | 
			
		||||
	// them. If something was removed from the directory, nothing will happen,
 | 
			
		||||
	// as everything else should still be watched.
 | 
			
		||||
	files, err := os.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, entry := range files {
 | 
			
		||||
		path := filepath.Join(path, entry.Name())
 | 
			
		||||
		if w.port.PathIsWatched(path) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		finfo, err := entry.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		err = w.associateFile(path, finfo, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if !w.sendError(err) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !w.sendEvent(path, Create) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) associateFile(path string, stat os.FileInfo, follow bool) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
	// This is primarily protecting the call to AssociatePath but it is
 | 
			
		||||
	// important and intentional that the call to PathIsWatched is also
 | 
			
		||||
	// protected by this mutex. Without this mutex, AssociatePath has been seen
 | 
			
		||||
	// to error out that the path is already associated.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if w.port.PathIsWatched(path) {
 | 
			
		||||
		// Remove the old association in favor of this one If we get ENOENT,
 | 
			
		||||
		// then while the x/sys/unix wrapper still thought that this path was
 | 
			
		||||
		// associated, the underlying event port did not. This call will have
 | 
			
		||||
		// cleared up that discrepancy. The most likely cause is that the event
 | 
			
		||||
		// has fired but we haven't processed it yet.
 | 
			
		||||
		err := w.port.DissociatePath(path)
 | 
			
		||||
		if err != nil && err != unix.ENOENT {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// FILE_NOFOLLOW means we watch symlinks themselves rather than their
 | 
			
		||||
	// targets.
 | 
			
		||||
	events := unix.FILE_MODIFIED | unix.FILE_ATTRIB | unix.FILE_NOFOLLOW
 | 
			
		||||
	if follow {
 | 
			
		||||
		// We *DO* follow symlinks for explicitly watched entries.
 | 
			
		||||
		events = unix.FILE_MODIFIED | unix.FILE_ATTRIB
 | 
			
		||||
	}
 | 
			
		||||
	return w.port.AssociatePath(path, stat,
 | 
			
		||||
		events,
 | 
			
		||||
		stat.Mode())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) dissociateFile(path string, stat os.FileInfo, unused bool) error {
 | 
			
		||||
	if !w.port.PathIsWatched(path) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return w.port.DissociatePath(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]string, 0, len(w.watches)+len(w.dirs))
 | 
			
		||||
	for pathname := range w.dirs {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	}
 | 
			
		||||
	for pathname := range w.watches {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return entries
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,8 @@
 | 
			
		|||
//go:build linux
 | 
			
		||||
// +build linux
 | 
			
		||||
//go:build linux && !appengine
 | 
			
		||||
// +build linux,!appengine
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -26,9 +29,9 @@ import (
 | 
			
		|||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -42,16 +45,16 @@ import (
 | 
			
		|||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
| 
						 | 
				
			
			@ -67,14 +70,20 @@ import (
 | 
			
		|||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
| 
						 | 
				
			
			@ -101,36 +110,148 @@ type Watcher struct {
 | 
			
		|||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	// Store fd here as os.File.Read() will no longer return on close after
 | 
			
		||||
	// calling Fd(). See: https://github.com/golang/go/issues/26439
 | 
			
		||||
	fd          int
 | 
			
		||||
	mu          sync.Mutex // Map access
 | 
			
		||||
	inotifyFile *os.File
 | 
			
		||||
	watches     map[string]*watch // Map of inotify watches (key: path)
 | 
			
		||||
	paths       map[int]string    // Map of watched paths (key: watch descriptor)
 | 
			
		||||
	done        chan struct{}     // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
	doneResp    chan struct{}     // Channel to respond to Close
 | 
			
		||||
	watches     *watches
 | 
			
		||||
	done        chan struct{} // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
	closeMu     sync.Mutex
 | 
			
		||||
	doneResp    chan struct{} // Channel to respond to Close
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	watches struct {
 | 
			
		||||
		mu   sync.RWMutex
 | 
			
		||||
		wd   map[uint32]*watch // wd → watch
 | 
			
		||||
		path map[string]uint32 // pathname → wd
 | 
			
		||||
	}
 | 
			
		||||
	watch struct {
 | 
			
		||||
		wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
 | 
			
		||||
		flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
 | 
			
		||||
		path  string // Watch path.
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newWatches() *watches {
 | 
			
		||||
	return &watches{
 | 
			
		||||
		wd:   make(map[uint32]*watch),
 | 
			
		||||
		path: make(map[string]uint32),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) len() int {
 | 
			
		||||
	w.mu.RLock()
 | 
			
		||||
	defer w.mu.RUnlock()
 | 
			
		||||
	return len(w.wd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) add(ww *watch) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	w.wd[ww.wd] = ww
 | 
			
		||||
	w.path[ww.path] = ww.wd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) remove(wd uint32) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	delete(w.path, w.wd[wd].path)
 | 
			
		||||
	delete(w.wd, wd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) removePath(path string) (uint32, bool) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	wd, ok := w.path[path]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return 0, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(w.path, path)
 | 
			
		||||
	delete(w.wd, wd)
 | 
			
		||||
 | 
			
		||||
	return wd, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) byPath(path string) *watch {
 | 
			
		||||
	w.mu.RLock()
 | 
			
		||||
	defer w.mu.RUnlock()
 | 
			
		||||
	return w.wd[w.path[path]]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) byWd(wd uint32) *watch {
 | 
			
		||||
	w.mu.RLock()
 | 
			
		||||
	defer w.mu.RUnlock()
 | 
			
		||||
	return w.wd[wd]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	var existing *watch
 | 
			
		||||
	wd, ok := w.path[path]
 | 
			
		||||
	if ok {
 | 
			
		||||
		existing = w.wd[wd]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	upd, err := f(existing)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if upd != nil {
 | 
			
		||||
		w.wd[upd.wd] = upd
 | 
			
		||||
		w.path[upd.path] = upd.wd
 | 
			
		||||
 | 
			
		||||
		if upd.wd != wd {
 | 
			
		||||
			delete(w.wd, wd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	// Create inotify fd
 | 
			
		||||
	// Need to set the FD to nonblocking mode in order for SetDeadline methods to work
 | 
			
		||||
	// Otherwise, blocking i/o operations won't terminate on close
 | 
			
		||||
	return NewBufferedWatcher(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
 | 
			
		||||
	// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
 | 
			
		||||
	// I/O operations won't terminate on close.
 | 
			
		||||
	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
 | 
			
		||||
	if fd == -1 {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
| 
						 | 
				
			
			@ -139,9 +260,8 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		|||
	w := &Watcher{
 | 
			
		||||
		fd:          fd,
 | 
			
		||||
		inotifyFile: os.NewFile(uintptr(fd), ""),
 | 
			
		||||
		watches:     make(map[string]*watch),
 | 
			
		||||
		paths:       make(map[int]string),
 | 
			
		||||
		Events:      make(chan Event),
 | 
			
		||||
		watches:     newWatches(),
 | 
			
		||||
		Events:      make(chan Event, sz),
 | 
			
		||||
		Errors:      make(chan error),
 | 
			
		||||
		done:        make(chan struct{}),
 | 
			
		||||
		doneResp:    make(chan struct{}),
 | 
			
		||||
| 
						 | 
				
			
			@ -157,8 +277,8 @@ func (w *Watcher) sendEvent(e Event) bool {
 | 
			
		|||
	case w.Events <- e:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns true if the error was sent, or false if watcher is closed.
 | 
			
		||||
| 
						 | 
				
			
			@ -180,17 +300,15 @@ func (w *Watcher) isClosed() bool {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.closeMu.Lock()
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		w.closeMu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send 'close' signal to goroutine, and set the Watcher to closed.
 | 
			
		||||
	close(w.done)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	w.closeMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Causes any blocking reads to return with an error, provided the file
 | 
			
		||||
	// still supports deadline operations.
 | 
			
		||||
| 
						 | 
				
			
			@ -207,17 +325,21 @@ func (w *Watcher) Close() error {
 | 
			
		|||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
| 
						 | 
				
			
			@ -227,44 +349,59 @@ func (w *Watcher) Close() error {
 | 
			
		|||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return errors.New("inotify instance already closed")
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	_ = getOptions(opts...)
 | 
			
		||||
 | 
			
		||||
	var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
 | 
			
		||||
		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
 | 
			
		||||
		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	watchEntry := w.watches[name]
 | 
			
		||||
	if watchEntry != nil {
 | 
			
		||||
		flags |= watchEntry.flags | unix.IN_MASK_ADD
 | 
			
		||||
	}
 | 
			
		||||
	wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
 | 
			
		||||
	if wd == -1 {
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
	return w.watches.updatePath(name, func(existing *watch) (*watch, error) {
 | 
			
		||||
		if existing != nil {
 | 
			
		||||
			flags |= existing.flags | unix.IN_MASK_ADD
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if watchEntry == nil {
 | 
			
		||||
		w.watches[name] = &watch{wd: uint32(wd), flags: flags}
 | 
			
		||||
		w.paths[wd] = name
 | 
			
		||||
	} else {
 | 
			
		||||
		watchEntry.wd = uint32(wd)
 | 
			
		||||
		watchEntry.flags = flags
 | 
			
		||||
	}
 | 
			
		||||
		wd, err := unix.InotifyAddWatch(w.fd, name, flags)
 | 
			
		||||
		if wd == -1 {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
		if existing == nil {
 | 
			
		||||
			return &watch{
 | 
			
		||||
				wd:    uint32(wd),
 | 
			
		||||
				path:  name,
 | 
			
		||||
				flags: flags,
 | 
			
		||||
			}, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		existing.wd = uint32(wd)
 | 
			
		||||
		existing.flags = flags
 | 
			
		||||
		return existing, nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove stops monitoring the path for changes.
 | 
			
		||||
| 
						 | 
				
			
			@ -273,32 +410,22 @@ func (w *Watcher) Add(name string) error {
 | 
			
		|||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return w.remove(filepath.Clean(name))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	// Fetch the watch.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	watch, ok := w.watches[name]
 | 
			
		||||
 | 
			
		||||
	// Remove it from inotify.
 | 
			
		||||
func (w *Watcher) remove(name string) error {
 | 
			
		||||
	wd, ok := w.watches.removePath(name)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We successfully removed the watch if InotifyRmWatch doesn't return an
 | 
			
		||||
	// error, we need to clean up our internal state to ensure it matches
 | 
			
		||||
	// inotify's kernel state.
 | 
			
		||||
	delete(w.paths, int(watch.wd))
 | 
			
		||||
	delete(w.watches, name)
 | 
			
		||||
 | 
			
		||||
	// inotify_rm_watch will return EINVAL if the file has been deleted;
 | 
			
		||||
	// the inotify will already have been removed.
 | 
			
		||||
	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
 | 
			
		||||
	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
 | 
			
		||||
	// so that EINVAL means that the wd is being rm_watch()ed or its file removed
 | 
			
		||||
	// by another thread and we have not received IN_IGNORE event.
 | 
			
		||||
	success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
 | 
			
		||||
	success, errno := unix.InotifyRmWatch(w.fd, wd)
 | 
			
		||||
	if success == -1 {
 | 
			
		||||
		// TODO: Perhaps it's not helpful to return an error here in every case;
 | 
			
		||||
		//       The only two possible errors are:
 | 
			
		||||
| 
						 | 
				
			
			@ -312,26 +439,26 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		|||
		//         are watching is deleted.
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	entries := make([]string, 0, len(w.watches))
 | 
			
		||||
	for pathname := range w.watches {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return entries
 | 
			
		||||
}
 | 
			
		||||
	entries := make([]string, 0, w.watches.len())
 | 
			
		||||
	w.watches.mu.RLock()
 | 
			
		||||
	for pathname := range w.watches.path {
 | 
			
		||||
		entries = append(entries, pathname)
 | 
			
		||||
	}
 | 
			
		||||
	w.watches.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
type watch struct {
 | 
			
		||||
	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
 | 
			
		||||
	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
 | 
			
		||||
	return entries
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents reads from the inotify file descriptor, converts the
 | 
			
		||||
| 
						 | 
				
			
			@ -367,14 +494,11 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
		if n < unix.SizeofInotifyEvent {
 | 
			
		||||
			var err error
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				// If EOF is received. This should really never happen.
 | 
			
		||||
				err = io.EOF
 | 
			
		||||
				err = io.EOF // If EOF is received. This should really never happen.
 | 
			
		||||
			} else if n < 0 {
 | 
			
		||||
				// If an error occurred while reading.
 | 
			
		||||
				err = errno
 | 
			
		||||
				err = errno // If an error occurred while reading.
 | 
			
		||||
			} else {
 | 
			
		||||
				// Read was too short.
 | 
			
		||||
				err = errors.New("notify: short read in readEvents()")
 | 
			
		||||
				err = errors.New("notify: short read in readEvents()") // Read was too short.
 | 
			
		||||
			}
 | 
			
		||||
			if !w.sendError(err) {
 | 
			
		||||
				return
 | 
			
		||||
| 
						 | 
				
			
			@ -403,18 +527,29 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
			// doesn't append the filename to the event, but we would like to always fill the
 | 
			
		||||
			// the "Name" field with a valid filename. We retrieve the path of the watch from
 | 
			
		||||
			// the "paths" map.
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			name, ok := w.paths[int(raw.Wd)]
 | 
			
		||||
			// IN_DELETE_SELF occurs when the file/directory being watched is removed.
 | 
			
		||||
			// This is a sign to clean up the maps, otherwise we are no longer in sync
 | 
			
		||||
			// with the inotify kernel state which has already deleted the watch
 | 
			
		||||
			// automatically.
 | 
			
		||||
			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
 | 
			
		||||
				delete(w.paths, int(raw.Wd))
 | 
			
		||||
				delete(w.watches, name)
 | 
			
		||||
			}
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
			watch := w.watches.byWd(uint32(raw.Wd))
 | 
			
		||||
 | 
			
		||||
			// inotify will automatically remove the watch on deletes; just need
 | 
			
		||||
			// to clean our state here.
 | 
			
		||||
			if watch != nil && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
 | 
			
		||||
				w.watches.remove(watch.wd)
 | 
			
		||||
			}
 | 
			
		||||
			// We can't really update the state when a watched path is moved;
 | 
			
		||||
			// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
 | 
			
		||||
			// the watch.
 | 
			
		||||
			if watch != nil && mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
 | 
			
		||||
				err := w.remove(watch.path)
 | 
			
		||||
				if err != nil && !errors.Is(err, ErrNonExistentWatch) {
 | 
			
		||||
					if !w.sendError(err) {
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var name string
 | 
			
		||||
			if watch != nil {
 | 
			
		||||
				name = watch.path
 | 
			
		||||
			}
 | 
			
		||||
			if nameLen > 0 {
 | 
			
		||||
				// Point "bytes" at the first byte of the filename
 | 
			
		||||
				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,14 @@
 | 
			
		|||
//go:build freebsd || openbsd || netbsd || dragonfly || darwin
 | 
			
		||||
// +build freebsd openbsd netbsd dragonfly darwin
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
| 
						 | 
				
			
			@ -24,9 +26,9 @@ import (
 | 
			
		|||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -40,16 +42,16 @@ import (
 | 
			
		|||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
| 
						 | 
				
			
			@ -65,14 +67,20 @@ import (
 | 
			
		|||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
| 
						 | 
				
			
			@ -99,18 +107,27 @@ type Watcher struct {
 | 
			
		|||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	done         chan struct{}
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +150,18 @@ type pathInfo struct {
 | 
			
		|||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return NewBufferedWatcher(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
 | 
			
		||||
	kq, closepipe, err := newKqueue()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +176,7 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		|||
		paths:        make(map[int]pathInfo),
 | 
			
		||||
		fileExists:   make(map[string]struct{}),
 | 
			
		||||
		userWatches:  make(map[string]struct{}),
 | 
			
		||||
		Events:       make(chan Event),
 | 
			
		||||
		Events:       make(chan Event, sz),
 | 
			
		||||
		Errors:       make(chan error),
 | 
			
		||||
		done:         make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -197,8 +226,8 @@ func (w *Watcher) sendEvent(e Event) bool {
 | 
			
		|||
	case w.Events <- e:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns true if the error was sent, or false if watcher is closed.
 | 
			
		||||
| 
						 | 
				
			
			@ -207,11 +236,11 @@ func (w *Watcher) sendError(err error) bool {
 | 
			
		|||
	case w.Errors <- err:
 | 
			
		||||
		return true
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
| 
						 | 
				
			
			@ -239,17 +268,21 @@ func (w *Watcher) Close() error {
 | 
			
		|||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
| 
						 | 
				
			
			@ -259,15 +292,28 @@ func (w *Watcher) Close() error {
 | 
			
		|||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	_ = getOptions(opts...)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.userWatches[name] = struct{}{}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
| 
						 | 
				
			
			@ -281,9 +327,19 @@ func (w *Watcher) Add(name string) error {
 | 
			
		|||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	return w.remove(name, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) remove(name string, unwatchFiles bool) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	watchfd, ok := w.watches[name]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	if !ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +371,7 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		|||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Find all watched paths that are in this directory that are not external.
 | 
			
		||||
	if isDir {
 | 
			
		||||
	if unwatchFiles && isDir {
 | 
			
		||||
		var pathsToRemove []string
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		for fd := range w.watchesByDir[name] {
 | 
			
		||||
| 
						 | 
				
			
			@ -326,20 +382,25 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		|||
		}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		for _, name := range pathsToRemove {
 | 
			
		||||
			// Since these are internal, not much sense in propagating error
 | 
			
		||||
			// to the user, as that will just confuse them with an error about
 | 
			
		||||
			// a path they did not explicitly watch themselves.
 | 
			
		||||
			// Since these are internal, not much sense in propagating error to
 | 
			
		||||
			// the user, as that will just confuse them with an error about a
 | 
			
		||||
			// path they did not explicitly watch themselves.
 | 
			
		||||
			w.Remove(name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entries := make([]string, 0, len(w.userWatches))
 | 
			
		||||
	for pathname := range w.userWatches {
 | 
			
		||||
| 
						 | 
				
			
			@ -352,18 +413,18 @@ func (w *Watcher) WatchList() []string {
 | 
			
		|||
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
 | 
			
		||||
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
 | 
			
		||||
 | 
			
		||||
// addWatch adds name to the watched file set.
 | 
			
		||||
// The flags are interpreted as described in kevent(2).
 | 
			
		||||
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
 | 
			
		||||
// addWatch adds name to the watched file set; the flags are interpreted as
 | 
			
		||||
// described in kevent(2).
 | 
			
		||||
//
 | 
			
		||||
// Returns the real path to the file which was added, with symlinks resolved.
 | 
			
		||||
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
	var isDir bool
 | 
			
		||||
	// Make ./name and name equivalent
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return "", errors.New("kevent instance already closed")
 | 
			
		||||
		return "", ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
	watchfd, alreadyWatching := w.watches[name]
 | 
			
		||||
	// We already have a watch, but we can still override flags.
 | 
			
		||||
| 
						 | 
				
			
			@ -383,27 +444,30 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		|||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Follow Symlinks
 | 
			
		||||
		//
 | 
			
		||||
		// Linux can add unresolvable symlinks to the watch list without issue,
 | 
			
		||||
		// and Windows can't do symlinks period. To maintain consistency, we
 | 
			
		||||
		// will act like everything is fine if the link can't be resolved.
 | 
			
		||||
		// There will simply be no file events for broken symlinks. Hence the
 | 
			
		||||
		// returns of nil on errors.
 | 
			
		||||
		// Follow Symlinks.
 | 
			
		||||
		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
 | 
			
		||||
			name, err = filepath.EvalSymlinks(name)
 | 
			
		||||
			link, err := os.Readlink(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				// Return nil because Linux can add unresolvable symlinks to the
 | 
			
		||||
				// watch list without problems, so maintain consistency with
 | 
			
		||||
				// that. There will be no file events for broken symlinks.
 | 
			
		||||
				// TODO: more specific check; returns os.PathError; ENOENT?
 | 
			
		||||
				return "", nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			_, alreadyWatching = w.watches[name]
 | 
			
		||||
			_, alreadyWatching = w.watches[link]
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
			if alreadyWatching {
 | 
			
		||||
				return name, nil
 | 
			
		||||
				// Add to watches so we don't get spurious Create events later
 | 
			
		||||
				// on when we diff the directories.
 | 
			
		||||
				w.watches[name] = 0
 | 
			
		||||
				w.fileExists[name] = struct{}{}
 | 
			
		||||
				return link, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			name = link
 | 
			
		||||
			fi, err = os.Lstat(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", nil
 | 
			
		||||
| 
						 | 
				
			
			@ -411,7 +475,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		// Retry on EINTR; open() can return EINTR in practice on macOS.
 | 
			
		||||
		// See #354, and go issues 11180 and 39237.
 | 
			
		||||
		// See #354, and Go issues 11180 and 39237.
 | 
			
		||||
		for {
 | 
			
		||||
			watchfd, err = unix.Open(name, openMode, 0)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -444,14 +508,13 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		|||
			w.watchesByDir[parentName] = watchesByDir
 | 
			
		||||
		}
 | 
			
		||||
		watchesByDir[watchfd] = struct{}{}
 | 
			
		||||
 | 
			
		||||
		w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isDir {
 | 
			
		||||
		// Watch the directory if it has not been watched before,
 | 
			
		||||
		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
 | 
			
		||||
		// Watch the directory if it has not been watched before, or if it was
 | 
			
		||||
		// watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
 | 
			
		||||
		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
 | 
			
		||||
| 
						 | 
				
			
			@ -473,13 +536,10 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		|||
// Event values that it sends down the Events channel.
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := unix.Close(w.kq)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			w.Errors <- err
 | 
			
		||||
		}
 | 
			
		||||
		unix.Close(w.closepipe[0])
 | 
			
		||||
		close(w.Events)
 | 
			
		||||
		close(w.Errors)
 | 
			
		||||
		_ = unix.Close(w.kq)
 | 
			
		||||
		unix.Close(w.closepipe[0])
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	eventBuffer := make([]unix.Kevent_t, 10)
 | 
			
		||||
| 
						 | 
				
			
			@ -513,18 +573,8 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
 | 
			
		||||
			event := w.newEvent(path.name, mask)
 | 
			
		||||
 | 
			
		||||
			if path.isDir && !event.Has(Remove) {
 | 
			
		||||
				// Double check to make sure the directory exists. This can
 | 
			
		||||
				// happen when we do a rm -fr on a recursively watched folders
 | 
			
		||||
				// and we receive a modification event first but the folder has
 | 
			
		||||
				// been deleted and later receive the delete event.
 | 
			
		||||
				if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
 | 
			
		||||
					event.Op |= Remove
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if event.Has(Rename) || event.Has(Remove) {
 | 
			
		||||
				w.Remove(event.Name)
 | 
			
		||||
				w.remove(event.Name, false)
 | 
			
		||||
				w.mu.Lock()
 | 
			
		||||
				delete(w.fileExists, event.Name)
 | 
			
		||||
				w.mu.Unlock()
 | 
			
		||||
| 
						 | 
				
			
			@ -540,26 +590,30 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			if event.Has(Remove) {
 | 
			
		||||
				// Look for a file that may have overwritten this.
 | 
			
		||||
				// For example, mv f1 f2 will delete f2, then create f2.
 | 
			
		||||
				// Look for a file that may have overwritten this; for example,
 | 
			
		||||
				// mv f1 f2 will delete f2, then create f2.
 | 
			
		||||
				if path.isDir {
 | 
			
		||||
					fileDir := filepath.Clean(event.Name)
 | 
			
		||||
					w.mu.Lock()
 | 
			
		||||
					_, found := w.watches[fileDir]
 | 
			
		||||
					w.mu.Unlock()
 | 
			
		||||
					if found {
 | 
			
		||||
						// make sure the directory exists before we watch for changes. When we
 | 
			
		||||
						// do a recursive watch and perform rm -fr, the parent directory might
 | 
			
		||||
						// have gone missing, ignore the missing directory and let the
 | 
			
		||||
						// upcoming delete event remove the watch from the parent directory.
 | 
			
		||||
						if _, err := os.Lstat(fileDir); err == nil {
 | 
			
		||||
							w.sendDirectoryChangeEvents(fileDir)
 | 
			
		||||
						err := w.sendDirectoryChangeEvents(fileDir)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							if !w.sendError(err) {
 | 
			
		||||
								closed = true
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					filePath := filepath.Clean(event.Name)
 | 
			
		||||
					if fileInfo, err := os.Lstat(filePath); err == nil {
 | 
			
		||||
						w.sendFileCreatedEventIfNew(filePath, fileInfo)
 | 
			
		||||
					if fi, err := os.Lstat(filePath); err == nil {
 | 
			
		||||
						err := w.sendFileCreatedEventIfNew(filePath, fi)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							if !w.sendError(err) {
 | 
			
		||||
								closed = true
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -582,21 +636,31 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
 | 
			
		|||
	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	// No point sending a write and delete event at the same time: if it's gone,
 | 
			
		||||
	// then it's gone.
 | 
			
		||||
	if e.Op.Has(Write) && e.Op.Has(Remove) {
 | 
			
		||||
		e.Op &^= Write
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
 | 
			
		||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		||||
	// Get all files
 | 
			
		||||
	files, err := ioutil.ReadDir(dirPath)
 | 
			
		||||
	files, err := os.ReadDir(dirPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, fileInfo := range files {
 | 
			
		||||
		path := filepath.Join(dirPath, fileInfo.Name())
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		path := filepath.Join(dirPath, f.Name())
 | 
			
		||||
 | 
			
		||||
		cleanPath, err := w.internalWatch(path, fileInfo)
 | 
			
		||||
		fi, err := f.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("%q: %w", path, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cleanPath, err := w.internalWatch(path, fi)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// No permission to read the file; that's not a problem: just skip.
 | 
			
		||||
			// But do add it to w.fileExists to prevent it from being picked up
 | 
			
		||||
| 
						 | 
				
			
			@ -606,7 +670,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		|||
			case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
 | 
			
		||||
				cleanPath = filepath.Clean(path)
 | 
			
		||||
			default:
 | 
			
		||||
				return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
 | 
			
		||||
				return fmt.Errorf("%q: %w", path, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -622,26 +686,37 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		|||
//
 | 
			
		||||
// This functionality is to have the BSD watcher match the inotify, which sends
 | 
			
		||||
// a create event for files created in a watched directory.
 | 
			
		||||
func (w *Watcher) sendDirectoryChangeEvents(dir string) {
 | 
			
		||||
	// Get all files
 | 
			
		||||
	files, err := ioutil.ReadDir(dir)
 | 
			
		||||
func (w *Watcher) sendDirectoryChangeEvents(dir string) error {
 | 
			
		||||
	files, err := os.ReadDir(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) {
 | 
			
		||||
			return
 | 
			
		||||
		// Directory no longer exists: we can ignore this safely. kqueue will
 | 
			
		||||
		// still give us the correct events.
 | 
			
		||||
		if errors.Is(err, os.ErrNotExist) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Search for new files
 | 
			
		||||
	for _, fi := range files {
 | 
			
		||||
		err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		fi, err := f.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
			return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Don't need to send an error if this file isn't readable.
 | 
			
		||||
			if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
 | 
			
		||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
 | 
			
		||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fi os.FileInfo) (err error) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	_, doesExist := w.fileExists[filePath]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
| 
						 | 
				
			
			@ -652,7 +727,7 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// like watchDirectoryFiles (but without doing another ReadDir)
 | 
			
		||||
	filePath, err = w.internalWatch(filePath, fileInfo)
 | 
			
		||||
	filePath, err = w.internalWatch(filePath, fi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -664,10 +739,10 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
 | 
			
		||||
	if fileInfo.IsDir() {
 | 
			
		||||
		// mimic Linux providing delete events for subdirectories
 | 
			
		||||
		// but preserve the flags used if currently watching subdirectory
 | 
			
		||||
func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
 | 
			
		||||
	if fi.IsDir() {
 | 
			
		||||
		// mimic Linux providing delete events for subdirectories, but preserve
 | 
			
		||||
		// the flags used if currently watching subdirectory
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		flags := w.dirFlags[name]
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,39 +1,169 @@
 | 
			
		|||
//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows
 | 
			
		||||
// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
 | 
			
		||||
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
 | 
			
		||||
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
 | 
			
		||||
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
import "errors"
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of files, delivering events to a channel.
 | 
			
		||||
type Watcher struct{}
 | 
			
		||||
// Watcher watches a set of paths, delivering events on a channel.
 | 
			
		||||
//
 | 
			
		||||
// A watcher should not be copied (e.g. pass it by pointer, rather than by
 | 
			
		||||
// value).
 | 
			
		||||
//
 | 
			
		||||
// # Linux notes
 | 
			
		||||
//
 | 
			
		||||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
 | 
			
		||||
// for the number of watches per user, and fs.inotify.max_user_instances
 | 
			
		||||
// specifies the maximum number of inotify instances per user. Every Watcher you
 | 
			
		||||
// create is an "instance", and every path you add is a "watch".
 | 
			
		||||
//
 | 
			
		||||
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
 | 
			
		||||
// /proc/sys/fs/inotify/max_user_instances
 | 
			
		||||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
//
 | 
			
		||||
// # kqueue notes (macOS, BSD)
 | 
			
		||||
//
 | 
			
		||||
// kqueue requires opening a file descriptor for every file that's being watched;
 | 
			
		||||
// so if you're watching a directory with five files then that's six file
 | 
			
		||||
// descriptors. You will run in to your system's "max open files" limit faster on
 | 
			
		||||
// these platforms.
 | 
			
		||||
//
 | 
			
		||||
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
 | 
			
		||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
	// fsnotify can send the following events; a "path" here can refer to a
 | 
			
		||||
	// file, directory, symbolic link, or special file like a FIFO.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Create    A new path was created; this may be followed by one
 | 
			
		||||
	//                      or more Write events if data also gets written to a
 | 
			
		||||
	//                      file.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Remove    A path was removed.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Rename    A path was renamed. A rename is always sent with the
 | 
			
		||||
	//                      old path as Event.Name, and a Create event will be
 | 
			
		||||
	//                      sent with the new name. Renames are only sent for
 | 
			
		||||
	//                      paths that are currently watched; e.g. moving an
 | 
			
		||||
	//                      unmonitored file into a monitored directory will
 | 
			
		||||
	//                      show up as just a Create. Similarly, renaming a file
 | 
			
		||||
	//                      to outside a monitored directory will show up as
 | 
			
		||||
	//                      only a Rename.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Write     A file or named pipe was written to. A Truncate will
 | 
			
		||||
	//                      also trigger a Write. A single "write action"
 | 
			
		||||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
 | 
			
		||||
	return nil, errors.New("fsnotify not supported on the current platform")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
func NewBufferedWatcher(sz uint) (*Watcher, error) { return NewWatcher() }
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error { return nil }
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string { return nil }
 | 
			
		||||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
| 
						 | 
				
			
			@ -43,17 +173,26 @@ func (w *Watcher) Close() error {
 | 
			
		|||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return nil }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error { return nil }
 | 
			
		||||
 | 
			
		||||
// Remove stops monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +200,6 @@ func (w *Watcher) Add(name string) error {
 | 
			
		|||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error { return nil }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,13 @@
 | 
			
		|||
//go:build windows
 | 
			
		||||
// +build windows
 | 
			
		||||
 | 
			
		||||
// Windows backend based on ReadDirectoryChangesW()
 | 
			
		||||
//
 | 
			
		||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
 | 
			
		||||
//
 | 
			
		||||
// Note: the documentation on the Watcher type and methods is generated from
 | 
			
		||||
// mkdoc.zsh
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
| 
						 | 
				
			
			@ -27,9 +34,9 @@ import (
 | 
			
		|||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -43,16 +50,16 @@ import (
 | 
			
		|||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
| 
						 | 
				
			
			@ -68,14 +75,20 @@ import (
 | 
			
		|||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\path\to\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	// Events sends the filesystem change events.
 | 
			
		||||
	//
 | 
			
		||||
| 
						 | 
				
			
			@ -102,31 +115,52 @@ type Watcher struct {
 | 
			
		|||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
	Events chan Event
 | 
			
		||||
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
	Errors chan error
 | 
			
		||||
 | 
			
		||||
	port  windows.Handle // Handle to completion port
 | 
			
		||||
	input chan *input    // Inputs to the reader are sent on this channel
 | 
			
		||||
	quit  chan chan<- error
 | 
			
		||||
 | 
			
		||||
	mu       sync.Mutex // Protects access to watches, isClosed
 | 
			
		||||
	watches  watchMap   // Map of watches (key: i-number)
 | 
			
		||||
	isClosed bool       // Set to true when Close() is first called
 | 
			
		||||
	mu      sync.Mutex // Protects access to watches, closed
 | 
			
		||||
	watches watchMap   // Map of watches (key: i-number)
 | 
			
		||||
	closed  bool       // Set to true when Close() is first called
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher creates a new Watcher.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return NewBufferedWatcher(50)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
 | 
			
		||||
	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, os.NewSyscallError("CreateIoCompletionPort", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +169,7 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		|||
		port:    port,
 | 
			
		||||
		watches: make(watchMap),
 | 
			
		||||
		input:   make(chan *input, 1),
 | 
			
		||||
		Events:  make(chan Event, 50),
 | 
			
		||||
		Events:  make(chan Event, sz),
 | 
			
		||||
		Errors:  make(chan error),
 | 
			
		||||
		quit:    make(chan chan<- error, 1),
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +177,12 @@ func NewWatcher() (*Watcher, error) {
 | 
			
		|||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) isClosed() bool {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	return w.closed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
 | 
			
		||||
	if mask == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -167,14 +207,14 @@ func (w *Watcher) sendError(err error) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	w.isClosed = true
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.closed = true
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Send "quit" message to the reader goroutine
 | 
			
		||||
| 
						 | 
				
			
			@ -188,17 +228,21 @@ func (w *Watcher) Close() error {
 | 
			
		|||
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
| 
						 | 
				
			
			@ -208,27 +252,41 @@ func (w *Watcher) Close() error {
 | 
			
		|||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return errors.New("watcher already closed")
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
 | 
			
		||||
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return ErrClosed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	with := getOptions(opts...)
 | 
			
		||||
	if with.bufsize < 4096 {
 | 
			
		||||
		return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
 | 
			
		||||
	}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	in := &input{
 | 
			
		||||
		op:    opAddWatch,
 | 
			
		||||
		path:  filepath.Clean(name),
 | 
			
		||||
		flags: sysFSALLEVENTS,
 | 
			
		||||
		reply: make(chan error),
 | 
			
		||||
		op:      opAddWatch,
 | 
			
		||||
		path:    filepath.Clean(name),
 | 
			
		||||
		flags:   sysFSALLEVENTS,
 | 
			
		||||
		reply:   make(chan error),
 | 
			
		||||
		bufsize: with.bufsize,
 | 
			
		||||
	}
 | 
			
		||||
	w.input <- in
 | 
			
		||||
	if err := w.wakeupReader(); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +301,13 @@ func (w *Watcher) Add(name string) error {
 | 
			
		|||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	in := &input{
 | 
			
		||||
		op:    opRemoveWatch,
 | 
			
		||||
		path:  filepath.Clean(name),
 | 
			
		||||
| 
						 | 
				
			
			@ -256,8 +320,15 @@ func (w *Watcher) Remove(name string) error {
 | 
			
		|||
	return <-in.reply
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
func (w *Watcher) WatchList() []string {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -279,7 +350,6 @@ func (w *Watcher) WatchList() []string {
 | 
			
		|||
// This should all be removed at some point, and just use windows.FILE_NOTIFY_*
 | 
			
		||||
const (
 | 
			
		||||
	sysFSALLEVENTS  = 0xfff
 | 
			
		||||
	sysFSATTRIB     = 0x4
 | 
			
		||||
	sysFSCREATE     = 0x100
 | 
			
		||||
	sysFSDELETE     = 0x200
 | 
			
		||||
	sysFSDELETESELF = 0x400
 | 
			
		||||
| 
						 | 
				
			
			@ -305,9 +375,6 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
 | 
			
		|||
	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
 | 
			
		||||
		e.Op |= Rename
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSATTRIB == sysFSATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -321,10 +388,11 @@ const (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
type input struct {
 | 
			
		||||
	op    int
 | 
			
		||||
	path  string
 | 
			
		||||
	flags uint32
 | 
			
		||||
	reply chan error
 | 
			
		||||
	op      int
 | 
			
		||||
	path    string
 | 
			
		||||
	flags   uint32
 | 
			
		||||
	bufsize int
 | 
			
		||||
	reply   chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type inode struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -334,13 +402,14 @@ type inode struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type watch struct {
 | 
			
		||||
	ov     windows.Overlapped
 | 
			
		||||
	ino    *inode            // i-number
 | 
			
		||||
	path   string            // Directory path
 | 
			
		||||
	mask   uint64            // Directory itself is being watched with these notify flags
 | 
			
		||||
	names  map[string]uint64 // Map of names being watched and their notify flags
 | 
			
		||||
	rename string            // Remembers the old name while renaming a file
 | 
			
		||||
	buf    [65536]byte       // 64K buffer
 | 
			
		||||
	ov      windows.Overlapped
 | 
			
		||||
	ino     *inode            // i-number
 | 
			
		||||
	recurse bool              // Recursive watch?
 | 
			
		||||
	path    string            // Directory path
 | 
			
		||||
	mask    uint64            // Directory itself is being watched with these notify flags
 | 
			
		||||
	names   map[string]uint64 // Map of names being watched and their notify flags
 | 
			
		||||
	rename  string            // Remembers the old name while renaming a file
 | 
			
		||||
	buf     []byte            // buffer, allocated later
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
| 
						 | 
				
			
			@ -413,7 +482,10 @@ func (m watchMap) set(ino *inode, watch *watch) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		||||
func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
 | 
			
		||||
	//pathname, recurse := recursivePath(pathname)
 | 
			
		||||
	recurse := false
 | 
			
		||||
 | 
			
		||||
	dir, err := w.getDir(pathname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -433,9 +505,11 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		|||
			return os.NewSyscallError("CreateIoCompletionPort", err)
 | 
			
		||||
		}
 | 
			
		||||
		watchEntry = &watch{
 | 
			
		||||
			ino:   ino,
 | 
			
		||||
			path:  dir,
 | 
			
		||||
			names: make(map[string]uint64),
 | 
			
		||||
			ino:     ino,
 | 
			
		||||
			path:    dir,
 | 
			
		||||
			names:   make(map[string]uint64),
 | 
			
		||||
			recurse: recurse,
 | 
			
		||||
			buf:     make([]byte, bufsize),
 | 
			
		||||
		}
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.watches.set(ino, watchEntry)
 | 
			
		||||
| 
						 | 
				
			
			@ -465,6 +539,8 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		|||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) remWatch(pathname string) error {
 | 
			
		||||
	pathname, recurse := recursivePath(pathname)
 | 
			
		||||
 | 
			
		||||
	dir, err := w.getDir(pathname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -478,6 +554,10 @@ func (w *Watcher) remWatch(pathname string) error {
 | 
			
		|||
	watch := w.watches.get(ino)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if recurse && !watch.recurse {
 | 
			
		||||
		return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = windows.CloseHandle(ino.handle)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.sendError(os.NewSyscallError("CloseHandle", err))
 | 
			
		||||
| 
						 | 
				
			
			@ -535,8 +615,11 @@ func (w *Watcher) startRead(watch *watch) error {
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
 | 
			
		||||
		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
 | 
			
		||||
	// We need to pass the array, rather than the slice.
 | 
			
		||||
	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
 | 
			
		||||
	rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
 | 
			
		||||
		(*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
 | 
			
		||||
		watch.recurse, mask, nil, &watch.ov, 0)
 | 
			
		||||
	if rdErr != nil {
 | 
			
		||||
		err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
 | 
			
		||||
		if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -563,9 +646,8 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
	runtime.LockOSThread()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		// This error is handled after the watch == nil check below.
 | 
			
		||||
		qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
 | 
			
		||||
		// This error is handled after the watch == nil check below. NOTE: this
 | 
			
		||||
		// seems odd, note sure if it's correct.
 | 
			
		||||
 | 
			
		||||
		watch := (*watch)(unsafe.Pointer(ov))
 | 
			
		||||
		if watch == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -595,7 +677,7 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
			case in := <-w.input:
 | 
			
		||||
				switch in.op {
 | 
			
		||||
				case opAddWatch:
 | 
			
		||||
					in.reply <- w.addWatch(in.path, uint64(in.flags))
 | 
			
		||||
					in.reply <- w.addWatch(in.path, uint64(in.flags), in.bufsize)
 | 
			
		||||
				case opRemoveWatch:
 | 
			
		||||
					in.reply <- w.remWatch(in.path)
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -605,6 +687,8 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		switch qErr {
 | 
			
		||||
		case nil:
 | 
			
		||||
			// No error
 | 
			
		||||
		case windows.ERROR_MORE_DATA:
 | 
			
		||||
			if watch == nil {
 | 
			
		||||
				w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
 | 
			
		||||
| 
						 | 
				
			
			@ -626,13 +710,12 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
		default:
 | 
			
		||||
			w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
 | 
			
		||||
			continue
 | 
			
		||||
		case nil:
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var offset uint32
 | 
			
		||||
		for {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				w.sendError(errors.New("short read in readEvents()"))
 | 
			
		||||
				w.sendError(ErrEventOverflow)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -703,8 +786,9 @@ func (w *Watcher) readEvents() {
 | 
			
		|||
 | 
			
		||||
			// Error!
 | 
			
		||||
			if offset >= n {
 | 
			
		||||
				//lint:ignore ST1005 Windows should be capitalized
 | 
			
		||||
				w.sendError(errors.New(
 | 
			
		||||
					"Windows system assumed buffer larger than it is, events have likely been missed."))
 | 
			
		||||
					"Windows system assumed buffer larger than it is, events have likely been missed"))
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -720,9 +804,6 @@ func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
 | 
			
		|||
	if mask&sysFSMODIFY != 0 {
 | 
			
		||||
		m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSATTRIB != 0 {
 | 
			
		||||
		m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES
 | 
			
		||||
	}
 | 
			
		||||
	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
 | 
			
		||||
		m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,18 @@
 | 
			
		|||
//go:build !plan9
 | 
			
		||||
// +build !plan9
 | 
			
		||||
 | 
			
		||||
// Package fsnotify provides a cross-platform interface for file system
 | 
			
		||||
// notifications.
 | 
			
		||||
//
 | 
			
		||||
// Currently supported systems:
 | 
			
		||||
//
 | 
			
		||||
//	Linux 2.6.32+    via inotify
 | 
			
		||||
//	BSD, macOS       via kqueue
 | 
			
		||||
//	Windows          via ReadDirectoryChangesW
 | 
			
		||||
//	illumos          via FEN
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,34 +38,52 @@ type Op uint32
 | 
			
		|||
// The operations fsnotify can trigger; see the documentation on [Watcher] for a
 | 
			
		||||
// full description, and check them with [Event.Has].
 | 
			
		||||
const (
 | 
			
		||||
	// A new pathname was created.
 | 
			
		||||
	Create Op = 1 << iota
 | 
			
		||||
 | 
			
		||||
	// The pathname was written to; this does *not* mean the write has finished,
 | 
			
		||||
	// and a write can be followed by more writes.
 | 
			
		||||
	Write
 | 
			
		||||
 | 
			
		||||
	// The path was removed; any watches on it will be removed. Some "remove"
 | 
			
		||||
	// operations may trigger a Rename if the file is actually moved (for
 | 
			
		||||
	// example "remove to trash" is often a rename).
 | 
			
		||||
	Remove
 | 
			
		||||
 | 
			
		||||
	// The path was renamed to something else; any watched on it will be
 | 
			
		||||
	// removed.
 | 
			
		||||
	Rename
 | 
			
		||||
 | 
			
		||||
	// File attributes were changed.
 | 
			
		||||
	//
 | 
			
		||||
	// It's generally not recommended to take action on this event, as it may
 | 
			
		||||
	// get triggered very frequently by some software. For example, Spotlight
 | 
			
		||||
	// indexing on macOS, anti-virus software, backup software, etc.
 | 
			
		||||
	Chmod
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Common errors that can be reported by a watcher
 | 
			
		||||
// Common errors that can be reported.
 | 
			
		||||
var (
 | 
			
		||||
	ErrNonExistentWatch = errors.New("can't remove non-existent watcher")
 | 
			
		||||
	ErrEventOverflow    = errors.New("fsnotify queue overflow")
 | 
			
		||||
	ErrNonExistentWatch = errors.New("fsnotify: can't remove non-existent watch")
 | 
			
		||||
	ErrEventOverflow    = errors.New("fsnotify: queue or buffer overflow")
 | 
			
		||||
	ErrClosed           = errors.New("fsnotify: watcher already closed")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (op Op) String() string {
 | 
			
		||||
func (o Op) String() string {
 | 
			
		||||
	var b strings.Builder
 | 
			
		||||
	if op.Has(Create) {
 | 
			
		||||
	if o.Has(Create) {
 | 
			
		||||
		b.WriteString("|CREATE")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Remove) {
 | 
			
		||||
	if o.Has(Remove) {
 | 
			
		||||
		b.WriteString("|REMOVE")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Write) {
 | 
			
		||||
	if o.Has(Write) {
 | 
			
		||||
		b.WriteString("|WRITE")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Rename) {
 | 
			
		||||
	if o.Has(Rename) {
 | 
			
		||||
		b.WriteString("|RENAME")
 | 
			
		||||
	}
 | 
			
		||||
	if op.Has(Chmod) {
 | 
			
		||||
	if o.Has(Chmod) {
 | 
			
		||||
		b.WriteString("|CHMOD")
 | 
			
		||||
	}
 | 
			
		||||
	if b.Len() == 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +93,7 @@ func (op Op) String() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Has reports if this operation has the given operation.
 | 
			
		||||
func (o Op) Has(h Op) bool { return o&h == h }
 | 
			
		||||
func (o Op) Has(h Op) bool { return o&h != 0 }
 | 
			
		||||
 | 
			
		||||
// Has reports if this event has the given operation.
 | 
			
		||||
func (e Event) Has(op Op) bool { return e.Op.Has(op) }
 | 
			
		||||
| 
						 | 
				
			
			@ -79,3 +102,45 @@ func (e Event) Has(op Op) bool { return e.Op.Has(op) }
 | 
			
		|||
func (e Event) String() string {
 | 
			
		||||
	return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	addOpt   func(opt *withOpts)
 | 
			
		||||
	withOpts struct {
 | 
			
		||||
		bufsize int
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var defaultOpts = withOpts{
 | 
			
		||||
	bufsize: 65536, // 64K
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getOptions(opts ...addOpt) withOpts {
 | 
			
		||||
	with := defaultOpts
 | 
			
		||||
	for _, o := range opts {
 | 
			
		||||
		o(&with)
 | 
			
		||||
	}
 | 
			
		||||
	return with
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithBufferSize sets the [ReadDirectoryChangesW] buffer size.
 | 
			
		||||
//
 | 
			
		||||
// This only has effect on Windows systems, and is a no-op for other backends.
 | 
			
		||||
//
 | 
			
		||||
// The default value is 64K (65536 bytes) which is the highest value that works
 | 
			
		||||
// on all filesystems and should be enough for most applications, but if you
 | 
			
		||||
// have a large burst of events it may not be enough. You can increase it if
 | 
			
		||||
// you're hitting "queue or buffer overflow" errors ([ErrEventOverflow]).
 | 
			
		||||
//
 | 
			
		||||
// [ReadDirectoryChangesW]: https://learn.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
 | 
			
		||||
func WithBufferSize(bytes int) addOpt {
 | 
			
		||||
	return func(opt *withOpts) { opt.bufsize = bytes }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check if this path is recursive (ends with "/..." or "\..."), and return the
 | 
			
		||||
// path with the /... stripped.
 | 
			
		||||
func recursivePath(path string) (string, bool) {
 | 
			
		||||
	if filepath.Base(path) == "..." {
 | 
			
		||||
		return filepath.Dir(path), true
 | 
			
		||||
	}
 | 
			
		||||
	return path, false
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,8 +2,8 @@
 | 
			
		|||
[ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1
 | 
			
		||||
setopt err_exit no_unset pipefail extended_glob
 | 
			
		||||
 | 
			
		||||
# Simple script to update the godoc comments on all watchers. Probably took me
 | 
			
		||||
# more time to write this than doing it manually, but ah well 🙃
 | 
			
		||||
# Simple script to update the godoc comments on all watchers so you don't need
 | 
			
		||||
# to update the same comment 5 times.
 | 
			
		||||
 | 
			
		||||
watcher=$(<<EOF
 | 
			
		||||
// Watcher watches a set of paths, delivering events on a channel.
 | 
			
		||||
| 
						 | 
				
			
			@ -16,9 +16,9 @@ watcher=$(<<EOF
 | 
			
		|||
// When a file is removed a Remove event won't be emitted until all file
 | 
			
		||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
 | 
			
		||||
//
 | 
			
		||||
//     fp := os.Open("file")
 | 
			
		||||
//     os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//     fp.Close()               // Triggers Remove
 | 
			
		||||
//	fp := os.Open("file")
 | 
			
		||||
//	os.Remove("file")        // Triggers Chmod
 | 
			
		||||
//	fp.Close()               // Triggers Remove
 | 
			
		||||
//
 | 
			
		||||
// This is the event that inotify sends, so not much can be changed about this.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -32,16 +32,16 @@ watcher=$(<<EOF
 | 
			
		|||
//
 | 
			
		||||
// To increase them you can use sysctl or write the value to the /proc file:
 | 
			
		||||
//
 | 
			
		||||
//     # Default values on Linux 5.18
 | 
			
		||||
//     sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//     sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//	# Default values on Linux 5.18
 | 
			
		||||
//	sysctl fs.inotify.max_user_watches=124983
 | 
			
		||||
//	sysctl fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
 | 
			
		||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
 | 
			
		||||
// your distro's documentation):
 | 
			
		||||
//
 | 
			
		||||
//     fs.inotify.max_user_watches=124983
 | 
			
		||||
//     fs.inotify.max_user_instances=128
 | 
			
		||||
//	fs.inotify.max_user_watches=124983
 | 
			
		||||
//	fs.inotify.max_user_instances=128
 | 
			
		||||
//
 | 
			
		||||
// Reaching the limit will result in a "no space left on device" or "too many open
 | 
			
		||||
// files" error.
 | 
			
		||||
| 
						 | 
				
			
			@ -57,14 +57,20 @@ watcher=$(<<EOF
 | 
			
		|||
// control the maximum number of open files, as well as /etc/login.conf on BSD
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// # macOS notes
 | 
			
		||||
// # Windows notes
 | 
			
		||||
//
 | 
			
		||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
 | 
			
		||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
 | 
			
		||||
// Settings" until we have a native FSEvents implementation (see [#11]).
 | 
			
		||||
// Paths can be added as "C:\\path\\to\\dir", but forward slashes
 | 
			
		||||
// ("C:/path/to/dir") will also work.
 | 
			
		||||
//
 | 
			
		||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
 | 
			
		||||
// When a watched directory is removed it will always send an event for the
 | 
			
		||||
// directory itself, but may not send events for all files in that directory.
 | 
			
		||||
// Sometimes it will send events for all times, sometimes it will send no
 | 
			
		||||
// events, and often only for some files.
 | 
			
		||||
//
 | 
			
		||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
 | 
			
		||||
// value that is guaranteed to work with SMB filesystems. If you have many
 | 
			
		||||
// events in quick succession this may not be enough, and you will have to use
 | 
			
		||||
// [WithBufferSize] to increase the value.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,20 +79,36 @@ new=$(<<EOF
 | 
			
		|||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
newbuffered=$(<<EOF
 | 
			
		||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// The main use case for this is situations with a very large number of events
 | 
			
		||||
// where the kernel buffer size can't be increased (e.g. due to lack of
 | 
			
		||||
// permissions). An unbuffered Watcher will perform better for almost all use
 | 
			
		||||
// cases, and whenever possible you will be better off increasing the kernel
 | 
			
		||||
// buffers instead of adding a large userspace buffer.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add=$(<<EOF
 | 
			
		||||
// Add starts monitoring the path for changes.
 | 
			
		||||
//
 | 
			
		||||
// A path can only be watched once; attempting to watch it more than once will
 | 
			
		||||
// return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// added. A watch will be automatically removed if the path is deleted.
 | 
			
		||||
// A path can only be watched once; watching it more than once is a no-op and will
 | 
			
		||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
 | 
			
		||||
// watched.
 | 
			
		||||
//
 | 
			
		||||
// A path will remain watched if it gets renamed to somewhere else on the same
 | 
			
		||||
// filesystem, but the monitor will get removed if the path gets deleted and
 | 
			
		||||
// re-created, or if it's moved to a different filesystem.
 | 
			
		||||
// A watch will be automatically removed if the watched path is deleted or
 | 
			
		||||
// renamed. The exception is the Windows backend, which doesn't remove the
 | 
			
		||||
// watcher on renames.
 | 
			
		||||
//
 | 
			
		||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
 | 
			
		||||
// filesystems (/proc, /sys, etc.) generally don't work.
 | 
			
		||||
//
 | 
			
		||||
// Returns [ErrClosed] if [Watcher.Close] was called.
 | 
			
		||||
//
 | 
			
		||||
// See [Watcher.AddWith] for a version that allows adding options.
 | 
			
		||||
//
 | 
			
		||||
// # Watching directories
 | 
			
		||||
//
 | 
			
		||||
// All files in a directory are monitored, including new files that are created
 | 
			
		||||
| 
						 | 
				
			
			@ -96,14 +118,27 @@ add=$(<<EOF
 | 
			
		|||
// # Watching files
 | 
			
		||||
//
 | 
			
		||||
// Watching individual files (rather than directories) is generally not
 | 
			
		||||
// recommended as many tools update files atomically. Instead of "just" writing
 | 
			
		||||
// to the file a temporary file will be written to first, and if successful the
 | 
			
		||||
// temporary file is moved to to destination removing the original, or some
 | 
			
		||||
// variant thereof. The watcher on the original file is now lost, as it no
 | 
			
		||||
// longer exists.
 | 
			
		||||
// recommended as many programs (especially editors) update files atomically: it
 | 
			
		||||
// will write to a temporary file which is then moved to to destination,
 | 
			
		||||
// overwriting the original (or some variant thereof). The watcher on the
 | 
			
		||||
// original file is now lost, as that no longer exists.
 | 
			
		||||
//
 | 
			
		||||
// Instead, watch the parent directory and use Event.Name to filter out files
 | 
			
		||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
 | 
			
		||||
// The upshot of this is that a power failure or crash won't leave a
 | 
			
		||||
// half-written file.
 | 
			
		||||
//
 | 
			
		||||
// Watch the parent directory and use Event.Name to filter out files you're not
 | 
			
		||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
addwith=$(<<EOF
 | 
			
		||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
 | 
			
		||||
// the defaults described below are used.
 | 
			
		||||
//
 | 
			
		||||
// Possible options are:
 | 
			
		||||
//
 | 
			
		||||
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
 | 
			
		||||
//     other platforms. The default is 64K (65536 bytes).
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,16 +149,21 @@ remove=$(<<EOF
 | 
			
		|||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
 | 
			
		||||
//
 | 
			
		||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
close=$(<<EOF
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
// Close removes all watches and closes the Events channel.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
watchlist=$(<<EOF
 | 
			
		||||
// WatchList returns all paths added with [Add] (and are not yet removed).
 | 
			
		||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
 | 
			
		||||
// yet removed).
 | 
			
		||||
//
 | 
			
		||||
// Returns nil if [Watcher.Close] was called.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,20 +193,29 @@ events=$(<<EOF
 | 
			
		|||
	//                      initiated by the user may show up as one or multiple
 | 
			
		||||
	//                      writes, depending on when the system syncs things to
 | 
			
		||||
	//                      disk. For example when compiling a large Go program
 | 
			
		||||
	//                      you may get hundreds of Write events, so you
 | 
			
		||||
	//                      probably want to wait until you've stopped receiving
 | 
			
		||||
	//                      them (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//                      you may get hundreds of Write events, and you may
 | 
			
		||||
	//                      want to wait until you've stopped receiving them
 | 
			
		||||
	//                      (see the dedup example in cmd/fsnotify).
 | 
			
		||||
	//
 | 
			
		||||
	//                      Some systems may send Write event for directories
 | 
			
		||||
	//                      when the directory content changes.
 | 
			
		||||
	//
 | 
			
		||||
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
 | 
			
		||||
	//                      when a file is removed (or more accurately, when a
 | 
			
		||||
	//                      link to an inode is removed). On kqueue it's sent
 | 
			
		||||
	//                      and on kqueue when a file is truncated. On Windows
 | 
			
		||||
	//                      it's never sent.
 | 
			
		||||
	//                      when a file is truncated. On Windows it's never
 | 
			
		||||
	//                      sent.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
errors=$(<<EOF
 | 
			
		||||
	// Errors sends any errors.
 | 
			
		||||
	//
 | 
			
		||||
	// ErrEventOverflow is used to indicate there are too many events:
 | 
			
		||||
	//
 | 
			
		||||
	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
 | 
			
		||||
	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
 | 
			
		||||
	//  - kqueue, fen:  Not used.
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +249,9 @@ set-cmt() {
 | 
			
		|||
 | 
			
		||||
set-cmt '^type Watcher struct '             $watcher
 | 
			
		||||
set-cmt '^func NewWatcher('                 $new
 | 
			
		||||
set-cmt '^func NewBufferedWatcher('         $newbuffered
 | 
			
		||||
set-cmt '^func (w \*Watcher) Add('          $add
 | 
			
		||||
set-cmt '^func (w \*Watcher) AddWith('      $addwith
 | 
			
		||||
set-cmt '^func (w \*Watcher) Remove('       $remove
 | 
			
		||||
set-cmt '^func (w \*Watcher) Close('        $close
 | 
			
		||||
set-cmt '^func (w \*Watcher) WatchList('    $watchlist
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
# A minimal logging API for Go
 | 
			
		||||
 | 
			
		||||
[](https://pkg.go.dev/github.com/go-logr/logr)
 | 
			
		||||
[](https://securityscorecards.dev/viewer/?platform=github.com&org=go-logr&repo=logr)
 | 
			
		||||
 | 
			
		||||
logr offers an(other) opinion on how Go programs and libraries can do logging
 | 
			
		||||
without becoming coupled to a particular logging implementation.  This is not
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +74,29 @@ received:
 | 
			
		|||
If the Go standard library had defined an interface for logging, this project
 | 
			
		||||
probably would not be needed.  Alas, here we are.
 | 
			
		||||
 | 
			
		||||
When the Go developers started developing such an interface with
 | 
			
		||||
[slog](https://github.com/golang/go/issues/56345), they adopted some of the
 | 
			
		||||
logr design but also left out some parts and changed others:
 | 
			
		||||
 | 
			
		||||
| Feature | logr | slog |
 | 
			
		||||
|---------|------|------|
 | 
			
		||||
| High-level API | `Logger` (passed by value) | `Logger` (passed by [pointer](https://github.com/golang/go/issues/59126)) |
 | 
			
		||||
| Low-level API | `LogSink` | `Handler` |
 | 
			
		||||
| Stack unwinding | done by `LogSink` | done by `Logger` |
 | 
			
		||||
| Skipping helper functions | `WithCallDepth`, `WithCallStackHelper` | [not supported by Logger](https://github.com/golang/go/issues/59145) |
 | 
			
		||||
| Generating a value for logging on demand | `Marshaler` | `LogValuer` |
 | 
			
		||||
| Log levels | >= 0, higher meaning "less important" | positive and negative, with 0 for "info" and higher meaning "more important" |
 | 
			
		||||
| Error log entries | always logged, don't have a verbosity level | normal log entries with level >= `LevelError` |
 | 
			
		||||
| Passing logger via context | `NewContext`, `FromContext` | no API |
 | 
			
		||||
| Adding a name to a logger | `WithName` | no API |
 | 
			
		||||
| Modify verbosity of log entries in a call chain | `V` | no API |
 | 
			
		||||
| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` |
 | 
			
		||||
 | 
			
		||||
The high-level slog API is explicitly meant to be one of many different APIs
 | 
			
		||||
that can be layered on top of a shared `slog.Handler`. logr is one such
 | 
			
		||||
alternative API, with [interoperability](#slog-interoperability) provided by the [`slogr`](slogr)
 | 
			
		||||
package.
 | 
			
		||||
 | 
			
		||||
### Inspiration
 | 
			
		||||
 | 
			
		||||
Before you consider this package, please read [this blog post by the
 | 
			
		||||
| 
						 | 
				
			
			@ -118,6 +142,91 @@ There are implementations for the following logging libraries:
 | 
			
		|||
- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0)
 | 
			
		||||
- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing)
 | 
			
		||||
 | 
			
		||||
## slog interoperability
 | 
			
		||||
 | 
			
		||||
Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler`
 | 
			
		||||
and using the `slog.Logger` API with a `logr.LogSink`. [slogr](./slogr) provides `NewLogr` and
 | 
			
		||||
`NewSlogHandler` API calls to convert between a `logr.Logger` and a `slog.Handler`.
 | 
			
		||||
As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level
 | 
			
		||||
slog API. `slogr` itself leaves that to the caller.
 | 
			
		||||
 | 
			
		||||
## Using a `logr.Sink` as backend for slog
 | 
			
		||||
 | 
			
		||||
Ideally, a logr sink implementation should support both logr and slog by
 | 
			
		||||
implementing both the normal logr interface(s) and `slogr.SlogSink`.  Because
 | 
			
		||||
of a conflict in the parameters of the common `Enabled` method, it is [not
 | 
			
		||||
possible to implement both slog.Handler and logr.Sink in the same
 | 
			
		||||
type](https://github.com/golang/go/issues/59110).
 | 
			
		||||
 | 
			
		||||
If both are supported, log calls can go from the high-level APIs to the backend
 | 
			
		||||
without the need to convert parameters. `NewLogr` and `NewSlogHandler` can
 | 
			
		||||
convert back and forth without adding additional wrappers, with one exception:
 | 
			
		||||
when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then
 | 
			
		||||
`NewSlogHandler` has to use a wrapper which adjusts the verbosity for future
 | 
			
		||||
log calls.
 | 
			
		||||
 | 
			
		||||
Such an implementation should also support values that implement specific
 | 
			
		||||
interfaces from both packages for logging (`logr.Marshaler`, `slog.LogValuer`,
 | 
			
		||||
`slog.GroupValue`). logr does not convert those.
 | 
			
		||||
 | 
			
		||||
Not supporting slog has several drawbacks:
 | 
			
		||||
- Recording source code locations works correctly if the handler gets called
 | 
			
		||||
  through `slog.Logger`, but may be wrong in other cases. That's because a
 | 
			
		||||
  `logr.Sink` does its own stack unwinding instead of using the program counter
 | 
			
		||||
  provided by the high-level API.
 | 
			
		||||
- slog levels <= 0 can be mapped to logr levels by negating the level without a
 | 
			
		||||
  loss of information. But all slog levels > 0 (e.g. `slog.LevelWarning` as
 | 
			
		||||
  used by `slog.Logger.Warn`) must be mapped to 0 before calling the sink
 | 
			
		||||
  because logr does not support "more important than info" levels.
 | 
			
		||||
- The slog group concept is supported by prefixing each key in a key/value
 | 
			
		||||
  pair with the group names, separated by a dot. For structured output like
 | 
			
		||||
  JSON it would be better to group the key/value pairs inside an object.
 | 
			
		||||
- Special slog values and interfaces don't work as expected.
 | 
			
		||||
- The overhead is likely to be higher.
 | 
			
		||||
 | 
			
		||||
These drawbacks are severe enough that applications using a mixture of slog and
 | 
			
		||||
logr should switch to a different backend.
 | 
			
		||||
 | 
			
		||||
## Using a `slog.Handler` as backend for logr
 | 
			
		||||
 | 
			
		||||
Using a plain `slog.Handler` without support for logr works better than the
 | 
			
		||||
other direction:
 | 
			
		||||
- All logr verbosity levels can be mapped 1:1 to their corresponding slog level
 | 
			
		||||
  by negating them.
 | 
			
		||||
- Stack unwinding is done by the `slogr.SlogSink` and the resulting program
 | 
			
		||||
  counter is passed to the `slog.Handler`.
 | 
			
		||||
- Names added via `Logger.WithName` are gathered and recorded in an additional
 | 
			
		||||
  attribute with `logger` as key and the names separated by slash as value.
 | 
			
		||||
- `Logger.Error` is turned into a log record with `slog.LevelError` as level
 | 
			
		||||
  and an additional attribute with `err` as key, if an error was provided.
 | 
			
		||||
 | 
			
		||||
The main drawback is that `logr.Marshaler` will not be supported. Types should
 | 
			
		||||
ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility
 | 
			
		||||
with logr implementations without slog support is not important, then
 | 
			
		||||
`slog.Valuer` is sufficient.
 | 
			
		||||
 | 
			
		||||
## Context support for slog
 | 
			
		||||
 | 
			
		||||
Storing a logger in a `context.Context` is not supported by
 | 
			
		||||
slog. `logr.NewContext` and `logr.FromContext` can be used with slog like this
 | 
			
		||||
to fill this gap:
 | 
			
		||||
 | 
			
		||||
    func HandlerFromContext(ctx context.Context) slog.Handler {
 | 
			
		||||
        logger, err := logr.FromContext(ctx)
 | 
			
		||||
        if err == nil {
 | 
			
		||||
            return slogr.NewSlogHandler(logger)
 | 
			
		||||
        }
 | 
			
		||||
        return slog.Default().Handler()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func ContextWithHandler(ctx context.Context, handler slog.Handler) context.Context {
 | 
			
		||||
        return logr.NewContext(ctx, slogr.NewLogr(handler))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
The downside is that storing and retrieving a `slog.Handler` needs more
 | 
			
		||||
allocations compared to using a `logr.Logger`. Therefore the recommendation is
 | 
			
		||||
to use the `logr.Logger` API in code which uses contextual logging.
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
 | 
			
		||||
### Conceptual
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +350,9 @@ Otherwise, you can start out with `0` as "you always want to see this",
 | 
			
		|||
 | 
			
		||||
Then gradually choose levels in between as you need them, working your way
 | 
			
		||||
down from 10 (for debug and trace style logs) and up from 1 (for chattier
 | 
			
		||||
info-type logs.)
 | 
			
		||||
info-type logs). For reference, slog pre-defines -4 for debug logs
 | 
			
		||||
(corresponds to 4 in logr), which matches what is
 | 
			
		||||
[recommended for Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md#what-method-to-use).
 | 
			
		||||
 | 
			
		||||
#### How do I choose my keys?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Security Policy
 | 
			
		||||
 | 
			
		||||
If you have discovered a security vulnerability in this project, please report it
 | 
			
		||||
privately. **Do not disclose it as a public issue.** This gives us time to work with you
 | 
			
		||||
to fix the issue before public exposure, reducing the chance that the exploit will be
 | 
			
		||||
used before a patch is released.
 | 
			
		||||
 | 
			
		||||
You may submit the report in the following ways:
 | 
			
		||||
 | 
			
		||||
- send an email to go-logr-security@googlegroups.com
 | 
			
		||||
- send us a [private vulnerability report](https://github.com/go-logr/logr/security/advisories/new)
 | 
			
		||||
 | 
			
		||||
Please provide the following information in your report:
 | 
			
		||||
 | 
			
		||||
- A description of the vulnerability and its impact
 | 
			
		||||
- How to reproduce the issue
 | 
			
		||||
 | 
			
		||||
We ask that you give us 90 days to work on a fix before public exposure.
 | 
			
		||||
| 
						 | 
				
			
			@ -116,17 +116,17 @@ type Options struct {
 | 
			
		|||
	// Equivalent hooks are offered for key-value pairs saved via
 | 
			
		||||
	// logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and
 | 
			
		||||
	// for user-provided pairs (see RenderArgsHook).
 | 
			
		||||
	RenderBuiltinsHook func(kvList []interface{}) []interface{}
 | 
			
		||||
	RenderBuiltinsHook func(kvList []any) []any
 | 
			
		||||
 | 
			
		||||
	// RenderValuesHook is the same as RenderBuiltinsHook, except that it is
 | 
			
		||||
	// only called for key-value pairs saved via logr.Logger.WithValues.  See
 | 
			
		||||
	// RenderBuiltinsHook for more details.
 | 
			
		||||
	RenderValuesHook func(kvList []interface{}) []interface{}
 | 
			
		||||
	RenderValuesHook func(kvList []any) []any
 | 
			
		||||
 | 
			
		||||
	// RenderArgsHook is the same as RenderBuiltinsHook, except that it is only
 | 
			
		||||
	// called for key-value pairs passed directly to Info and Error.  See
 | 
			
		||||
	// RenderBuiltinsHook for more details.
 | 
			
		||||
	RenderArgsHook func(kvList []interface{}) []interface{}
 | 
			
		||||
	RenderArgsHook func(kvList []any) []any
 | 
			
		||||
 | 
			
		||||
	// MaxLogDepth tells funcr how many levels of nested fields (e.g. a struct
 | 
			
		||||
	// that contains a struct, etc.) it may log.  Every time it finds a struct,
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +163,7 @@ func (l fnlogger) WithName(name string) logr.LogSink {
 | 
			
		|||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
 | 
			
		||||
func (l fnlogger) WithValues(kvList ...any) logr.LogSink {
 | 
			
		||||
	l.Formatter.AddValues(kvList)
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -173,12 +173,12 @@ func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
 | 
			
		|||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
 | 
			
		||||
func (l fnlogger) Info(level int, msg string, kvList ...any) {
 | 
			
		||||
	prefix, args := l.FormatInfo(level, msg, kvList)
 | 
			
		||||
	l.write(prefix, args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
 | 
			
		||||
func (l fnlogger) Error(err error, msg string, kvList ...any) {
 | 
			
		||||
	prefix, args := l.FormatError(err, msg, kvList)
 | 
			
		||||
	l.write(prefix, args)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -229,7 +229,7 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
 | 
			
		|||
type Formatter struct {
 | 
			
		||||
	outputFormat outputFormat
 | 
			
		||||
	prefix       string
 | 
			
		||||
	values       []interface{}
 | 
			
		||||
	values       []any
 | 
			
		||||
	valuesStr    string
 | 
			
		||||
	depth        int
 | 
			
		||||
	opts         *Options
 | 
			
		||||
| 
						 | 
				
			
			@ -246,10 +246,10 @@ const (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
// PseudoStruct is a list of key-value pairs that gets logged as a struct.
 | 
			
		||||
type PseudoStruct []interface{}
 | 
			
		||||
type PseudoStruct []any
 | 
			
		||||
 | 
			
		||||
// render produces a log line, ready to use.
 | 
			
		||||
func (f Formatter) render(builtins, args []interface{}) string {
 | 
			
		||||
func (f Formatter) render(builtins, args []any) string {
 | 
			
		||||
	// Empirically bytes.Buffer is faster than strings.Builder for this.
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +292,7 @@ func (f Formatter) render(builtins, args []interface{}) string {
 | 
			
		|||
// This function returns a potentially modified version of kvList, which
 | 
			
		||||
// ensures that there is a value for every key (adding a value if needed) and
 | 
			
		||||
// that each key is a string (substituting a key if needed).
 | 
			
		||||
func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool, escapeKeys bool) []interface{} {
 | 
			
		||||
func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, escapeKeys bool) []any {
 | 
			
		||||
	// This logic overlaps with sanitize() but saves one type-cast per key,
 | 
			
		||||
	// which can be measurable.
 | 
			
		||||
	if len(kvList)%2 != 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -334,7 +334,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing b
 | 
			
		|||
	return kvList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Formatter) pretty(value interface{}) string {
 | 
			
		||||
func (f Formatter) pretty(value any) string {
 | 
			
		||||
	return f.prettyWithFlags(value, 0, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -343,7 +343,7 @@ const (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
// TODO: This is not fast. Most of the overhead goes here.
 | 
			
		||||
func (f Formatter) prettyWithFlags(value interface{}, flags uint32, depth int) string {
 | 
			
		||||
func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
 | 
			
		||||
	if depth > f.opts.MaxLogDepth {
 | 
			
		||||
		return `"<max-log-depth-exceeded>"`
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -614,7 +614,7 @@ func isEmpty(v reflect.Value) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func invokeMarshaler(m logr.Marshaler) (ret interface{}) {
 | 
			
		||||
func invokeMarshaler(m logr.Marshaler) (ret any) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			ret = fmt.Sprintf("<panic: %s>", r)
 | 
			
		||||
| 
						 | 
				
			
			@ -675,12 +675,12 @@ func (f Formatter) caller() Caller {
 | 
			
		|||
 | 
			
		||||
const noValue = "<no-value>"
 | 
			
		||||
 | 
			
		||||
func (f Formatter) nonStringKey(v interface{}) string {
 | 
			
		||||
func (f Formatter) nonStringKey(v any) string {
 | 
			
		||||
	return fmt.Sprintf("<non-string-key: %s>", f.snippet(v))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// snippet produces a short snippet string of an arbitrary value.
 | 
			
		||||
func (f Formatter) snippet(v interface{}) string {
 | 
			
		||||
func (f Formatter) snippet(v any) string {
 | 
			
		||||
	const snipLen = 16
 | 
			
		||||
 | 
			
		||||
	snip := f.pretty(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -693,7 +693,7 @@ func (f Formatter) snippet(v interface{}) string {
 | 
			
		|||
// sanitize ensures that a list of key-value pairs has a value for every key
 | 
			
		||||
// (adding a value if needed) and that each key is a string (substituting a key
 | 
			
		||||
// if needed).
 | 
			
		||||
func (f Formatter) sanitize(kvList []interface{}) []interface{} {
 | 
			
		||||
func (f Formatter) sanitize(kvList []any) []any {
 | 
			
		||||
	if len(kvList)%2 != 0 {
 | 
			
		||||
		kvList = append(kvList, noValue)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -727,8 +727,8 @@ func (f Formatter) GetDepth() int {
 | 
			
		|||
// FormatInfo renders an Info log message into strings.  The prefix will be
 | 
			
		||||
// empty when no names were set (via AddNames), or when the output is
 | 
			
		||||
// configured for JSON.
 | 
			
		||||
func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) {
 | 
			
		||||
	args := make([]interface{}, 0, 64) // using a constant here impacts perf
 | 
			
		||||
func (f Formatter) FormatInfo(level int, msg string, kvList []any) (prefix, argsStr string) {
 | 
			
		||||
	args := make([]any, 0, 64) // using a constant here impacts perf
 | 
			
		||||
	prefix = f.prefix
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		args = append(args, "logger", prefix)
 | 
			
		||||
| 
						 | 
				
			
			@ -745,10 +745,10 @@ func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (pref
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// FormatError renders an Error log message into strings.  The prefix will be
 | 
			
		||||
// empty when no names were set (via AddNames),  or when the output is
 | 
			
		||||
// empty when no names were set (via AddNames), or when the output is
 | 
			
		||||
// configured for JSON.
 | 
			
		||||
func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) {
 | 
			
		||||
	args := make([]interface{}, 0, 64) // using a constant here impacts perf
 | 
			
		||||
func (f Formatter) FormatError(err error, msg string, kvList []any) (prefix, argsStr string) {
 | 
			
		||||
	args := make([]any, 0, 64) // using a constant here impacts perf
 | 
			
		||||
	prefix = f.prefix
 | 
			
		||||
	if f.outputFormat == outputJSON {
 | 
			
		||||
		args = append(args, "logger", prefix)
 | 
			
		||||
| 
						 | 
				
			
			@ -761,12 +761,12 @@ func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (pre
 | 
			
		|||
		args = append(args, "caller", f.caller())
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "msg", msg)
 | 
			
		||||
	var loggableErr interface{}
 | 
			
		||||
	var loggableErr any
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		loggableErr = err.Error()
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, "error", loggableErr)
 | 
			
		||||
	return f.prefix, f.render(args, kvList)
 | 
			
		||||
	return prefix, f.render(args, kvList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddName appends the specified name.  funcr uses '/' characters to separate
 | 
			
		||||
| 
						 | 
				
			
			@ -781,7 +781,7 @@ func (f *Formatter) AddName(name string) {
 | 
			
		|||
 | 
			
		||||
// AddValues adds key-value pairs to the set of saved values to be logged with
 | 
			
		||||
// each log line.
 | 
			
		||||
func (f *Formatter) AddValues(kvList []interface{}) {
 | 
			
		||||
func (f *Formatter) AddValues(kvList []any) {
 | 
			
		||||
	// Three slice args forces a copy.
 | 
			
		||||
	n := len(f.values)
 | 
			
		||||
	f.values = append(f.values[:n:n], kvList...)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -127,9 +127,9 @@ limitations under the License.
 | 
			
		|||
// such a value can call its methods without having to check whether the
 | 
			
		||||
// instance is ready for use.
 | 
			
		||||
//
 | 
			
		||||
// Calling methods with the null logger (Logger{}) as instance will crash
 | 
			
		||||
// because it has no LogSink. Therefore this null logger should never be passed
 | 
			
		||||
// around. For cases where passing a logger is optional, a pointer to Logger
 | 
			
		||||
// The zero logger (= Logger{}) is identical to Discard() and discards all log
 | 
			
		||||
// entries. Code that receives a Logger by value can simply call it, the methods
 | 
			
		||||
// will never crash. For cases where passing a logger is optional, a pointer to Logger
 | 
			
		||||
// should be used.
 | 
			
		||||
//
 | 
			
		||||
// # Key Naming Conventions
 | 
			
		||||
| 
						 | 
				
			
			@ -258,6 +258,12 @@ type Logger struct {
 | 
			
		|||
// Enabled tests whether this Logger is enabled.  For example, commandline
 | 
			
		||||
// flags might be used to set the logging verbosity and disable some info logs.
 | 
			
		||||
func (l Logger) Enabled() bool {
 | 
			
		||||
	// Some implementations of LogSink look at the caller in Enabled (e.g.
 | 
			
		||||
	// different verbosity levels per package or file), but we only pass one
 | 
			
		||||
	// CallDepth in (via Init).  This means that all calls from Logger to the
 | 
			
		||||
	// LogSink's Enabled, Info, and Error methods must have the same number of
 | 
			
		||||
	// frames.  In other words, Logger methods can't call other Logger methods
 | 
			
		||||
	// which call these LogSink methods unless we do it the same in all paths.
 | 
			
		||||
	return l.sink != nil && l.sink.Enabled(l.level)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -267,11 +273,11 @@ func (l Logger) Enabled() bool {
 | 
			
		|||
// line.  The key/value pairs can then be used to add additional variable
 | 
			
		||||
// information.  The key/value pairs must alternate string keys and arbitrary
 | 
			
		||||
// values.
 | 
			
		||||
func (l Logger) Info(msg string, keysAndValues ...interface{}) {
 | 
			
		||||
func (l Logger) Info(msg string, keysAndValues ...any) {
 | 
			
		||||
	if l.sink == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if l.Enabled() {
 | 
			
		||||
	if l.sink.Enabled(l.level) { // see comment in Enabled
 | 
			
		||||
		if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
 | 
			
		||||
			withHelper.GetCallStackHelper()()
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +295,7 @@ func (l Logger) Info(msg string, keysAndValues ...interface{}) {
 | 
			
		|||
// while the err argument should be used to attach the actual error that
 | 
			
		||||
// triggered this log line, if present. The err parameter is optional
 | 
			
		||||
// and nil may be passed instead of an error instance.
 | 
			
		||||
func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
 | 
			
		||||
func (l Logger) Error(err error, msg string, keysAndValues ...any) {
 | 
			
		||||
	if l.sink == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -314,9 +320,16 @@ func (l Logger) V(level int) Logger {
 | 
			
		|||
	return l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetV returns the verbosity level of the logger. If the logger's LogSink is
 | 
			
		||||
// nil as in the Discard logger, this will always return 0.
 | 
			
		||||
func (l Logger) GetV() int {
 | 
			
		||||
	// 0 if l.sink nil because of the if check in V above.
 | 
			
		||||
	return l.level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithValues returns a new Logger instance with additional key/value pairs.
 | 
			
		||||
// See Info for documentation on how key/value pairs work.
 | 
			
		||||
func (l Logger) WithValues(keysAndValues ...interface{}) Logger {
 | 
			
		||||
func (l Logger) WithValues(keysAndValues ...any) Logger {
 | 
			
		||||
	if l.sink == nil {
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -467,15 +480,15 @@ type LogSink interface {
 | 
			
		|||
	// The level argument is provided for optional logging.  This method will
 | 
			
		||||
	// only be called when Enabled(level) is true. See Logger.Info for more
 | 
			
		||||
	// details.
 | 
			
		||||
	Info(level int, msg string, keysAndValues ...interface{})
 | 
			
		||||
	Info(level int, msg string, keysAndValues ...any)
 | 
			
		||||
 | 
			
		||||
	// Error logs an error, with the given message and key/value pairs as
 | 
			
		||||
	// context.  See Logger.Error for more details.
 | 
			
		||||
	Error(err error, msg string, keysAndValues ...interface{})
 | 
			
		||||
	Error(err error, msg string, keysAndValues ...any)
 | 
			
		||||
 | 
			
		||||
	// WithValues returns a new LogSink with additional key/value pairs.  See
 | 
			
		||||
	// Logger.WithValues for more details.
 | 
			
		||||
	WithValues(keysAndValues ...interface{}) LogSink
 | 
			
		||||
	WithValues(keysAndValues ...any) LogSink
 | 
			
		||||
 | 
			
		||||
	// WithName returns a new LogSink with the specified name appended.  See
 | 
			
		||||
	// Logger.WithName for more details.
 | 
			
		||||
| 
						 | 
				
			
			@ -546,5 +559,5 @@ type Marshaler interface {
 | 
			
		|||
	//     with exported fields
 | 
			
		||||
	//
 | 
			
		||||
	// It may return any value of any type.
 | 
			
		||||
	MarshalLog() interface{}
 | 
			
		||||
	MarshalLog() any
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										168
									
								
								cluster-autoscaler/vendor/github.com/go-logr/logr/slogr/sloghandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										168
									
								
								cluster-autoscaler/vendor/github.com/go-logr/logr/slogr/sloghandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,168 @@
 | 
			
		|||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 The logr Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package slogr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-logr/logr"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type slogHandler struct {
 | 
			
		||||
	// May be nil, in which case all logs get discarded.
 | 
			
		||||
	sink logr.LogSink
 | 
			
		||||
	// Non-nil if sink is non-nil and implements SlogSink.
 | 
			
		||||
	slogSink SlogSink
 | 
			
		||||
 | 
			
		||||
	// groupPrefix collects values from WithGroup calls. It gets added as
 | 
			
		||||
	// prefix to value keys when handling a log record.
 | 
			
		||||
	groupPrefix string
 | 
			
		||||
 | 
			
		||||
	// levelBias can be set when constructing the handler to influence the
 | 
			
		||||
	// slog.Level of log records. A positive levelBias reduces the
 | 
			
		||||
	// slog.Level value. slog has no API to influence this value after the
 | 
			
		||||
	// handler got created, so it can only be set indirectly through
 | 
			
		||||
	// Logger.V.
 | 
			
		||||
	levelBias slog.Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ slog.Handler = &slogHandler{}
 | 
			
		||||
 | 
			
		||||
// groupSeparator is used to concatenate WithGroup names and attribute keys.
 | 
			
		||||
const groupSeparator = "."
 | 
			
		||||
 | 
			
		||||
// GetLevel is used for black box unit testing.
 | 
			
		||||
func (l *slogHandler) GetLevel() slog.Level {
 | 
			
		||||
	return l.levelBias
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) Enabled(ctx context.Context, level slog.Level) bool {
 | 
			
		||||
	return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error {
 | 
			
		||||
	if l.slogSink != nil {
 | 
			
		||||
		// Only adjust verbosity level of log entries < slog.LevelError.
 | 
			
		||||
		if record.Level < slog.LevelError {
 | 
			
		||||
			record.Level -= l.levelBias
 | 
			
		||||
		}
 | 
			
		||||
		return l.slogSink.Handle(ctx, record)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No need to check for nil sink here because Handle will only be called
 | 
			
		||||
	// when Enabled returned true.
 | 
			
		||||
 | 
			
		||||
	kvList := make([]any, 0, 2*record.NumAttrs())
 | 
			
		||||
	record.Attrs(func(attr slog.Attr) bool {
 | 
			
		||||
		if attr.Key != "" {
 | 
			
		||||
			kvList = append(kvList, l.addGroupPrefix(attr.Key), attr.Value.Resolve().Any())
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
	if record.Level >= slog.LevelError {
 | 
			
		||||
		l.sinkWithCallDepth().Error(nil, record.Message, kvList...)
 | 
			
		||||
	} else {
 | 
			
		||||
		level := l.levelFromSlog(record.Level)
 | 
			
		||||
		l.sinkWithCallDepth().Info(level, record.Message, kvList...)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sinkWithCallDepth adjusts the stack unwinding so that when Error or Info
 | 
			
		||||
// are called by Handle, code in slog gets skipped.
 | 
			
		||||
//
 | 
			
		||||
// This offset currently (Go 1.21.0) works for calls through
 | 
			
		||||
// slog.New(NewSlogHandler(...)).  There's no guarantee that the call
 | 
			
		||||
// chain won't change. Wrapping the handler will also break unwinding. It's
 | 
			
		||||
// still better than not adjusting at all....
 | 
			
		||||
//
 | 
			
		||||
// This cannot be done when constructing the handler because NewLogr needs
 | 
			
		||||
// access to the original sink without this adjustment. A second copy would
 | 
			
		||||
// work, but then WithAttrs would have to be called for both of them.
 | 
			
		||||
func (l *slogHandler) sinkWithCallDepth() logr.LogSink {
 | 
			
		||||
	if sink, ok := l.sink.(logr.CallDepthLogSink); ok {
 | 
			
		||||
		return sink.WithCallDepth(2)
 | 
			
		||||
	}
 | 
			
		||||
	return l.sink
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
 | 
			
		||||
	if l.sink == nil || len(attrs) == 0 {
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	copy := *l
 | 
			
		||||
	if l.slogSink != nil {
 | 
			
		||||
		copy.slogSink = l.slogSink.WithAttrs(attrs)
 | 
			
		||||
		copy.sink = copy.slogSink
 | 
			
		||||
	} else {
 | 
			
		||||
		kvList := make([]any, 0, 2*len(attrs))
 | 
			
		||||
		for _, attr := range attrs {
 | 
			
		||||
			if attr.Key != "" {
 | 
			
		||||
				kvList = append(kvList, l.addGroupPrefix(attr.Key), attr.Value.Resolve().Any())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		copy.sink = l.sink.WithValues(kvList...)
 | 
			
		||||
	}
 | 
			
		||||
	return ©
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) WithGroup(name string) slog.Handler {
 | 
			
		||||
	if l.sink == nil {
 | 
			
		||||
		return l
 | 
			
		||||
	}
 | 
			
		||||
	copy := *l
 | 
			
		||||
	if l.slogSink != nil {
 | 
			
		||||
		copy.slogSink = l.slogSink.WithGroup(name)
 | 
			
		||||
		copy.sink = l.slogSink
 | 
			
		||||
	} else {
 | 
			
		||||
		copy.groupPrefix = copy.addGroupPrefix(name)
 | 
			
		||||
	}
 | 
			
		||||
	return ©
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogHandler) addGroupPrefix(name string) string {
 | 
			
		||||
	if l.groupPrefix == "" {
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
	return l.groupPrefix + groupSeparator + name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
 | 
			
		||||
// It ensures that the result is >= 0. This is necessary because the result is
 | 
			
		||||
// passed to a logr.LogSink and that API did not historically document whether
 | 
			
		||||
// levels could be negative or what that meant.
 | 
			
		||||
//
 | 
			
		||||
// Some example usage:
 | 
			
		||||
//     logrV0 := getMyLogger()
 | 
			
		||||
//     logrV2 := logrV0.V(2)
 | 
			
		||||
//     slogV2 := slog.New(slogr.NewSlogHandler(logrV2))
 | 
			
		||||
//     slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
 | 
			
		||||
//     slogV2.Info("msg")  // =~  logrV2.V(0) =~ logrV0.V(2)
 | 
			
		||||
//     slogv2.Warn("msg")  // =~ logrV2.V(-4) =~ logrV0.V(0)
 | 
			
		||||
func (l *slogHandler) levelFromSlog(level slog.Level) int {
 | 
			
		||||
	result := -level
 | 
			
		||||
	result += l.levelBias // in case the original logr.Logger had a V level
 | 
			
		||||
	if result < 0 {
 | 
			
		||||
		result = 0 // because logr.LogSink doesn't expect negative V levels
 | 
			
		||||
	}
 | 
			
		||||
	return int(result)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 The logr Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Package slogr enables usage of a slog.Handler with logr.Logger as front-end
 | 
			
		||||
// API and of a logr.LogSink through the slog.Handler and thus slog.Logger
 | 
			
		||||
// APIs.
 | 
			
		||||
//
 | 
			
		||||
// See the README in the top-level [./logr] package for a discussion of
 | 
			
		||||
// interoperability.
 | 
			
		||||
package slogr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-logr/logr"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewLogr returns a logr.Logger which writes to the slog.Handler.
 | 
			
		||||
//
 | 
			
		||||
// The logr verbosity level is mapped to slog levels such that V(0) becomes
 | 
			
		||||
// slog.LevelInfo and V(4) becomes slog.LevelDebug.
 | 
			
		||||
func NewLogr(handler slog.Handler) logr.Logger {
 | 
			
		||||
	if handler, ok := handler.(*slogHandler); ok {
 | 
			
		||||
		if handler.sink == nil {
 | 
			
		||||
			return logr.Discard()
 | 
			
		||||
		}
 | 
			
		||||
		return logr.New(handler.sink).V(int(handler.levelBias))
 | 
			
		||||
	}
 | 
			
		||||
	return logr.New(&slogSink{handler: handler})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSlogHandler returns a slog.Handler which writes to the same sink as the logr.Logger.
 | 
			
		||||
//
 | 
			
		||||
// The returned logger writes all records with level >= slog.LevelError as
 | 
			
		||||
// error log entries with LogSink.Error, regardless of the verbosity level of
 | 
			
		||||
// the logr.Logger:
 | 
			
		||||
//
 | 
			
		||||
//	logger := <some logr.Logger with 0 as verbosity level>
 | 
			
		||||
//	slog.New(NewSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...)
 | 
			
		||||
//
 | 
			
		||||
// The level of all other records gets reduced by the verbosity
 | 
			
		||||
// level of the logr.Logger and the result is negated. If it happens
 | 
			
		||||
// to be negative, then it gets replaced by zero because a LogSink
 | 
			
		||||
// is not expected to handled negative levels:
 | 
			
		||||
//
 | 
			
		||||
//	slog.New(NewSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...)
 | 
			
		||||
//	slog.New(NewSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...)
 | 
			
		||||
//	slog.New(NewSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...)
 | 
			
		||||
//	slog.New(NewSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...)
 | 
			
		||||
func NewSlogHandler(logger logr.Logger) slog.Handler {
 | 
			
		||||
	if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 {
 | 
			
		||||
		return sink.handler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handler := &slogHandler{sink: logger.GetSink(), levelBias: slog.Level(logger.GetV())}
 | 
			
		||||
	if slogSink, ok := handler.sink.(SlogSink); ok {
 | 
			
		||||
		handler.slogSink = slogSink
 | 
			
		||||
	}
 | 
			
		||||
	return handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SlogSink is an optional interface that a LogSink can implement to support
 | 
			
		||||
// logging through the slog.Logger or slog.Handler APIs better. It then should
 | 
			
		||||
// also support special slog values like slog.Group. When used as a
 | 
			
		||||
// slog.Handler, the advantages are:
 | 
			
		||||
//
 | 
			
		||||
//   - stack unwinding gets avoided in favor of logging the pre-recorded PC,
 | 
			
		||||
//     as intended by slog
 | 
			
		||||
//   - proper grouping of key/value pairs via WithGroup
 | 
			
		||||
//   - verbosity levels > slog.LevelInfo can be recorded
 | 
			
		||||
//   - less overhead
 | 
			
		||||
//
 | 
			
		||||
// Both APIs (logr.Logger and slog.Logger/Handler) then are supported equally
 | 
			
		||||
// well. Developers can pick whatever API suits them better and/or mix
 | 
			
		||||
// packages which use either API in the same binary with a common logging
 | 
			
		||||
// implementation.
 | 
			
		||||
//
 | 
			
		||||
// This interface is necessary because the type implementing the LogSink
 | 
			
		||||
// interface cannot also implement the slog.Handler interface due to the
 | 
			
		||||
// different prototype of the common Enabled method.
 | 
			
		||||
//
 | 
			
		||||
// An implementation could support both interfaces in two different types, but then
 | 
			
		||||
// additional interfaces would be needed to convert between those types in NewLogr
 | 
			
		||||
// and NewSlogHandler.
 | 
			
		||||
type SlogSink interface {
 | 
			
		||||
	logr.LogSink
 | 
			
		||||
 | 
			
		||||
	Handle(ctx context.Context, record slog.Record) error
 | 
			
		||||
	WithAttrs(attrs []slog.Attr) SlogSink
 | 
			
		||||
	WithGroup(name string) SlogSink
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										122
									
								
								cluster-autoscaler/vendor/github.com/go-logr/logr/slogr/slogsink.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										122
									
								
								cluster-autoscaler/vendor/github.com/go-logr/logr/slogr/slogsink.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,122 @@
 | 
			
		|||
//go:build go1.21
 | 
			
		||||
// +build go1.21
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2023 The logr Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package slogr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-logr/logr"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_ logr.LogSink          = &slogSink{}
 | 
			
		||||
	_ logr.CallDepthLogSink = &slogSink{}
 | 
			
		||||
	_ Underlier             = &slogSink{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Underlier is implemented by the LogSink returned by NewLogr.
 | 
			
		||||
type Underlier interface {
 | 
			
		||||
	// GetUnderlying returns the Handler used by the LogSink.
 | 
			
		||||
	GetUnderlying() slog.Handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// nameKey is used to log the `WithName` values as an additional attribute.
 | 
			
		||||
	nameKey = "logger"
 | 
			
		||||
 | 
			
		||||
	// errKey is used to log the error parameter of Error as an additional attribute.
 | 
			
		||||
	errKey = "err"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type slogSink struct {
 | 
			
		||||
	callDepth int
 | 
			
		||||
	name      string
 | 
			
		||||
	handler   slog.Handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Init(info logr.RuntimeInfo) {
 | 
			
		||||
	l.callDepth = info.CallDepth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) GetUnderlying() slog.Handler {
 | 
			
		||||
	return l.handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) WithCallDepth(depth int) logr.LogSink {
 | 
			
		||||
	newLogger := *l
 | 
			
		||||
	newLogger.callDepth += depth
 | 
			
		||||
	return &newLogger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Enabled(level int) bool {
 | 
			
		||||
	return l.handler.Enabled(context.Background(), slog.Level(-level))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Info(level int, msg string, kvList ...interface{}) {
 | 
			
		||||
	l.log(nil, msg, slog.Level(-level), kvList...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) Error(err error, msg string, kvList ...interface{}) {
 | 
			
		||||
	l.log(err, msg, slog.LevelError, kvList...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interface{}) {
 | 
			
		||||
	var pcs [1]uintptr
 | 
			
		||||
	// skip runtime.Callers, this function, Info/Error, and all helper functions above that.
 | 
			
		||||
	runtime.Callers(3+l.callDepth, pcs[:])
 | 
			
		||||
 | 
			
		||||
	record := slog.NewRecord(time.Now(), level, msg, pcs[0])
 | 
			
		||||
	if l.name != "" {
 | 
			
		||||
		record.AddAttrs(slog.String(nameKey, l.name))
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		record.AddAttrs(slog.Any(errKey, err))
 | 
			
		||||
	}
 | 
			
		||||
	record.Add(kvList...)
 | 
			
		||||
	l.handler.Handle(context.Background(), record)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l slogSink) WithName(name string) logr.LogSink {
 | 
			
		||||
	if l.name != "" {
 | 
			
		||||
		l.name = l.name + "/"
 | 
			
		||||
	}
 | 
			
		||||
	l.name += name
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l slogSink) WithValues(kvList ...interface{}) logr.LogSink {
 | 
			
		||||
	l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...))
 | 
			
		||||
	return &l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func kvListToAttrs(kvList ...interface{}) []slog.Attr {
 | 
			
		||||
	// We don't need the record itself, only its Add method.
 | 
			
		||||
	record := slog.NewRecord(time.Time{}, 0, "", 0)
 | 
			
		||||
	record.Add(kvList...)
 | 
			
		||||
	attrs := make([]slog.Attr, 0, record.NumAttrs())
 | 
			
		||||
	record.Attrs(func(attr slog.Attr) bool {
 | 
			
		||||
		attrs = append(attrs, attr)
 | 
			
		||||
		return true
 | 
			
		||||
	})
 | 
			
		||||
	return attrs
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -176,9 +176,10 @@ func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, boo
 | 
			
		|||
					return err, false
 | 
			
		||||
				}
 | 
			
		||||
				state = waitingForReject
 | 
			
		||||
			} else {
 | 
			
		||||
				conn.uuid = string(s[1])
 | 
			
		||||
				return nil, true
 | 
			
		||||
			}
 | 
			
		||||
			conn.uuid = string(s[1])
 | 
			
		||||
			return nil, true
 | 
			
		||||
		case state == waitingForData:
 | 
			
		||||
			err = authWriteLine(conn.transport, []byte("ERROR"))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -191,9 +192,10 @@ func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, boo
 | 
			
		|||
					return err, false
 | 
			
		||||
				}
 | 
			
		||||
				state = waitingForReject
 | 
			
		||||
			} else {
 | 
			
		||||
				conn.uuid = string(s[1])
 | 
			
		||||
				return nil, true
 | 
			
		||||
			}
 | 
			
		||||
			conn.uuid = string(s[1])
 | 
			
		||||
			return nil, true
 | 
			
		||||
		case state == waitingForOk && string(s[0]) == "DATA":
 | 
			
		||||
			err = authWriteLine(conn.transport, []byte("DATA"))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,7 +169,7 @@ func Connect(address string, opts ...ConnOption) (*Conn, error) {
 | 
			
		|||
 | 
			
		||||
// SystemBusPrivate returns a new private connection to the system bus.
 | 
			
		||||
// Note: this connection is not ready to use. One must perform Auth and Hello
 | 
			
		||||
// on the connection before it is useable.
 | 
			
		||||
// on the connection before it is usable.
 | 
			
		||||
func SystemBusPrivate(opts ...ConnOption) (*Conn, error) {
 | 
			
		||||
	return Dial(getSystemBusPlatformAddress(), opts...)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -284,10 +284,6 @@ func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
 | 
			
		|||
		conn.ctx = context.Background()
 | 
			
		||||
	}
 | 
			
		||||
	conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx)
 | 
			
		||||
	go func() {
 | 
			
		||||
		<-conn.ctx.Done()
 | 
			
		||||
		conn.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	conn.calls = newCallTracker()
 | 
			
		||||
	if conn.handler == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +298,11 @@ func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
 | 
			
		|||
	conn.outHandler = &outputHandler{conn: conn}
 | 
			
		||||
	conn.names = newNameTracker()
 | 
			
		||||
	conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		<-conn.ctx.Done()
 | 
			
		||||
		conn.Close()
 | 
			
		||||
	}()
 | 
			
		||||
	return conn, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -550,6 +551,11 @@ func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
 | 
			
		|||
		call.ctx = ctx
 | 
			
		||||
		call.ctxCanceler = canceler
 | 
			
		||||
		conn.calls.track(msg.serial, call)
 | 
			
		||||
		if ctx.Err() != nil {
 | 
			
		||||
			// short path: don't even send the message if context already cancelled
 | 
			
		||||
			conn.calls.handleSendError(msg, ctx.Err())
 | 
			
		||||
			return call
 | 
			
		||||
		}
 | 
			
		||||
		go func() {
 | 
			
		||||
			<-ctx.Done()
 | 
			
		||||
			conn.calls.handleSendError(msg, ctx.Err())
 | 
			
		||||
| 
						 | 
				
			
			@ -649,7 +655,9 @@ func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...Match
 | 
			
		|||
 | 
			
		||||
// Signal registers the given channel to be passed all received signal messages.
 | 
			
		||||
//
 | 
			
		||||
// Multiple of these channels can be registered at the same time.
 | 
			
		||||
// Multiple of these channels can be registered at the same time. The channel is
 | 
			
		||||
// closed if the Conn is closed; it should not be closed by the caller before
 | 
			
		||||
// RemoveSignal was called on it.
 | 
			
		||||
//
 | 
			
		||||
// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
 | 
			
		||||
// channel for eavesdropped messages, this channel receives all signals, and
 | 
			
		||||
| 
						 | 
				
			
			@ -765,7 +773,12 @@ func getKey(s, key string) string {
 | 
			
		|||
	for _, keyEqualsValue := range strings.Split(s, ",") {
 | 
			
		||||
		keyValue := strings.SplitN(keyEqualsValue, "=", 2)
 | 
			
		||||
		if len(keyValue) == 2 && keyValue[0] == key {
 | 
			
		||||
			return keyValue[1]
 | 
			
		||||
			val, err := UnescapeBusAddressValue(keyValue[1])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				// No way to return an error.
 | 
			
		||||
				return ""
 | 
			
		||||
			}
 | 
			
		||||
			return val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ func tryDiscoverDbusSessionBusAddress() string {
 | 
			
		|||
		if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) {
 | 
			
		||||
			// if /run/user/<uid>/bus exists, that file itself
 | 
			
		||||
			// *is* the unix socket, so return its path
 | 
			
		||||
			return fmt.Sprintf("unix:path=%s", runUserBusFile)
 | 
			
		||||
			return fmt.Sprintf("unix:path=%s", EscapeBusAddressValue(runUserBusFile))
 | 
			
		||||
		}
 | 
			
		||||
		if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) {
 | 
			
		||||
			// if /run/user/<uid>/dbus-session exists, it's a
 | 
			
		||||
| 
						 | 
				
			
			@ -85,9 +85,6 @@ func getRuntimeDirectory() (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func fileExists(filename string) bool {
 | 
			
		||||
	if _, err := os.Stat(filename); !os.IsNotExist(err) {
 | 
			
		||||
		return true
 | 
			
		||||
	} else {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	_, err := os.Stat(filename)
 | 
			
		||||
	return !os.IsNotExist(err)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,8 +122,11 @@ func isConvertibleTo(dest, src reflect.Type) bool {
 | 
			
		|||
	case dest.Kind() == reflect.Slice:
 | 
			
		||||
		return src.Kind() == reflect.Slice &&
 | 
			
		||||
			isConvertibleTo(dest.Elem(), src.Elem())
 | 
			
		||||
	case dest.Kind() == reflect.Ptr:
 | 
			
		||||
		dest = dest.Elem()
 | 
			
		||||
		return isConvertibleTo(dest, src)
 | 
			
		||||
	case dest.Kind() == reflect.Struct:
 | 
			
		||||
		return src == interfacesType
 | 
			
		||||
		return src == interfacesType || dest.Kind() == src.Kind()
 | 
			
		||||
	default:
 | 
			
		||||
		return src.ConvertibleTo(dest)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -274,13 +277,8 @@ func storeSliceIntoInterface(dest, src reflect.Value) error {
 | 
			
		|||
func storeSliceIntoSlice(dest, src reflect.Value) error {
 | 
			
		||||
	if dest.IsNil() || dest.Len() < src.Len() {
 | 
			
		||||
		dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
 | 
			
		||||
	}
 | 
			
		||||
	if dest.Len() != src.Len() {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"dbus.Store: type mismatch: "+
 | 
			
		||||
				"slices are different lengths "+
 | 
			
		||||
				"need: %d have: %d",
 | 
			
		||||
			src.Len(), dest.Len())
 | 
			
		||||
	} else if dest.Len() > src.Len() {
 | 
			
		||||
		dest.Set(dest.Slice(0, src.Len()))
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < src.Len(); i++ {
 | 
			
		||||
		err := store(dest.Index(i), getVariantValue(src.Index(i)))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,8 +10,10 @@ value.
 | 
			
		|||
Conversion Rules
 | 
			
		||||
 | 
			
		||||
For outgoing messages, Go types are automatically converted to the
 | 
			
		||||
corresponding D-Bus types. The following types are directly encoded as their
 | 
			
		||||
respective D-Bus equivalents:
 | 
			
		||||
corresponding D-Bus types. See the official specification at
 | 
			
		||||
https://dbus.freedesktop.org/doc/dbus-specification.html#type-system for more
 | 
			
		||||
information on the D-Bus type system. The following types are directly encoded
 | 
			
		||||
as their respective D-Bus equivalents:
 | 
			
		||||
 | 
			
		||||
     Go type     | D-Bus type
 | 
			
		||||
     ------------+-----------
 | 
			
		||||
| 
						 | 
				
			
			@ -39,8 +41,8 @@ Maps encode as DICTs, provided that their key type can be used as a key for
 | 
			
		|||
a DICT.
 | 
			
		||||
 | 
			
		||||
Structs other than Variant and Signature encode as a STRUCT containing their
 | 
			
		||||
exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will
 | 
			
		||||
be skipped.
 | 
			
		||||
exported fields in order. Fields whose tags contain `dbus:"-"` and unexported
 | 
			
		||||
fields will be skipped.
 | 
			
		||||
 | 
			
		||||
Pointers encode as the value they're pointed to.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,84 @@
 | 
			
		|||
package dbus
 | 
			
		||||
 | 
			
		||||
import "net/url"
 | 
			
		||||
 | 
			
		||||
// EscapeBusAddressValue implements a requirement to escape the values
 | 
			
		||||
// in D-Bus server addresses, as defined by the D-Bus specification at
 | 
			
		||||
// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
 | 
			
		||||
func EscapeBusAddressValue(val string) string {
 | 
			
		||||
	toEsc := strNeedsEscape(val)
 | 
			
		||||
	if toEsc == 0 {
 | 
			
		||||
		// Avoid unneeded allocation/copying.
 | 
			
		||||
		return val
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Avoid allocation for short paths.
 | 
			
		||||
	var buf [64]byte
 | 
			
		||||
	var out []byte
 | 
			
		||||
	// Every to-be-escaped byte needs 2 extra bytes.
 | 
			
		||||
	required := len(val) + 2*toEsc
 | 
			
		||||
	if required <= len(buf) {
 | 
			
		||||
		out = buf[:required]
 | 
			
		||||
	} else {
 | 
			
		||||
		out = make([]byte, required)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	j := 0
 | 
			
		||||
	for i := 0; i < len(val); i++ {
 | 
			
		||||
		if ch := val[i]; needsEscape(ch) {
 | 
			
		||||
			// Convert ch to %xx, where xx is hex value.
 | 
			
		||||
			out[j] = '%'
 | 
			
		||||
			out[j+1] = hexchar(ch >> 4)
 | 
			
		||||
			out[j+2] = hexchar(ch & 0x0F)
 | 
			
		||||
			j += 3
 | 
			
		||||
		} else {
 | 
			
		||||
			out[j] = ch
 | 
			
		||||
			j++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnescapeBusAddressValue unescapes values in D-Bus server addresses,
 | 
			
		||||
// as defined by the D-Bus specification at
 | 
			
		||||
// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
 | 
			
		||||
func UnescapeBusAddressValue(val string) (string, error) {
 | 
			
		||||
	// Looks like url.PathUnescape does exactly what is required.
 | 
			
		||||
	return url.PathUnescape(val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hexchar returns an octal representation of a n, where n < 16.
 | 
			
		||||
// For invalid values of n, the function panics.
 | 
			
		||||
func hexchar(n byte) byte {
 | 
			
		||||
	const hex = "0123456789abcdef"
 | 
			
		||||
 | 
			
		||||
	// For n >= len(hex), runtime will panic.
 | 
			
		||||
	return hex[n]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// needsEscape tells if a byte is NOT one of optionally-escaped bytes.
 | 
			
		||||
func needsEscape(c byte) bool {
 | 
			
		||||
	if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	switch c {
 | 
			
		||||
	case '-', '_', '/', '\\', '.', '*':
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// strNeedsEscape tells how many bytes in the string need escaping.
 | 
			
		||||
func strNeedsEscape(val string) int {
 | 
			
		||||
	count := 0
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(val); i++ {
 | 
			
		||||
		if needsEscape(val[i]) {
 | 
			
		||||
			count++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return count
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package dbus
 | 
			
		|||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -209,28 +210,23 @@ func (conn *Conn) handleCall(msg *Message) {
 | 
			
		|||
		}
 | 
			
		||||
		reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
 | 
			
		||||
 | 
			
		||||
		conn.sendMessageAndIfClosed(reply, nil)
 | 
			
		||||
		if err := reply.IsValid(); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "dbus: dropping invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err)
 | 
			
		||||
		} else {
 | 
			
		||||
			conn.sendMessageAndIfClosed(reply, nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Emit emits the given signal on the message bus. The name parameter must be
 | 
			
		||||
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
 | 
			
		||||
func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
 | 
			
		||||
	if !path.IsValid() {
 | 
			
		||||
		return errors.New("dbus: invalid object path")
 | 
			
		||||
	}
 | 
			
		||||
	i := strings.LastIndex(name, ".")
 | 
			
		||||
	if i == -1 {
 | 
			
		||||
		return errors.New("dbus: invalid method name")
 | 
			
		||||
	}
 | 
			
		||||
	iface := name[:i]
 | 
			
		||||
	member := name[i+1:]
 | 
			
		||||
	if !isValidMember(member) {
 | 
			
		||||
		return errors.New("dbus: invalid method name")
 | 
			
		||||
	}
 | 
			
		||||
	if !isValidInterface(iface) {
 | 
			
		||||
		return errors.New("dbus: invalid interface name")
 | 
			
		||||
	}
 | 
			
		||||
	msg := new(Message)
 | 
			
		||||
	msg.Type = TypeSignal
 | 
			
		||||
	msg.Headers = make(map[HeaderField]Variant)
 | 
			
		||||
| 
						 | 
				
			
			@ -241,6 +237,9 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro
 | 
			
		|||
	if len(values) > 0 {
 | 
			
		||||
		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
 | 
			
		||||
	}
 | 
			
		||||
	if err := msg.IsValid(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var closed bool
 | 
			
		||||
	conn.sendMessageAndIfClosed(msg, func() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,27 +2,24 @@ package dbus
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	homeDir     string
 | 
			
		||||
	homeDirLock sync.Mutex
 | 
			
		||||
	"os/user"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Get returns the home directory of the current user, which is usually the
 | 
			
		||||
// value of HOME environment variable. In case it is not set or empty, os/user
 | 
			
		||||
// package is used.
 | 
			
		||||
//
 | 
			
		||||
// If linking statically with cgo enabled against glibc, make sure the
 | 
			
		||||
// osusergo build tag is used.
 | 
			
		||||
//
 | 
			
		||||
// If needing to do nss lookups, do not disable cgo or set osusergo.
 | 
			
		||||
func getHomeDir() string {
 | 
			
		||||
	homeDirLock.Lock()
 | 
			
		||||
	defer homeDirLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	homeDir := os.Getenv("HOME")
 | 
			
		||||
	if homeDir != "" {
 | 
			
		||||
		return homeDir
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	homeDir = os.Getenv("HOME")
 | 
			
		||||
	if homeDir != "" {
 | 
			
		||||
		return homeDir
 | 
			
		||||
	if u, err := user.Current(); err == nil {
 | 
			
		||||
		return u.HomeDir
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	homeDir = lookupHomeDir()
 | 
			
		||||
	return homeDir
 | 
			
		||||
	return "/"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +0,0 @@
 | 
			
		|||
// +build !static_build
 | 
			
		||||
 | 
			
		||||
package dbus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os/user"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func lookupHomeDir() string {
 | 
			
		||||
	u, err := user.Current()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "/"
 | 
			
		||||
	}
 | 
			
		||||
	return u.HomeDir
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,45 +0,0 @@
 | 
			
		|||
// +build static_build
 | 
			
		||||
 | 
			
		||||
package dbus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func lookupHomeDir() string {
 | 
			
		||||
	myUid := os.Getuid()
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open("/etc/passwd")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "/"
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	s := bufio.NewScanner(f)
 | 
			
		||||
 | 
			
		||||
	for s.Scan() {
 | 
			
		||||
		if err := s.Err(); err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		line := strings.TrimSpace(s.Text())
 | 
			
		||||
		if line == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		parts := strings.Split(line, ":")
 | 
			
		||||
 | 
			
		||||
		if len(parts) >= 6 {
 | 
			
		||||
			uid, err := strconv.Atoi(parts[2])
 | 
			
		||||
			if err == nil && uid == myUid {
 | 
			
		||||
				return parts[5]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Default to / if we can't get a better value
 | 
			
		||||
	return "/"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +208,7 @@ func DecodeMessageWithFDs(rd io.Reader, fds []int) (msg *Message, err error) {
 | 
			
		|||
// The possibly returned error can be an error of the underlying reader, an
 | 
			
		||||
// InvalidMessageError or a FormatError.
 | 
			
		||||
func DecodeMessage(rd io.Reader) (msg *Message, err error) {
 | 
			
		||||
	return DecodeMessageWithFDs(rd, make([]int, 0));
 | 
			
		||||
	return DecodeMessageWithFDs(rd, make([]int, 0))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nullwriter struct{}
 | 
			
		||||
| 
						 | 
				
			
			@ -227,8 +227,8 @@ func (msg *Message) CountFds() (int, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds []int, err error) {
 | 
			
		||||
	if err := msg.IsValid(); err != nil {
 | 
			
		||||
		return make([]int, 0), err
 | 
			
		||||
	if err := msg.validateHeader(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var vs [7]interface{}
 | 
			
		||||
	switch order {
 | 
			
		||||
| 
						 | 
				
			
			@ -237,7 +237,7 @@ func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds
 | 
			
		|||
	case binary.BigEndian:
 | 
			
		||||
		vs[0] = byte('B')
 | 
			
		||||
	default:
 | 
			
		||||
		return make([]int, 0), errors.New("dbus: invalid byte order")
 | 
			
		||||
		return nil, errors.New("dbus: invalid byte order")
 | 
			
		||||
	}
 | 
			
		||||
	body := new(bytes.Buffer)
 | 
			
		||||
	fds = make([]int, 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -284,8 +284,13 @@ func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) (err error)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// IsValid checks whether msg is a valid message and returns an
 | 
			
		||||
// InvalidMessageError if it is not.
 | 
			
		||||
// InvalidMessageError or FormatError if it is not.
 | 
			
		||||
func (msg *Message) IsValid() error {
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	return msg.EncodeTo(&b, nativeEndian)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *Message) validateHeader() error {
 | 
			
		||||
	if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 {
 | 
			
		||||
		return InvalidMessageError("invalid flags")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -330,6 +335,7 @@ func (msg *Message) IsValid() error {
 | 
			
		|||
			return InvalidMessageError("missing signature")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ type Method interface {
 | 
			
		|||
// any other decoding scheme.
 | 
			
		||||
type ArgumentDecoder interface {
 | 
			
		||||
	// To decode the arguments of a method the sender and message are
 | 
			
		||||
	// provided incase the semantics of the implementer provides access
 | 
			
		||||
	// provided in case the semantics of the implementer provides access
 | 
			
		||||
	// to these as part of the method invocation.
 | 
			
		||||
	DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ func getSignature(t reflect.Type, depth *depthCounter) (sig string) {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(s) == 0 {
 | 
			
		||||
			panic("empty struct")
 | 
			
		||||
			panic(InvalidTypeError{t})
 | 
			
		||||
		}
 | 
			
		||||
		return "(" + s + ")"
 | 
			
		||||
	case reflect.Array, reflect.Slice:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -154,17 +154,15 @@ func (t *unixTransport) ReadMessage() (*Message, error) {
 | 
			
		|||
		// substitute the values in the message body (which are indices for the
 | 
			
		||||
		// array receiver via OOB) with the actual values
 | 
			
		||||
		for i, v := range msg.Body {
 | 
			
		||||
			switch v.(type) {
 | 
			
		||||
			switch index := v.(type) {
 | 
			
		||||
			case UnixFDIndex:
 | 
			
		||||
				j := v.(UnixFDIndex)
 | 
			
		||||
				if uint32(j) >= unixfds {
 | 
			
		||||
				if uint32(index) >= unixfds {
 | 
			
		||||
					return nil, InvalidMessageError("invalid index for unix fd")
 | 
			
		||||
				}
 | 
			
		||||
				msg.Body[i] = UnixFD(fds[j])
 | 
			
		||||
				msg.Body[i] = UnixFD(fds[index])
 | 
			
		||||
			case []UnixFDIndex:
 | 
			
		||||
				idxArray := v.([]UnixFDIndex)
 | 
			
		||||
				fdArray := make([]UnixFD, len(idxArray))
 | 
			
		||||
				for k, j := range idxArray {
 | 
			
		||||
				fdArray := make([]UnixFD, len(index))
 | 
			
		||||
				for k, j := range index {
 | 
			
		||||
					if uint32(j) >= unixfds {
 | 
			
		||||
						return nil, InvalidMessageError("invalid index for unix fd")
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								cluster-autoscaler/vendor/github.com/godbus/dbus/v5/transport_zos.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										6
									
								
								cluster-autoscaler/vendor/github.com/godbus/dbus/v5/transport_zos.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
package dbus
 | 
			
		||||
 | 
			
		||||
func (t *unixTransport) SendNullByte() error {
 | 
			
		||||
	_, err := t.Write([]byte{0})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ func ParseVariant(s string, sig Signature) (Variant, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// format returns a formatted version of v and whether this string can be parsed
 | 
			
		||||
// unambigously.
 | 
			
		||||
// unambiguously.
 | 
			
		||||
func (v Variant) format() (string, bool) {
 | 
			
		||||
	switch v.sig.str[0] {
 | 
			
		||||
	case 'b', 'i':
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/collector/generic_collector.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										4
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/collector/generic_collector.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -17,7 +17,7 @@ package collector
 | 
			
		|||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +145,7 @@ func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (t
 | 
			
		|||
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
 | 
			
		||||
	pageContent, err := ioutil.ReadAll(response.Body)
 | 
			
		||||
	pageContent, err := io.ReadAll(response.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nextCollectionTime, nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,6 @@ package common
 | 
			
		|||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +47,7 @@ type networkInterface struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func GetContainerHintsFromFile(containerHintsFile string) (ContainerHints, error) {
 | 
			
		||||
	dat, err := ioutil.ReadFile(containerHintsFile)
 | 
			
		||||
	dat, err := os.ReadFile(containerHintsFile)
 | 
			
		||||
	if os.IsNotExist(err) {
 | 
			
		||||
		return ContainerHints{}, nil
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/common/helpers.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										24
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/common/helpers.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -16,7 +16,6 @@ package common
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +75,11 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
 | 
			
		|||
		dir, err := os.Stat(cgroupPathDir)
 | 
			
		||||
		if err == nil && dir.ModTime().Before(lowestTime) {
 | 
			
		||||
			lowestTime = dir.ModTime()
 | 
			
		||||
		} else if os.IsNotExist(err) {
 | 
			
		||||
			// Directory does not exist, skip checking for files within.
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The modified time of the cgroup directory sometimes changes whenever a subcontainer is created.
 | 
			
		||||
		// eg. /docker will have creation time matching the creation of latest docker container.
 | 
			
		||||
		// Use clone_children/events as a workaround as it isn't usually modified. It is only likely changed
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +238,7 @@ func readString(dirpath string, file string) string {
 | 
			
		|||
	cgroupFile := path.Join(dirpath, file)
 | 
			
		||||
 | 
			
		||||
	// Read
 | 
			
		||||
	out, err := ioutil.ReadFile(cgroupFile)
 | 
			
		||||
	out, err := os.ReadFile(cgroupFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// Ignore non-existent files
 | 
			
		||||
		if !os.IsNotExist(err) {
 | 
			
		||||
| 
						 | 
				
			
			@ -437,3 +440,20 @@ func (m deviceIdentifierMap) Find(major, minor uint64, namer DeviceNamer) string
 | 
			
		|||
	m[d] = s
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveNetMetrics is used to remove any network metrics from the given MetricSet.
 | 
			
		||||
// It returns the original set as is if remove is false, or if there are no metrics
 | 
			
		||||
// to remove.
 | 
			
		||||
func RemoveNetMetrics(metrics container.MetricSet, remove bool) container.MetricSet {
 | 
			
		||||
	if !remove {
 | 
			
		||||
		return metrics
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if there is anything we can remove, to avoid useless copying.
 | 
			
		||||
	if !metrics.HasAny(container.AllNetworkMetrics) {
 | 
			
		||||
		return metrics
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A copy of all metrics except for network ones.
 | 
			
		||||
	return metrics.Difference(container.AllNetworkMetrics)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ const (
 | 
			
		|||
	ContainerTypeCrio
 | 
			
		||||
	ContainerTypeContainerd
 | 
			
		||||
	ContainerTypeMesos
 | 
			
		||||
	ContainerTypePodman
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Interface for container operation handlers.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/containerd/client.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										2
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/containerd/client.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -59,6 +59,7 @@ const (
 | 
			
		|||
	maxBackoffDelay   = 3 * time.Second
 | 
			
		||||
	baseBackoffDelay  = 100 * time.Millisecond
 | 
			
		||||
	connectionTimeout = 2 * time.Second
 | 
			
		||||
	maxMsgSize        = 16 * 1024 * 1024 // 16MB
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client creates a containerd client
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +83,7 @@ func Client(address, namespace string) (ContainerdClient, error) {
 | 
			
		|||
			grpc.WithContextDialer(dialer.ContextDialer),
 | 
			
		||||
			grpc.WithBlock(),
 | 
			
		||||
			grpc.WithConnectParams(connParams),
 | 
			
		||||
			grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)),
 | 
			
		||||
		}
 | 
			
		||||
		unary, stream := newNSInterceptors(namespace)
 | 
			
		||||
		gopts = append(gopts,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/containerd/handler.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										32
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/containerd/handler.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -126,7 +126,14 @@ func newContainerdContainerHandler(
 | 
			
		|||
		Aliases:   []string{id, name},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), includedMetrics)
 | 
			
		||||
	// Containers that don't have their own network -- this includes
 | 
			
		||||
	// containers running in Kubernetes pods that use the network of the
 | 
			
		||||
	// infrastructure container -- does not need their stats to be
 | 
			
		||||
	// reported. This stops metrics being reported multiple times for each
 | 
			
		||||
	// container in a pod.
 | 
			
		||||
	metrics := common.RemoveNetMetrics(includedMetrics, cntr.Labels["io.cri-containerd.kind"] != "sandbox")
 | 
			
		||||
 | 
			
		||||
	libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), metrics)
 | 
			
		||||
 | 
			
		||||
	handler := &containerdContainerHandler{
 | 
			
		||||
		machineInfoFactory:  machineInfoFactory,
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +141,7 @@ func newContainerdContainerHandler(
 | 
			
		|||
		fsInfo:              fsInfo,
 | 
			
		||||
		envs:                make(map[string]string),
 | 
			
		||||
		labels:              cntr.Labels,
 | 
			
		||||
		includedMetrics:     includedMetrics,
 | 
			
		||||
		includedMetrics:     metrics,
 | 
			
		||||
		reference:           containerReference,
 | 
			
		||||
		libcontainerHandler: libcontainerHandler,
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -164,22 +171,12 @@ func (h *containerdContainerHandler) ContainerReference() (info.ContainerReferen
 | 
			
		|||
	return h.reference, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *containerdContainerHandler) needNet() bool {
 | 
			
		||||
	// Since containerd does not handle networking ideally we need to return based
 | 
			
		||||
	// on includedMetrics list. Here the assumption is the presence of cri-containerd
 | 
			
		||||
	// label
 | 
			
		||||
	if h.includedMetrics.Has(container.NetworkUsageMetrics) {
 | 
			
		||||
		//TODO change it to exported cri-containerd constants
 | 
			
		||||
		return h.labels["io.cri-containerd.kind"] == "sandbox"
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
 | 
			
		||||
	// TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
 | 
			
		||||
	// to false. Revisit when we support disk usage stats for containerd
 | 
			
		||||
	hasFilesystem := false
 | 
			
		||||
	spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem)
 | 
			
		||||
	hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics)
 | 
			
		||||
	spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem)
 | 
			
		||||
	spec.Labels = h.labels
 | 
			
		||||
	spec.Envs = h.envs
 | 
			
		||||
	spec.Image = h.image
 | 
			
		||||
| 
						 | 
				
			
			@ -204,13 +201,6 @@ func (h *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return stats, err
 | 
			
		||||
	}
 | 
			
		||||
	// Clean up stats for containers that don't have their own network - this
 | 
			
		||||
	// includes containers running in Kubernetes pods that use the network of the
 | 
			
		||||
	// infrastructure container. This stops metrics being reported multiple times
 | 
			
		||||
	// for each container in a pod.
 | 
			
		||||
	if !h.needNet() {
 | 
			
		||||
		stats.Network = info.NetworkStats{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get filesystem stats.
 | 
			
		||||
	err = h.getFsStats(stats)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,8 +17,9 @@ package crio
 | 
			
		|||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +27,8 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var crioClientTimeout = flag.Duration("crio_client_timeout", time.Duration(0), "CRI-O client timeout. Default is no timeout.")
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CrioSocket            = "/var/run/crio/crio.sock"
 | 
			
		||||
	maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +92,7 @@ func Client() (CrioClient, error) {
 | 
			
		|||
		theClient = &crioClientImpl{
 | 
			
		||||
			client: &http.Client{
 | 
			
		||||
				Transport: tr,
 | 
			
		||||
				Timeout:   *crioClientTimeout,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +146,7 @@ func (c *crioClientImpl) ContainerInfo(id string) (*ContainerInfo, error) {
 | 
			
		|||
	// golang's http.Do doesn't return an error if non 200 response code is returned
 | 
			
		||||
	// handle this case here, rather than failing to decode the body
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		respBody, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
		respBody, err := io.ReadAll(resp.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("Error finding container %s: Status %d", id, resp.StatusCode)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/crio/handler.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										30
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/crio/handler.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -148,7 +148,15 @@ func newCrioContainerHandler(
 | 
			
		|||
		Namespace: CrioNamespace,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, includedMetrics)
 | 
			
		||||
	// Find out if we need network metrics reported for this container.
 | 
			
		||||
	// Containers that don't have their own network -- this includes
 | 
			
		||||
	// containers running in Kubernetes pods that use the network of the
 | 
			
		||||
	// infrastructure container -- does not need their stats to be
 | 
			
		||||
	// reported. This stops metrics being reported multiple times for each
 | 
			
		||||
	// container in a pod.
 | 
			
		||||
	metrics := common.RemoveNetMetrics(includedMetrics, cInfo.Labels["io.kubernetes.container.name"] != "POD")
 | 
			
		||||
 | 
			
		||||
	libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, metrics)
 | 
			
		||||
 | 
			
		||||
	// TODO: extract object mother method
 | 
			
		||||
	handler := &crioContainerHandler{
 | 
			
		||||
| 
						 | 
				
			
			@ -161,7 +169,7 @@ func newCrioContainerHandler(
 | 
			
		|||
		rootfsStorageDir:    rootfsStorageDir,
 | 
			
		||||
		envs:                make(map[string]string),
 | 
			
		||||
		labels:              cInfo.Labels,
 | 
			
		||||
		includedMetrics:     includedMetrics,
 | 
			
		||||
		includedMetrics:     metrics,
 | 
			
		||||
		reference:           containerReference,
 | 
			
		||||
		libcontainerHandler: libcontainerHandler,
 | 
			
		||||
		cgroupManager:       cgroupManager,
 | 
			
		||||
| 
						 | 
				
			
			@ -210,16 +218,10 @@ func (h *crioContainerHandler) ContainerReference() (info.ContainerReference, er
 | 
			
		|||
	return h.reference, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *crioContainerHandler) needNet() bool {
 | 
			
		||||
	if h.includedMetrics.Has(container.NetworkUsageMetrics) {
 | 
			
		||||
		return h.labels["io.kubernetes.container.name"] == "POD"
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *crioContainerHandler) GetSpec() (info.ContainerSpec, error) {
 | 
			
		||||
	hasFilesystem := h.includedMetrics.Has(container.DiskUsageMetrics)
 | 
			
		||||
	spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem)
 | 
			
		||||
	hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics)
 | 
			
		||||
	spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem)
 | 
			
		||||
 | 
			
		||||
	spec.Labels = h.labels
 | 
			
		||||
	spec.Envs = h.envs
 | 
			
		||||
| 
						 | 
				
			
			@ -306,13 +308,7 @@ func (h *crioContainerHandler) GetStats() (*info.ContainerStats, error) {
 | 
			
		|||
		return stats, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !h.needNet() {
 | 
			
		||||
		// Clean up stats for containers that don't have their own network - this
 | 
			
		||||
		// includes containers running in Kubernetes pods that use the network of the
 | 
			
		||||
		// infrastructure container. This stops metrics being reported multiple times
 | 
			
		||||
		// for each container in a pod.
 | 
			
		||||
		stats.Network = info.NetworkStats{}
 | 
			
		||||
	} else if len(stats.Network.Interfaces) == 0 {
 | 
			
		||||
	if h.includedMetrics.Has(container.NetworkUsageMetrics) && len(stats.Network.Interfaces) == 0 {
 | 
			
		||||
		// No network related information indicates that the pid of the
 | 
			
		||||
		// container is not longer valid and we need to ask crio to
 | 
			
		||||
		// provide the pid of another container from that pod
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,6 +93,14 @@ var AllMetrics = MetricSet{
 | 
			
		|||
	OOMMetrics:                     struct{}{},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AllNetworkMetrics represents all network metrics that cAdvisor supports.
 | 
			
		||||
var AllNetworkMetrics = MetricSet{
 | 
			
		||||
	NetworkUsageMetrics:            struct{}{},
 | 
			
		||||
	NetworkTcpUsageMetrics:         struct{}{},
 | 
			
		||||
	NetworkAdvancedTcpUsageMetrics: struct{}{},
 | 
			
		||||
	NetworkUdpUsageMetrics:         struct{}{},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mk MetricKind) String() string {
 | 
			
		||||
	return string(mk)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +112,15 @@ func (ms MetricSet) Has(mk MetricKind) bool {
 | 
			
		|||
	return exists
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ms MetricSet) HasAny(ms1 MetricSet) bool {
 | 
			
		||||
	for m := range ms1 {
 | 
			
		||||
		if _, ok := ms[m]; ok {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ms MetricSet) add(mk MetricKind) {
 | 
			
		||||
	ms[mk] = struct{}{}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -199,7 +216,9 @@ func InitializePlugins(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includ
 | 
			
		|||
	for name, plugin := range plugins {
 | 
			
		||||
		watcher, err := plugin.Register(factory, fsInfo, includedMetrics)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.V(5).Infof("Registration of the %s container factory failed: %v", name, err)
 | 
			
		||||
			klog.Infof("Registration of the %s container factory failed: %v", name, err)
 | 
			
		||||
		} else {
 | 
			
		||||
			klog.Infof("Registration of the %s container factory successfully", name)
 | 
			
		||||
		}
 | 
			
		||||
		if watcher != nil {
 | 
			
		||||
			containerWatchers = append(containerWatchers, watcher)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										91
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/libcontainer/handler.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										91
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/container/libcontainer/handler.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -21,7 +21,6 @@ import (
 | 
			
		|||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +39,6 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	whitelistedUlimits      = [...]string{"max_open_files"}
 | 
			
		||||
	referencedResetInterval = flag.Uint64("referenced_reset_interval", 0,
 | 
			
		||||
		"Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -206,54 +204,56 @@ func parseUlimit(value string) (int64, error) {
 | 
			
		|||
	return num, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isUlimitWhitelisted(name string) bool {
 | 
			
		||||
	for _, whitelist := range whitelistedUlimits {
 | 
			
		||||
		if name == whitelist {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func processLimitsFile(fileData string) []info.UlimitSpec {
 | 
			
		||||
	const maxOpenFilesLinePrefix = "Max open files"
 | 
			
		||||
 | 
			
		||||
	limits := strings.Split(fileData, "\n")
 | 
			
		||||
	ulimits := make([]info.UlimitSpec, 0, len(limits))
 | 
			
		||||
	for _, lim := range limits {
 | 
			
		||||
		// Skip any headers/footers
 | 
			
		||||
		if strings.HasPrefix(lim, "Max") {
 | 
			
		||||
 | 
			
		||||
			// Line format: Max open files            16384                16384                files
 | 
			
		||||
			fields := regexp.MustCompile(`[\s]{2,}`).Split(lim, -1)
 | 
			
		||||
			name := strings.Replace(strings.ToLower(strings.TrimSpace(fields[0])), " ", "_", -1)
 | 
			
		||||
 | 
			
		||||
			found := isUlimitWhitelisted(name)
 | 
			
		||||
			if !found {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			soft := strings.TrimSpace(fields[1])
 | 
			
		||||
			softNum, softErr := parseUlimit(soft)
 | 
			
		||||
 | 
			
		||||
			hard := strings.TrimSpace(fields[2])
 | 
			
		||||
			hardNum, hardErr := parseUlimit(hard)
 | 
			
		||||
 | 
			
		||||
			// Omit metric if there were any parsing errors
 | 
			
		||||
			if softErr == nil && hardErr == nil {
 | 
			
		||||
				ulimitSpec := info.UlimitSpec{
 | 
			
		||||
					Name:      name,
 | 
			
		||||
					SoftLimit: int64(softNum),
 | 
			
		||||
					HardLimit: int64(hardNum),
 | 
			
		||||
				}
 | 
			
		||||
				ulimits = append(ulimits, ulimitSpec)
 | 
			
		||||
		if strings.HasPrefix(lim, "Max open files") {
 | 
			
		||||
			// Remove line prefix
 | 
			
		||||
			ulimit, err := processMaxOpenFileLimitLine(
 | 
			
		||||
				"max_open_files",
 | 
			
		||||
				lim[len(maxOpenFilesLinePrefix):],
 | 
			
		||||
			)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				ulimits = append(ulimits, ulimit)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ulimits
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Any caller of processMaxOpenFileLimitLine must ensure that the name prefix is already removed from the limit line.
 | 
			
		||||
// with the "Max open files" prefix.
 | 
			
		||||
func processMaxOpenFileLimitLine(name, line string) (info.UlimitSpec, error) {
 | 
			
		||||
	// Remove any leading whitespace
 | 
			
		||||
	line = strings.TrimSpace(line)
 | 
			
		||||
	// Split on whitespace
 | 
			
		||||
	fields := strings.Fields(line)
 | 
			
		||||
	if len(fields) != 3 {
 | 
			
		||||
		return info.UlimitSpec{}, fmt.Errorf("unable to parse max open files line: %s", line)
 | 
			
		||||
	}
 | 
			
		||||
	// The first field is the soft limit, the second is the hard limit
 | 
			
		||||
	soft, err := parseUlimit(fields[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.UlimitSpec{}, err
 | 
			
		||||
	}
 | 
			
		||||
	hard, err := parseUlimit(fields[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.UlimitSpec{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return info.UlimitSpec{
 | 
			
		||||
		Name:      name,
 | 
			
		||||
		SoftLimit: soft,
 | 
			
		||||
		HardLimit: hard,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
 | 
			
		||||
	filePath := path.Join(rootFs, "/proc", strconv.Itoa(rootPid), "limits")
 | 
			
		||||
	out, err := ioutil.ReadFile(filePath)
 | 
			
		||||
	out, err := os.ReadFile(filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.V(4).Infof("error while listing directory %q to read ulimits: %v", filePath, err)
 | 
			
		||||
		return []info.UlimitSpec{}
 | 
			
		||||
| 
						 | 
				
			
			@ -264,14 +264,14 @@ func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
 | 
			
		|||
func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info.ProcessStats, error) {
 | 
			
		||||
	var fdCount, socketCount uint64
 | 
			
		||||
	filePath := path.Join(cgroupPath, "cgroup.procs")
 | 
			
		||||
	out, err := ioutil.ReadFile(filePath)
 | 
			
		||||
	out, err := os.ReadFile(filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.ProcessStats{}, fmt.Errorf("couldn't open cpu cgroup procs file %v : %v", filePath, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pids := strings.Split(string(out), "\n")
 | 
			
		||||
 | 
			
		||||
	// EOL is also treated as a new line while reading "cgroup.procs" file with ioutil.ReadFile.
 | 
			
		||||
	// EOL is also treated as a new line while reading "cgroup.procs" file with os.ReadFile.
 | 
			
		||||
	// The last value is an empty string "". Ex: pids = ["22", "1223", ""]
 | 
			
		||||
	// Trim the last value
 | 
			
		||||
	if len(pids) != 0 && pids[len(pids)-1] == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +280,7 @@ func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info.
 | 
			
		|||
 | 
			
		||||
	for _, pid := range pids {
 | 
			
		||||
		dirPath := path.Join(rootFs, "/proc", pid, "fd")
 | 
			
		||||
		fds, err := ioutil.ReadDir(dirPath)
 | 
			
		||||
		fds, err := os.ReadDir(dirPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.V(4).Infof("error while listing directory %q to measure fd count: %v", dirPath, err)
 | 
			
		||||
			continue
 | 
			
		||||
| 
						 | 
				
			
			@ -324,7 +324,7 @@ func (h *Handler) schedulerStatsFromProcs() (info.CpuSchedstat, error) {
 | 
			
		|||
			return info.CpuSchedstat{}, fmt.Errorf("couldn't open scheduler statistics for process %d: %v", pid, err)
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		contents, err := ioutil.ReadAll(f)
 | 
			
		||||
		contents, err := io.ReadAll(f)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return info.CpuSchedstat{}, fmt.Errorf("couldn't read scheduler statistics for process %d: %v", pid, err)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -392,7 +392,7 @@ func getReferencedKBytes(pids []int) (uint64, error) {
 | 
			
		|||
	foundMatch := false
 | 
			
		||||
	for _, pid := range pids {
 | 
			
		||||
		smapsFilePath := fmt.Sprintf(smapsFilePathPattern, pid)
 | 
			
		||||
		smapsContent, err := ioutil.ReadFile(smapsFilePath)
 | 
			
		||||
		smapsContent, err := os.ReadFile(smapsFilePath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.V(5).Infof("Cannot read %s file, err: %s", smapsFilePath, err)
 | 
			
		||||
			if os.IsNotExist(err) {
 | 
			
		||||
| 
						 | 
				
			
			@ -468,7 +468,7 @@ func networkStatsFromProc(rootFs string, pid int) ([]info.InterfaceStats, error)
 | 
			
		|||
	return ifaceStats, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ignoredDevicePrefixes = []string{"lo", "veth", "docker"}
 | 
			
		||||
var ignoredDevicePrefixes = []string{"lo", "veth", "docker", "nerdctl"}
 | 
			
		||||
 | 
			
		||||
func isIgnoredDevice(ifName string) bool {
 | 
			
		||||
	for _, prefix := range ignoredDevicePrefixes {
 | 
			
		||||
| 
						 | 
				
			
			@ -575,7 +575,7 @@ func advancedTCPStatsFromProc(rootFs string, pid int, file1, file2 string) (info
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func scanAdvancedTCPStats(advancedStats *info.TcpAdvancedStat, advancedTCPStatsFile string) error {
 | 
			
		||||
	data, err := ioutil.ReadFile(advancedTCPStatsFile)
 | 
			
		||||
	data, err := os.ReadFile(advancedTCPStatsFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failure opening %s: %v", advancedTCPStatsFile, err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -631,7 +631,7 @@ func scanAdvancedTCPStats(advancedStats *info.TcpAdvancedStat, advancedTCPStatsF
 | 
			
		|||
func scanTCPStats(tcpStatsFile string) (info.TcpStat, error) {
 | 
			
		||||
	var stats info.TcpStat
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadFile(tcpStatsFile)
 | 
			
		||||
	data, err := os.ReadFile(tcpStatsFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return stats, fmt.Errorf("failure opening %s: %v", tcpStatsFile, err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -802,6 +802,7 @@ func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
 | 
			
		|||
	ret.Memory.Usage = s.MemoryStats.Usage.Usage
 | 
			
		||||
	ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage
 | 
			
		||||
	ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt
 | 
			
		||||
	ret.Memory.KernelUsage = s.MemoryStats.KernelUsage.Usage
 | 
			
		||||
 | 
			
		||||
	if cgroups.IsCgroup2UnifiedMode() {
 | 
			
		||||
		ret.Memory.Cache = s.MemoryStats.Stats["file"]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,13 +18,13 @@ package raw
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	inotify "k8s.io/utils/inotify"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/cadvisor/container"
 | 
			
		||||
	"github.com/google/cadvisor/container/common"
 | 
			
		||||
	"github.com/google/cadvisor/container/libcontainer"
 | 
			
		||||
	"github.com/google/cadvisor/watcher"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +43,8 @@ type rawContainerWatcher struct {
 | 
			
		|||
	stopWatcher chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewRawContainerWatcher() (watcher.ContainerWatcher, error) {
 | 
			
		||||
	cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(nil)
 | 
			
		||||
func NewRawContainerWatcher(includedMetrics container.MetricSet) (watcher.ContainerWatcher, error) {
 | 
			
		||||
	cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to get cgroup subsystems: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +139,7 @@ func (w *rawContainerWatcher) watchDirectory(events chan watcher.ContainerEvent,
 | 
			
		|||
 | 
			
		||||
	// TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime.
 | 
			
		||||
	// Watch subdirectories as well.
 | 
			
		||||
	entries, err := ioutil.ReadDir(dir)
 | 
			
		||||
	entries, err := os.ReadDir(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return alreadyWatching, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,6 @@ package fs
 | 
			
		|||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path"
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +145,7 @@ func getFsUUIDToDeviceNameMap() (map[string]string, error) {
 | 
			
		|||
		return make(map[string]string), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	files, err := ioutil.ReadDir(dir)
 | 
			
		||||
	files, err := os.ReadDir(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -387,6 +386,7 @@ func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	nfsInfo := make(map[string]Fs, 0)
 | 
			
		||||
	for device, partition := range i.partitions {
 | 
			
		||||
		_, hasMount := mountSet[partition.mountpoint]
 | 
			
		||||
		_, hasDevice := deviceSet[device]
 | 
			
		||||
| 
						 | 
				
			
			@ -395,7 +395,11 @@ func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error
 | 
			
		|||
				err error
 | 
			
		||||
				fs  Fs
 | 
			
		||||
			)
 | 
			
		||||
			switch partition.fsType {
 | 
			
		||||
			fsType := partition.fsType
 | 
			
		||||
			if strings.HasPrefix(partition.fsType, "nfs") {
 | 
			
		||||
				fsType = "nfs"
 | 
			
		||||
			}
 | 
			
		||||
			switch fsType {
 | 
			
		||||
			case DeviceMapper.String():
 | 
			
		||||
				fs.Capacity, fs.Free, fs.Available, err = getDMStats(device, partition.blockSize)
 | 
			
		||||
				klog.V(5).Infof("got devicemapper fs capacity stats: capacity: %v free: %v available: %v:", fs.Capacity, fs.Free, fs.Available)
 | 
			
		||||
| 
						 | 
				
			
			@ -408,6 +412,22 @@ func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error
 | 
			
		|||
				}
 | 
			
		||||
				// if /dev/zfs is not present default to VFS
 | 
			
		||||
				fallthrough
 | 
			
		||||
			case NFS.String():
 | 
			
		||||
				devId := fmt.Sprintf("%d:%d", partition.major, partition.minor)
 | 
			
		||||
				if v, ok := nfsInfo[devId]; ok {
 | 
			
		||||
					fs = v
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				var inodes, inodesFree uint64
 | 
			
		||||
				fs.Capacity, fs.Free, fs.Available, inodes, inodesFree, err = getVfsStats(partition.mountpoint)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					klog.V(4).Infof("the file system type is %s, partition mountpoint does not exist: %v, error: %v", partition.fsType, partition.mountpoint, err)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				fs.Inodes = &inodes
 | 
			
		||||
				fs.InodesFree = &inodesFree
 | 
			
		||||
				fs.Type = VFS
 | 
			
		||||
				nfsInfo[devId] = fs
 | 
			
		||||
			default:
 | 
			
		||||
				var inodes, inodesFree uint64
 | 
			
		||||
				if utils.FileExists(partition.mountpoint) {
 | 
			
		||||
| 
						 | 
				
			
			@ -616,7 +636,7 @@ func GetDirUsage(dir string) (UsageInfo, error) {
 | 
			
		|||
 | 
			
		||||
	rootStat, ok := rootInfo.Sys().(*syscall.Stat_t)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return usage, fmt.Errorf("unsuported fileinfo for getting inode usage of %q", dir)
 | 
			
		||||
		return usage, fmt.Errorf("unsupported fileinfo for getting inode usage of %q", dir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rootDevID := rootStat.Dev
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ type Context struct {
 | 
			
		|||
	// docker root directory.
 | 
			
		||||
	Docker DockerContext
 | 
			
		||||
	Crio   CrioContext
 | 
			
		||||
	Podman PodmanContext
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DockerContext struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +31,12 @@ type DockerContext struct {
 | 
			
		|||
	DriverStatus map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PodmanContext struct {
 | 
			
		||||
	Root         string
 | 
			
		||||
	Driver       string
 | 
			
		||||
	DriverStatus map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CrioContext struct {
 | 
			
		||||
	Root string
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +57,7 @@ const (
 | 
			
		|||
	ZFS          FsType = "zfs"
 | 
			
		||||
	DeviceMapper FsType = "devicemapper"
 | 
			
		||||
	VFS          FsType = "vfs"
 | 
			
		||||
	NFS          FsType = "nfs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Fs struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -395,6 +395,10 @@ type MemoryStats struct {
 | 
			
		|||
 | 
			
		||||
	Failcnt uint64 `json:"failcnt"`
 | 
			
		||||
 | 
			
		||||
	// Size of kernel memory allocated in bytes.
 | 
			
		||||
	// Units: Bytes.
 | 
			
		||||
	KernelUsage uint64 `json:"kernel"`
 | 
			
		||||
 | 
			
		||||
	ContainerData    MemoryStatsMemoryData `json:"container_data,omitempty"`
 | 
			
		||||
	HierarchicalData MemoryStatsMemoryData `json:"hierarchical_data,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ import (
 | 
			
		|||
const (
 | 
			
		||||
	TypeName   = "name"
 | 
			
		||||
	TypeDocker = "docker"
 | 
			
		||||
	TypePodman = "podman"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type CpuSpec struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ package machine
 | 
			
		|||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ func getInfoFromFiles(filePaths string) string {
 | 
			
		|||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	for _, file := range strings.Split(filePaths, ",") {
 | 
			
		||||
		id, err := ioutil.ReadFile(file)
 | 
			
		||||
		id, err := os.ReadFile(file)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return strings.TrimSpace(string(id))
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ func Info(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (*info.Mach
 | 
			
		|||
		rootFs = "/rootfs"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cpuinfo, err := ioutil.ReadFile(filepath.Join(rootFs, "/proc/cpuinfo"))
 | 
			
		||||
	cpuinfo, err := os.ReadFile(filepath.Join(rootFs, "/proc/cpuinfo"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,6 @@ package machine
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +45,7 @@ var (
 | 
			
		|||
	swapCapacityRegexp   = regexp.MustCompile(`SwapTotal:\s*([0-9]+) kB`)
 | 
			
		||||
	vendorIDRegexp       = regexp.MustCompile(`vendor_id\s*:\s*(\w+)`)
 | 
			
		||||
 | 
			
		||||
	cpuBusPath         = "/sys/bus/cpu/devices/"
 | 
			
		||||
	cpuAttributesPath  = "/sys/devices/system/cpu/"
 | 
			
		||||
	isMemoryController = regexp.MustCompile("mc[0-9]+")
 | 
			
		||||
	isDimm             = regexp.MustCompile("dimm[0-9]+")
 | 
			
		||||
	machineArch        = getMachineArch()
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +61,7 @@ func GetCPUVendorID(procInfo []byte) string {
 | 
			
		|||
 | 
			
		||||
	matches := vendorIDRegexp.FindSubmatch(procInfo)
 | 
			
		||||
	if len(matches) != 2 {
 | 
			
		||||
		klog.Warning("Cannot read vendor id correctly, set empty.")
 | 
			
		||||
		klog.V(4).Info("Cannot read vendor id correctly, set empty.")
 | 
			
		||||
		return vendorID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +76,7 @@ func GetPhysicalCores(procInfo []byte) int {
 | 
			
		|||
	if numCores == 0 {
 | 
			
		||||
		// read number of cores from /sys/bus/cpu/devices/cpu*/topology/core_id to deal with processors
 | 
			
		||||
		// for which 'core id' is not available in /proc/cpuinfo
 | 
			
		||||
		numCores = sysfs.GetUniqueCPUPropertyCount(cpuBusPath, sysfs.CPUCoreID)
 | 
			
		||||
		numCores = sysfs.GetUniqueCPUPropertyCount(cpuAttributesPath, sysfs.CPUCoreID)
 | 
			
		||||
	}
 | 
			
		||||
	if numCores == 0 {
 | 
			
		||||
		klog.Errorf("Cannot read number of physical cores correctly, number of cores set to %d", numCores)
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +90,7 @@ func GetSockets(procInfo []byte) int {
 | 
			
		|||
	if numSocket == 0 {
 | 
			
		||||
		// read number of sockets from /sys/bus/cpu/devices/cpu*/topology/physical_package_id to deal with processors
 | 
			
		||||
		// for which 'physical id' is not available in /proc/cpuinfo
 | 
			
		||||
		numSocket = sysfs.GetUniqueCPUPropertyCount(cpuBusPath, sysfs.CPUPhysicalPackageID)
 | 
			
		||||
		numSocket = sysfs.GetUniqueCPUPropertyCount(cpuAttributesPath, sysfs.CPUPhysicalPackageID)
 | 
			
		||||
	}
 | 
			
		||||
	if numSocket == 0 {
 | 
			
		||||
		klog.Errorf("Cannot read number of sockets correctly, number of sockets set to %d", numSocket)
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +102,7 @@ func GetSockets(procInfo []byte) int {
 | 
			
		|||
func GetClockSpeed(procInfo []byte) (uint64, error) {
 | 
			
		||||
	// First look through sys to find a max supported cpu frequency.
 | 
			
		||||
	if utils.FileExists(maxFreqFile) {
 | 
			
		||||
		val, err := ioutil.ReadFile(maxFreqFile)
 | 
			
		||||
		val, err := os.ReadFile(maxFreqFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +135,7 @@ func GetClockSpeed(procInfo []byte) (uint64, error) {
 | 
			
		|||
// GetMachineMemoryCapacity returns the machine's total memory from /proc/meminfo.
 | 
			
		||||
// Returns the total memory capacity as an uint64 (number of bytes).
 | 
			
		||||
func GetMachineMemoryCapacity() (uint64, error) {
 | 
			
		||||
	out, err := ioutil.ReadFile("/proc/meminfo")
 | 
			
		||||
	out, err := os.ReadFile("/proc/meminfo")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +155,7 @@ func GetMachineMemoryCapacity() (uint64, error) {
 | 
			
		|||
// (https://github.com/torvalds/linux/blob/v5.5/drivers/edac/edac_mc.c#L198)
 | 
			
		||||
func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error) {
 | 
			
		||||
	memory := map[string]*info.MemoryInfo{}
 | 
			
		||||
	names, err := ioutil.ReadDir(edacPath)
 | 
			
		||||
	names, err := os.ReadDir(edacPath)
 | 
			
		||||
	// On some architectures (such as ARM) memory controller device may not exist.
 | 
			
		||||
	// If this is the case then we ignore error and return empty slice.
 | 
			
		||||
	_, ok := err.(*os.PathError)
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +169,7 @@ func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error
 | 
			
		|||
		if !isMemoryController.MatchString(controller) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		dimms, err := ioutil.ReadDir(path.Join(edacPath, controllerDir.Name()))
 | 
			
		||||
		dimms, err := os.ReadDir(path.Join(edacPath, controllerDir.Name()))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return map[string]*info.MemoryInfo{}, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +178,7 @@ func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error
 | 
			
		|||
			if !isDimm.MatchString(dimm) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			memType, err := ioutil.ReadFile(path.Join(edacPath, controller, dimm, memTypeFileName))
 | 
			
		||||
			memType, err := os.ReadFile(path.Join(edacPath, controller, dimm, memTypeFileName))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return map[string]*info.MemoryInfo{}, err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +186,7 @@ func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error
 | 
			
		|||
			if _, exists := memory[readableMemType]; !exists {
 | 
			
		||||
				memory[readableMemType] = &info.MemoryInfo{}
 | 
			
		||||
			}
 | 
			
		||||
			size, err := ioutil.ReadFile(path.Join(edacPath, controller, dimm, sizeFileName))
 | 
			
		||||
			size, err := os.ReadFile(path.Join(edacPath, controller, dimm, sizeFileName))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return map[string]*info.MemoryInfo{}, err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +209,7 @@ func mbToBytes(megabytes int) int {
 | 
			
		|||
// GetMachineSwapCapacity returns the machine's total swap from /proc/meminfo.
 | 
			
		||||
// Returns the total swap capacity as an uint64 (number of bytes).
 | 
			
		||||
func GetMachineSwapCapacity() (uint64, error) {
 | 
			
		||||
	out, err := ioutil.ReadFile("/proc/meminfo")
 | 
			
		||||
	out, err := os.ReadFile("/proc/meminfo")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/machine/operatingsystem_unix.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										5
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/machine/operatingsystem_unix.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -19,7 +19,6 @@ package machine
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"regexp"
 | 
			
		||||
| 
						 | 
				
			
			@ -39,10 +38,10 @@ func getOperatingSystem() (string, error) {
 | 
			
		|||
		}
 | 
			
		||||
		return string(osName), nil
 | 
			
		||||
	}
 | 
			
		||||
	bytes, err := ioutil.ReadFile("/etc/os-release")
 | 
			
		||||
	bytes, err := os.ReadFile("/etc/os-release")
 | 
			
		||||
	if err != nil && os.IsNotExist(err) {
 | 
			
		||||
		// /usr/lib/os-release in stateless systems like Clear Linux
 | 
			
		||||
		bytes, err = ioutil.ReadFile("/usr/lib/os-release")
 | 
			
		||||
		bytes, err = os.ReadFile("/usr/lib/os-release")
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("error opening file : %v", err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,9 +17,9 @@ package manager
 | 
			
		|||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +243,7 @@ func (cd *containerData) ReadFile(filepath string, inHostNamespace bool) ([]byte
 | 
			
		|||
	for _, pid := range pids {
 | 
			
		||||
		filePath := path.Join(rootfs, "/proc", pid, "/root", filepath)
 | 
			
		||||
		klog.V(3).Infof("Trying path %q", filePath)
 | 
			
		||||
		data, err := ioutil.ReadFile(filePath)
 | 
			
		||||
		data, err := os.ReadFile(filePath)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return data, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -323,7 +323,7 @@ func (cd *containerData) parseProcessList(cadvisorContainer string, inHostNamesp
 | 
			
		|||
 | 
			
		||||
		var fdCount int
 | 
			
		||||
		dirPath := path.Join(rootfs, "/proc", strconv.Itoa(processInfo.Pid), "fd")
 | 
			
		||||
		fds, err := ioutil.ReadDir(dirPath)
 | 
			
		||||
		fds, err := os.ReadDir(dirPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.V(4).Infof("error while listing directory %q to measure fd count: %v", dirPath, err)
 | 
			
		||||
			continue
 | 
			
		||||
| 
						 | 
				
			
			@ -482,7 +482,7 @@ func (cd *containerData) nextHousekeepingInterval() time.Duration {
 | 
			
		|||
		stats, err := cd.memoryCache.RecentStats(cd.info.Name, empty, empty, 2)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if cd.allowErrorLogging() {
 | 
			
		||||
				klog.Warningf("Failed to get RecentStats(%q) while determining the next housekeeping: %v", cd.info.Name, err)
 | 
			
		||||
				klog.V(4).Infof("Failed to get RecentStats(%q) while determining the next housekeeping: %v", cd.info.Name, err)
 | 
			
		||||
			}
 | 
			
		||||
		} else if len(stats) == 2 {
 | 
			
		||||
			// TODO(vishnuk): Use no processes as a signal.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,10 +58,13 @@ var eventStorageAgeLimit = flag.String("event_storage_age_limit", "default=24h",
 | 
			
		|||
var eventStorageEventLimit = flag.String("event_storage_event_limit", "default=100000", "Max number of events to store (per type). Value is a comma separated list of key values, where the keys are event types (e.g.: creation, oom) or \"default\" and the value is an integer. Default is applied to all non-specified event types")
 | 
			
		||||
var applicationMetricsCountLimit = flag.Int("application_metrics_count_limit", 100, "Max number of application metrics to store (per container)")
 | 
			
		||||
 | 
			
		||||
// The namespace under which Docker aliases are unique.
 | 
			
		||||
const DockerNamespace = "docker"
 | 
			
		||||
// The namespace under which aliases are unique.
 | 
			
		||||
const (
 | 
			
		||||
	DockerNamespace = "docker"
 | 
			
		||||
	PodmanNamespace = "podman"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var HousekeepingConfigFlags = HouskeepingConfig{
 | 
			
		||||
var HousekeepingConfigFlags = HousekeepingConfig{
 | 
			
		||||
	flag.Duration("max_housekeeping_interval", 60*time.Second, "Largest interval to allow between container housekeepings"),
 | 
			
		||||
	flag.Bool("allow_dynamic_housekeeping", true, "Whether to allow the housekeeping interval to be dynamic"),
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -136,16 +139,20 @@ type Manager interface {
 | 
			
		|||
 | 
			
		||||
	// Returns debugging information. Map of lines per category.
 | 
			
		||||
	DebugInfo() map[string][]string
 | 
			
		||||
 | 
			
		||||
	AllPodmanContainers(c *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error)
 | 
			
		||||
 | 
			
		||||
	PodmanContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Housekeeping configuration for the manager
 | 
			
		||||
type HouskeepingConfig = struct {
 | 
			
		||||
type HousekeepingConfig = struct {
 | 
			
		||||
	Interval     *time.Duration
 | 
			
		||||
	AllowDynamic *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New takes a memory storage and returns a new manager.
 | 
			
		||||
func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, houskeepingConfig HouskeepingConfig, includedMetricsSet container.MetricSet, collectorHTTPClient *http.Client, rawContainerCgroupPathPrefixWhiteList, containerEnvMetadataWhiteList []string, perfEventsFile string, resctrlInterval time.Duration) (Manager, error) {
 | 
			
		||||
func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, HousekeepingConfig HousekeepingConfig, includedMetricsSet container.MetricSet, collectorHTTPClient *http.Client, rawContainerCgroupPathPrefixWhiteList, containerEnvMetadataWhiteList []string, perfEventsFile string, resctrlInterval time.Duration) (Manager, error) {
 | 
			
		||||
	if memoryCache == nil {
 | 
			
		||||
		return nil, fmt.Errorf("manager requires memory storage")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -192,8 +199,8 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, houskeepingConfig
 | 
			
		|||
		cadvisorContainer:                     selfContainer,
 | 
			
		||||
		inHostNamespace:                       inHostNamespace,
 | 
			
		||||
		startupTime:                           time.Now(),
 | 
			
		||||
		maxHousekeepingInterval:               *houskeepingConfig.Interval,
 | 
			
		||||
		allowDynamicHousekeeping:              *houskeepingConfig.AllowDynamic,
 | 
			
		||||
		maxHousekeepingInterval:               *HousekeepingConfig.Interval,
 | 
			
		||||
		allowDynamicHousekeeping:              *HousekeepingConfig.AllowDynamic,
 | 
			
		||||
		includedMetrics:                       includedMetricsSet,
 | 
			
		||||
		containerWatchers:                     []watcher.ContainerWatcher{},
 | 
			
		||||
		eventsChannel:                         eventsChannel,
 | 
			
		||||
| 
						 | 
				
			
			@ -265,6 +272,19 @@ type manager struct {
 | 
			
		|||
	containerEnvMetadataWhiteList []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) PodmanContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
 | 
			
		||||
	container, err := m.namespacedContainer(containerName, PodmanNamespace)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.ContainerInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	inf, err := m.containerDataToContainerInfo(container, query)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.ContainerInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return *inf, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start the container manager.
 | 
			
		||||
func (m *manager) Start() error {
 | 
			
		||||
	m.containerWatchers = container.InitializePlugins(m, m.fsInfo, m.includedMetrics)
 | 
			
		||||
| 
						 | 
				
			
			@ -274,7 +294,7 @@ func (m *manager) Start() error {
 | 
			
		|||
		klog.Errorf("Registration of the raw container factory failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rawWatcher, err := raw.NewRawContainerWatcher()
 | 
			
		||||
	rawWatcher, err := raw.NewRawContainerWatcher(m.includedMetrics)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -581,14 +601,14 @@ func (m *manager) SubcontainersInfo(containerName string, query *info.ContainerI
 | 
			
		|||
	return m.containerDataSliceToContainerInfoSlice(containers, query)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) getAllDockerContainers() map[string]*containerData {
 | 
			
		||||
func (m *manager) getAllNamespacedContainers(ns string) map[string]*containerData {
 | 
			
		||||
	m.containersLock.RLock()
 | 
			
		||||
	defer m.containersLock.RUnlock()
 | 
			
		||||
	containers := make(map[string]*containerData, len(m.containers))
 | 
			
		||||
 | 
			
		||||
	// Get containers in the Docker namespace.
 | 
			
		||||
	// Get containers in a namespace.
 | 
			
		||||
	for name, cont := range m.containers {
 | 
			
		||||
		if name.Namespace == DockerNamespace {
 | 
			
		||||
		if name.Namespace == ns {
 | 
			
		||||
			containers[cont.info.Name] = cont
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -596,48 +616,34 @@ func (m *manager) getAllDockerContainers() map[string]*containerData {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
 | 
			
		||||
	containers := m.getAllDockerContainers()
 | 
			
		||||
 | 
			
		||||
	output := make(map[string]info.ContainerInfo, len(containers))
 | 
			
		||||
	for name, cont := range containers {
 | 
			
		||||
		inf, err := m.containerDataToContainerInfo(cont, query)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Ignore the error because of race condition and return best-effort result.
 | 
			
		||||
			if err == memory.ErrDataNotFound {
 | 
			
		||||
				klog.Warningf("Error getting data for container %s because of race condition", name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		output[name] = *inf
 | 
			
		||||
	}
 | 
			
		||||
	return output, nil
 | 
			
		||||
	containers := m.getAllNamespacedContainers(DockerNamespace)
 | 
			
		||||
	return m.containersInfo(containers, query)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) getDockerContainer(containerName string) (*containerData, error) {
 | 
			
		||||
func (m *manager) namespacedContainer(containerName string, ns string) (*containerData, error) {
 | 
			
		||||
	m.containersLock.RLock()
 | 
			
		||||
	defer m.containersLock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	// Check for the container in the Docker container namespace.
 | 
			
		||||
	// Check for the container in the namespace.
 | 
			
		||||
	cont, ok := m.containers[namespacedContainerName{
 | 
			
		||||
		Namespace: DockerNamespace,
 | 
			
		||||
		Namespace: ns,
 | 
			
		||||
		Name:      containerName,
 | 
			
		||||
	}]
 | 
			
		||||
 | 
			
		||||
	// Look for container by short prefix name if no exact match found.
 | 
			
		||||
	if !ok {
 | 
			
		||||
		for contName, c := range m.containers {
 | 
			
		||||
			if contName.Namespace == DockerNamespace && strings.HasPrefix(contName.Name, containerName) {
 | 
			
		||||
			if contName.Namespace == ns && strings.HasPrefix(contName.Name, containerName) {
 | 
			
		||||
				if cont == nil {
 | 
			
		||||
					cont = c
 | 
			
		||||
				} else {
 | 
			
		||||
					return nil, fmt.Errorf("unable to find container. Container %q is not unique", containerName)
 | 
			
		||||
					return nil, fmt.Errorf("unable to find container in %q namespace. Container %q is not unique", ns, containerName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if cont == nil {
 | 
			
		||||
			return nil, fmt.Errorf("unable to find Docker container %q", containerName)
 | 
			
		||||
			return nil, fmt.Errorf("unable to find container %q in %q namespace", containerName, ns)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +651,7 @@ func (m *manager) getDockerContainer(containerName string) (*containerData, erro
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) DockerContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
 | 
			
		||||
	container, err := m.getDockerContainer(containerName)
 | 
			
		||||
	container, err := m.namespacedContainer(containerName, DockerNamespace)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.ContainerInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -691,7 +697,7 @@ func (m *manager) GetRequestedContainersInfo(containerName string, options v2.Re
 | 
			
		|||
		info, err := m.containerDataToContainerInfo(data, &query)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == memory.ErrDataNotFound {
 | 
			
		||||
				klog.Warningf("Error getting data for container %s because of race condition", name)
 | 
			
		||||
				klog.V(4).Infof("Error getting data for container %s because of race condition", name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			errs.append(name, "containerDataToContainerInfo", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -717,19 +723,23 @@ func (m *manager) getRequestedContainers(containerName string, options v2.Reques
 | 
			
		|||
				return containersMap, fmt.Errorf("unknown container: %q", containerName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case v2.TypeDocker:
 | 
			
		||||
	case v2.TypeDocker, v2.TypePodman:
 | 
			
		||||
		namespace := map[string]string{
 | 
			
		||||
			v2.TypeDocker: DockerNamespace,
 | 
			
		||||
			v2.TypePodman: PodmanNamespace,
 | 
			
		||||
		}[options.IdType]
 | 
			
		||||
		if !options.Recursive {
 | 
			
		||||
			containerName = strings.TrimPrefix(containerName, "/")
 | 
			
		||||
			cont, err := m.getDockerContainer(containerName)
 | 
			
		||||
			cont, err := m.namespacedContainer(containerName, namespace)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return containersMap, err
 | 
			
		||||
			}
 | 
			
		||||
			containersMap[cont.info.Name] = cont
 | 
			
		||||
		} else {
 | 
			
		||||
			if containerName != "/" {
 | 
			
		||||
				return containersMap, fmt.Errorf("invalid request for docker container %q with subcontainers", containerName)
 | 
			
		||||
				return containersMap, fmt.Errorf("invalid request for %s container %q with subcontainers", options.IdType, containerName)
 | 
			
		||||
			}
 | 
			
		||||
			containersMap = m.getAllDockerContainers()
 | 
			
		||||
			containersMap = m.getAllNamespacedContainers(namespace)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return containersMap, fmt.Errorf("invalid request type %q", options.IdType)
 | 
			
		||||
| 
						 | 
				
			
			@ -1357,6 +1367,28 @@ func (m *manager) getFsInfoByDeviceName(deviceName string) (v2.FsInfo, error) {
 | 
			
		|||
	return v2.FsInfo{}, fmt.Errorf("cannot find filesystem info for device %q", deviceName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) containersInfo(containers map[string]*containerData, query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
 | 
			
		||||
	output := make(map[string]info.ContainerInfo, len(containers))
 | 
			
		||||
	for name, cont := range containers {
 | 
			
		||||
		inf, err := m.containerDataToContainerInfo(cont, query)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Ignore the error because of race condition and return best-effort result.
 | 
			
		||||
			if err == memory.ErrDataNotFound {
 | 
			
		||||
				klog.V(4).Infof("Error getting data for container %s because of race condition", name)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		output[name] = *inf
 | 
			
		||||
	}
 | 
			
		||||
	return output, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) AllPodmanContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
 | 
			
		||||
	containers := m.getAllNamespacedContainers(PodmanNamespace)
 | 
			
		||||
	return m.containersInfo(containers, query)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getVersionInfo() (*info.VersionInfo, error) {
 | 
			
		||||
 | 
			
		||||
	kernelVersion := machine.KernelVersion()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -377,6 +377,13 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc, includedMetri
 | 
			
		|||
				getValues: func(s *info.ContainerStats) metricValues {
 | 
			
		||||
					return metricValues{{value: float64(s.Memory.RSS), timestamp: s.Timestamp}}
 | 
			
		||||
				},
 | 
			
		||||
			}, {
 | 
			
		||||
				name:      "container_memory_kernel_usage",
 | 
			
		||||
				help:      "Size of kernel memory allocated in bytes.",
 | 
			
		||||
				valueType: prometheus.GaugeValue,
 | 
			
		||||
				getValues: func(s *info.ContainerStats) metricValues {
 | 
			
		||||
					return metricValues{{value: float64(s.Memory.KernelUsage), timestamp: s.Timestamp}}
 | 
			
		||||
				},
 | 
			
		||||
			}, {
 | 
			
		||||
				name:      "container_memory_mapped_file",
 | 
			
		||||
				help:      "Size of memory mapped files in bytes.",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										9
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/metrics/prometheus_fake.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										9
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/metrics/prometheus_fake.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -350,10 +350,11 @@ func (p testSubcontainersInfoProvider) GetRequestedContainersInfo(string, v2.Req
 | 
			
		|||
								Unevictable: map[uint8]uint64{0: 8900, 1: 20000},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						Cache:      14,
 | 
			
		||||
						RSS:        15,
 | 
			
		||||
						MappedFile: 16,
 | 
			
		||||
						Swap:       8192,
 | 
			
		||||
						Cache:       14,
 | 
			
		||||
						RSS:         15,
 | 
			
		||||
						MappedFile:  16,
 | 
			
		||||
						KernelUsage: 17,
 | 
			
		||||
						Swap:        8192,
 | 
			
		||||
					},
 | 
			
		||||
					Hugetlb: map[string]info.HugetlbStats{
 | 
			
		||||
						"2Mi": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/metrics/prometheus_machine.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										8
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/metrics/prometheus_machine.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -111,6 +111,14 @@ func NewPrometheusMachineCollector(i infoProvider, includedMetrics container.Met
 | 
			
		|||
					return metricValues{{value: float64(machineInfo.MemoryCapacity), timestamp: machineInfo.Timestamp}}
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name:      "machine_swap_bytes",
 | 
			
		||||
				help:      "Amount of swap memory available on the machine.",
 | 
			
		||||
				valueType: prometheus.GaugeValue,
 | 
			
		||||
				getValues: func(machineInfo *info.MachineInfo) metricValues {
 | 
			
		||||
					return metricValues{{value: float64(machineInfo.SwapCapacity), timestamp: machineInfo.Timestamp}}
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name:        "machine_dimm_count",
 | 
			
		||||
				help:        "Number of RAM DIMM (all types memory modules) value labeled by dimm type.",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ func init() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// getAvgPowerBudget retrieves configured power budget
 | 
			
		||||
// (in watts) for NVM devices. When libipmct is not available
 | 
			
		||||
// (in watts) for NVM devices. When libipmctl is not available
 | 
			
		||||
// zero is returned.
 | 
			
		||||
func getAvgPowerBudget() (uint, error) {
 | 
			
		||||
	// Get number of devices on the platform
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ func getAvgPowerBudget() (uint, error) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if count == 0 {
 | 
			
		||||
		klog.Warningf("There are no NVM devices!")
 | 
			
		||||
		klog.V(4).Infof("There are no NVM devices.")
 | 
			
		||||
		return uint(0), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ type group struct {
 | 
			
		|||
 | 
			
		||||
var (
 | 
			
		||||
	isLibpfmInitialized = false
 | 
			
		||||
	libpmfMutex         = sync.Mutex{}
 | 
			
		||||
	libpfmMutex         = sync.Mutex{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -70,8 +70,8 @@ const (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	libpmfMutex.Lock()
 | 
			
		||||
	defer libpmfMutex.Unlock()
 | 
			
		||||
	libpfmMutex.Lock()
 | 
			
		||||
	defer libpfmMutex.Unlock()
 | 
			
		||||
	pErr := C.pfm_initialize()
 | 
			
		||||
	if pErr != C.PFM_SUCCESS {
 | 
			
		||||
		klog.Errorf("unable to initialize libpfm: %d", int(pErr))
 | 
			
		||||
| 
						 | 
				
			
			@ -266,10 +266,12 @@ func readPerfEventAttr(name string, pfmGetOsEventEncoding func(string, unsafe.Po
 | 
			
		|||
func pfmGetOsEventEncoding(name string, perfEventAttrMemory unsafe.Pointer) error {
 | 
			
		||||
	event := pfmPerfEncodeArgT{}
 | 
			
		||||
	fstr := C.CString("")
 | 
			
		||||
	defer C.free(unsafe.Pointer(fstr))
 | 
			
		||||
	event.fstr = unsafe.Pointer(fstr)
 | 
			
		||||
	event.attr = perfEventAttrMemory
 | 
			
		||||
	event.size = C.ulong(unsafe.Sizeof(event))
 | 
			
		||||
	cSafeName := C.CString(name)
 | 
			
		||||
	defer C.free(unsafe.Pointer(cSafeName))
 | 
			
		||||
	pErr := C.pfm_get_os_event_encoding(cSafeName, C.PFM_PLM0|C.PFM_PLM3, C.PFM_OS_PERF_EVENT, unsafe.Pointer(&event))
 | 
			
		||||
	if pErr != C.PFM_SUCCESS {
 | 
			
		||||
		return fmt.Errorf("unable to transform event name %s to perf_event_attr: %d", name, int(pErr))
 | 
			
		||||
| 
						 | 
				
			
			@ -409,8 +411,8 @@ func (c *collector) Destroy() {
 | 
			
		|||
 | 
			
		||||
// Finalize terminates libpfm4 to free resources.
 | 
			
		||||
func Finalize() {
 | 
			
		||||
	libpmfMutex.Lock()
 | 
			
		||||
	defer libpmfMutex.Unlock()
 | 
			
		||||
	libpfmMutex.Lock()
 | 
			
		||||
	defer libpfmMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	klog.V(1).Info("Attempting to terminate libpfm4")
 | 
			
		||||
	if !isLibpfmInitialized {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,6 @@ package perf
 | 
			
		|||
import "C"
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +69,7 @@ func getPMU(pmus uncorePMUs, gotType uint32) (*pmu, error) {
 | 
			
		|||
type uncorePMUs map[string]pmu
 | 
			
		||||
 | 
			
		||||
func readUncorePMU(path string, name string, cpumaskRegexp *regexp.Regexp) (*pmu, error) {
 | 
			
		||||
	buf, err := ioutil.ReadFile(filepath.Join(path, pmuTypeFilename))
 | 
			
		||||
	buf, err := os.ReadFile(filepath.Join(path, pmuTypeFilename))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +79,7 @@ func readUncorePMU(path string, name string, cpumaskRegexp *regexp.Regexp) (*pmu
 | 
			
		|||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf, err = ioutil.ReadFile(filepath.Join(path, pmuCpumaskFilename))
 | 
			
		||||
	buf, err = os.ReadFile(filepath.Join(path, pmuCpumaskFilename))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,6 @@ import (
 | 
			
		|||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +41,8 @@ const (
 | 
			
		|||
	cpusListFileName         = "cpus_list"
 | 
			
		||||
	schemataFileName         = "schemata"
 | 
			
		||||
	tasksFileName            = "tasks"
 | 
			
		||||
	modeFileName             = "mode"
 | 
			
		||||
	sizeFileName             = "size"
 | 
			
		||||
	infoDirName              = "info"
 | 
			
		||||
	monDataDirName           = "mon_data"
 | 
			
		||||
	monGroupsDirName         = "mon_groups"
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +73,8 @@ var (
 | 
			
		|||
		monGroupsDirName: {},
 | 
			
		||||
		schemataFileName: {},
 | 
			
		||||
		tasksFileName:    {},
 | 
			
		||||
		modeFileName:     {},
 | 
			
		||||
		sizeFileName:     {},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +192,7 @@ func getPids(containerName string) ([]int, error) {
 | 
			
		|||
func getAllProcessThreads(path string) ([]int, error) {
 | 
			
		||||
	processThreads := make([]int, 0)
 | 
			
		||||
 | 
			
		||||
	threadDirs, err := ioutil.ReadDir(path)
 | 
			
		||||
	threadDirs, err := os.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return processThreads, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -216,7 +219,7 @@ func findGroup(group string, pids []string, includeGroup bool, exclusive bool) (
 | 
			
		|||
		availablePaths = append(availablePaths, group)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	files, err := ioutil.ReadDir(group)
 | 
			
		||||
	files, err := os.ReadDir(group)
 | 
			
		||||
	for _, file := range files {
 | 
			
		||||
		if _, ok := groupDirectories[file.Name()]; !ok {
 | 
			
		||||
			availablePaths = append(availablePaths, filepath.Join(group, file.Name()))
 | 
			
		||||
| 
						 | 
				
			
			@ -296,7 +299,7 @@ func readTasksFile(tasksPath string) (map[string]struct{}, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func readStatFrom(path string, vendorID string) (uint64, error) {
 | 
			
		||||
	context, err := ioutil.ReadFile(path)
 | 
			
		||||
	context, err := os.ReadFile(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ func (s *StatsSummary) AddSample(stat v1.ContainerStats) error {
 | 
			
		|||
	}
 | 
			
		||||
	if elapsed > 60*time.Second {
 | 
			
		||||
		// Make a minute sample. This works with dynamic housekeeping as long
 | 
			
		||||
		// as we keep max dynamic houskeeping period close to a minute.
 | 
			
		||||
		// as we keep max dynamic housekeeping period close to a minute.
 | 
			
		||||
		minuteSample := GetMinutePercentiles(s.secondSamples)
 | 
			
		||||
		// Clear seconds samples. Keep the latest sample for continuity.
 | 
			
		||||
		// Copying and resizing helps avoid slice re-allocation.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/utils/cloudinfo/azure/azure.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										6
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/utils/cloudinfo/azure/azure.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -15,7 +15,7 @@
 | 
			
		|||
package cloudinfo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	info "github.com/google/cadvisor/info/v1"
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ type provider struct{}
 | 
			
		|||
var _ cloudinfo.CloudProvider = provider{}
 | 
			
		||||
 | 
			
		||||
func (provider) IsActiveProvider() bool {
 | 
			
		||||
	data, err := ioutil.ReadFile(sysVendorFileName)
 | 
			
		||||
	data, err := os.ReadFile(sysVendorFileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ func (provider) GetInstanceType() info.InstanceType {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (provider) GetInstanceID() info.InstanceID {
 | 
			
		||||
	data, err := ioutil.ReadFile(biosUUIDFileName)
 | 
			
		||||
	data, err := os.ReadFile(biosUUIDFileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return info.UnNamedInstance
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/utils/cloudinfo/gce/gce.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										4
									
								
								cluster-autoscaler/vendor/github.com/google/cadvisor/utils/cloudinfo/gce/gce.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -15,7 +15,7 @@
 | 
			
		|||
package gce
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	info "github.com/google/cadvisor/info/v1"
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ type provider struct{}
 | 
			
		|||
var _ cloudinfo.CloudProvider = provider{}
 | 
			
		||||
 | 
			
		||||
func (provider) IsActiveProvider() bool {
 | 
			
		||||
	data, err := ioutil.ReadFile(gceProductName)
 | 
			
		||||
	data, err := os.ReadFile(gceProductName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.V(2).Infof("Error while reading product_name: %v", err)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,6 @@ package sysfs
 | 
			
		|||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +101,9 @@ type SysFs interface {
 | 
			
		|||
	GetBlockDeviceScheduler(string) (string, error)
 | 
			
		||||
	// Get device major:minor number string.
 | 
			
		||||
	GetBlockDeviceNumbers(string) (string, error)
 | 
			
		||||
	// Is the device "hidden" (meaning will not have a device handle)
 | 
			
		||||
	// This is the case with native nvme multipathing.
 | 
			
		||||
	IsBlockDeviceHidden(string) (bool, error)
 | 
			
		||||
 | 
			
		||||
	GetNetworkDevices() ([]os.FileInfo, error)
 | 
			
		||||
	GetNetworkAddress(string) (string, error)
 | 
			
		||||
| 
						 | 
				
			
			@ -124,10 +126,14 @@ type SysFs interface {
 | 
			
		|||
	IsCPUOnline(dir string) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type realSysFs struct{}
 | 
			
		||||
type realSysFs struct {
 | 
			
		||||
	cpuPath string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewRealSysFs() SysFs {
 | 
			
		||||
	return &realSysFs{}
 | 
			
		||||
	return &realSysFs{
 | 
			
		||||
		cpuPath: "/sys/devices/system/cpu",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetNodesPaths() ([]string, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +148,7 @@ func (fs *realSysFs) GetCPUsPaths(cpusPath string) ([]string, error) {
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetCoreID(cpuPath string) (string, error) {
 | 
			
		||||
	coreIDFilePath := fmt.Sprintf("%s%s", cpuPath, coreIDFilePath)
 | 
			
		||||
	coreID, err := ioutil.ReadFile(coreIDFilePath)
 | 
			
		||||
	coreID, err := os.ReadFile(coreIDFilePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +157,7 @@ func (fs *realSysFs) GetCoreID(cpuPath string) (string, error) {
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetCPUPhysicalPackageID(cpuPath string) (string, error) {
 | 
			
		||||
	packageIDFilePath := fmt.Sprintf("%s%s", cpuPath, packageIDFilePath)
 | 
			
		||||
	packageID, err := ioutil.ReadFile(packageIDFilePath)
 | 
			
		||||
	packageID, err := os.ReadFile(packageIDFilePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -160,7 +166,7 @@ func (fs *realSysFs) GetCPUPhysicalPackageID(cpuPath string) (string, error) {
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetMemInfo(nodePath string) (string, error) {
 | 
			
		||||
	meminfoPath := fmt.Sprintf("%s/%s", nodePath, meminfoFile)
 | 
			
		||||
	meminfo, err := ioutil.ReadFile(meminfoPath)
 | 
			
		||||
	meminfo, err := os.ReadFile(meminfoPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +175,7 @@ func (fs *realSysFs) GetMemInfo(nodePath string) (string, error) {
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetDistances(nodePath string) (string, error) {
 | 
			
		||||
	distancePath := fmt.Sprintf("%s/%s", nodePath, distanceFile)
 | 
			
		||||
	distance, err := ioutil.ReadFile(distancePath)
 | 
			
		||||
	distance, err := os.ReadFile(distancePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -177,12 +183,16 @@ func (fs *realSysFs) GetDistances(nodePath string) (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetHugePagesInfo(hugePagesDirectory string) ([]os.FileInfo, error) {
 | 
			
		||||
	return ioutil.ReadDir(hugePagesDirectory)
 | 
			
		||||
	dirs, err := os.ReadDir(hugePagesDirectory)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return toFileInfo(dirs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetHugePagesNr(hugepagesDirectory string, hugePageName string) (string, error) {
 | 
			
		||||
	hugePageFilePath := fmt.Sprintf("%s%s/%s", hugepagesDirectory, hugePageName, HugePagesNrFile)
 | 
			
		||||
	hugePageFile, err := ioutil.ReadFile(hugePageFilePath)
 | 
			
		||||
	hugePageFile, err := os.ReadFile(hugePageFilePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -190,19 +200,43 @@ func (fs *realSysFs) GetHugePagesNr(hugepagesDirectory string, hugePageName stri
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetBlockDevices() ([]os.FileInfo, error) {
 | 
			
		||||
	return ioutil.ReadDir(blockDir)
 | 
			
		||||
	dirs, err := os.ReadDir(blockDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return toFileInfo(dirs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetBlockDeviceNumbers(name string) (string, error) {
 | 
			
		||||
	dev, err := ioutil.ReadFile(path.Join(blockDir, name, "/dev"))
 | 
			
		||||
	dev, err := os.ReadFile(path.Join(blockDir, name, "/dev"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return string(dev), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) IsBlockDeviceHidden(name string) (bool, error) {
 | 
			
		||||
	// See: https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-block
 | 
			
		||||
	//      https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git
 | 
			
		||||
	//        - c8487d854ba5 ("lsblk: Ignore hidden devices")
 | 
			
		||||
	devHiddenPath := path.Join(blockDir, name, "/hidden")
 | 
			
		||||
	hidden, err := os.ReadFile(devHiddenPath)
 | 
			
		||||
	if err != nil && os.IsNotExist(err) {
 | 
			
		||||
		// older OS may not have /hidden sysfs entry, so for sure
 | 
			
		||||
		// it is not a hidden device...
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, fmt.Errorf("failed to read %s: %w", devHiddenPath, err)
 | 
			
		||||
	}
 | 
			
		||||
	if string(hidden) == "1" {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetBlockDeviceScheduler(name string) (string, error) {
 | 
			
		||||
	sched, err := ioutil.ReadFile(path.Join(blockDir, name, "/queue/scheduler"))
 | 
			
		||||
	sched, err := os.ReadFile(path.Join(blockDir, name, "/queue/scheduler"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +244,7 @@ func (fs *realSysFs) GetBlockDeviceScheduler(name string) (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetBlockDeviceSize(name string) (string, error) {
 | 
			
		||||
	size, err := ioutil.ReadFile(path.Join(blockDir, name, "/size"))
 | 
			
		||||
	size, err := os.ReadFile(path.Join(blockDir, name, "/size"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -218,13 +252,17 @@ func (fs *realSysFs) GetBlockDeviceSize(name string) (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetNetworkDevices() ([]os.FileInfo, error) {
 | 
			
		||||
	files, err := ioutil.ReadDir(netDir)
 | 
			
		||||
	dirs, err := os.ReadDir(netDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	files, err := toFileInfo(dirs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Filter out non-directory & non-symlink files
 | 
			
		||||
	var dirs []os.FileInfo
 | 
			
		||||
	filtered := []os.FileInfo{}
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		if f.Mode()|os.ModeSymlink != 0 {
 | 
			
		||||
			f, err = os.Stat(path.Join(netDir, f.Name()))
 | 
			
		||||
| 
						 | 
				
			
			@ -233,14 +271,14 @@ func (fs *realSysFs) GetNetworkDevices() ([]os.FileInfo, error) {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if f.IsDir() {
 | 
			
		||||
			dirs = append(dirs, f)
 | 
			
		||||
			filtered = append(filtered, f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return dirs, nil
 | 
			
		||||
	return filtered, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetNetworkAddress(name string) (string, error) {
 | 
			
		||||
	address, err := ioutil.ReadFile(path.Join(netDir, name, "/address"))
 | 
			
		||||
	address, err := os.ReadFile(path.Join(netDir, name, "/address"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +286,7 @@ func (fs *realSysFs) GetNetworkAddress(name string) (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetNetworkMtu(name string) (string, error) {
 | 
			
		||||
	mtu, err := ioutil.ReadFile(path.Join(netDir, name, "/mtu"))
 | 
			
		||||
	mtu, err := os.ReadFile(path.Join(netDir, name, "/mtu"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +294,7 @@ func (fs *realSysFs) GetNetworkMtu(name string) (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetNetworkSpeed(name string) (string, error) {
 | 
			
		||||
	speed, err := ioutil.ReadFile(path.Join(netDir, name, "/speed"))
 | 
			
		||||
	speed, err := os.ReadFile(path.Join(netDir, name, "/speed"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -265,7 +303,7 @@ func (fs *realSysFs) GetNetworkSpeed(name string) (string, error) {
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetNetworkStatValue(dev string, stat string) (uint64, error) {
 | 
			
		||||
	statPath := path.Join(netDir, dev, "/statistics", stat)
 | 
			
		||||
	out, err := ioutil.ReadFile(statPath)
 | 
			
		||||
	out, err := os.ReadFile(statPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, fmt.Errorf("failed to read stat from %q for device %q", statPath, dev)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -279,7 +317,23 @@ func (fs *realSysFs) GetNetworkStatValue(dev string, stat string) (uint64, error
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetCaches(id int) ([]os.FileInfo, error) {
 | 
			
		||||
	cpuPath := fmt.Sprintf("%s%d/cache", cacheDir, id)
 | 
			
		||||
	return ioutil.ReadDir(cpuPath)
 | 
			
		||||
	dir, err := os.ReadDir(cpuPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return toFileInfo(dir)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toFileInfo(dirs []os.DirEntry) ([]os.FileInfo, error) {
 | 
			
		||||
	info := []os.FileInfo{}
 | 
			
		||||
	for _, dir := range dirs {
 | 
			
		||||
		fI, err := dir.Info()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		info = append(info, fI)
 | 
			
		||||
	}
 | 
			
		||||
	return info, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func bitCount(i uint64) (count int) {
 | 
			
		||||
| 
						 | 
				
			
			@ -293,7 +347,7 @@ func bitCount(i uint64) (count int) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func getCPUCount(cache string) (count int, err error) {
 | 
			
		||||
	out, err := ioutil.ReadFile(path.Join(cache, "/shared_cpu_map"))
 | 
			
		||||
	out, err := os.ReadFile(path.Join(cache, "/shared_cpu_map"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -311,7 +365,7 @@ func getCPUCount(cache string) (count int, err error) {
 | 
			
		|||
 | 
			
		||||
func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
 | 
			
		||||
	cachePath := fmt.Sprintf("%s%d/cache/%s", cacheDir, cpu, name)
 | 
			
		||||
	out, err := ioutil.ReadFile(path.Join(cachePath, "/id"))
 | 
			
		||||
	out, err := os.ReadFile(path.Join(cachePath, "/id"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return CacheInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -321,7 +375,7 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
 | 
			
		|||
		return CacheInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err = ioutil.ReadFile(path.Join(cachePath, "/size"))
 | 
			
		||||
	out, err = os.ReadFile(path.Join(cachePath, "/size"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return CacheInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -332,7 +386,7 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
 | 
			
		|||
	}
 | 
			
		||||
	// convert to bytes
 | 
			
		||||
	size = size * 1024
 | 
			
		||||
	out, err = ioutil.ReadFile(path.Join(cachePath, "/level"))
 | 
			
		||||
	out, err = os.ReadFile(path.Join(cachePath, "/level"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return CacheInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -342,7 +396,7 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
 | 
			
		|||
		return CacheInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err = ioutil.ReadFile(path.Join(cachePath, "/type"))
 | 
			
		||||
	out, err = os.ReadFile(path.Join(cachePath, "/type"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return CacheInfo{}, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -361,13 +415,13 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) GetSystemUUID() (string, error) {
 | 
			
		||||
	if id, err := ioutil.ReadFile(path.Join(dmiDir, "id", "product_uuid")); err == nil {
 | 
			
		||||
	if id, err := os.ReadFile(path.Join(dmiDir, "id", "product_uuid")); err == nil {
 | 
			
		||||
		return strings.TrimSpace(string(id)), nil
 | 
			
		||||
	} else if id, err = ioutil.ReadFile(path.Join(ppcDevTree, "system-id")); err == nil {
 | 
			
		||||
	} else if id, err = os.ReadFile(path.Join(ppcDevTree, "system-id")); err == nil {
 | 
			
		||||
		return strings.TrimSpace(strings.TrimRight(string(id), "\000")), nil
 | 
			
		||||
	} else if id, err = ioutil.ReadFile(path.Join(ppcDevTree, "vm,uuid")); err == nil {
 | 
			
		||||
	} else if id, err = os.ReadFile(path.Join(ppcDevTree, "vm,uuid")); err == nil {
 | 
			
		||||
		return strings.TrimSpace(strings.TrimRight(string(id), "\000")), nil
 | 
			
		||||
	} else if id, err = ioutil.ReadFile(path.Join(s390xDevTree, "machine-id")); err == nil {
 | 
			
		||||
	} else if id, err = os.ReadFile(path.Join(s390xDevTree, "machine-id")); err == nil {
 | 
			
		||||
		return strings.TrimSpace(string(id)), nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return "", err
 | 
			
		||||
| 
						 | 
				
			
			@ -375,19 +429,19 @@ func (fs *realSysFs) GetSystemUUID() (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (fs *realSysFs) IsCPUOnline(cpuPath string) bool {
 | 
			
		||||
	onlinePath, err := filepath.Abs(cpuPath + "/../online")
 | 
			
		||||
	cpuOnlinePath, err := filepath.Abs(fs.cpuPath + "/online")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.V(1).Infof("Unable to get absolute path for %s", cpuPath)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Quick check to determine if file exists: if it does not then kernel CPU hotplug is disabled and all CPUs are online.
 | 
			
		||||
	_, err = os.Stat(onlinePath)
 | 
			
		||||
	_, err = os.Stat(cpuOnlinePath)
 | 
			
		||||
	if err != nil && os.IsNotExist(err) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.V(1).Infof("Unable to stat %s: %s", onlinePath, err)
 | 
			
		||||
		klog.V(1).Infof("Unable to stat %s: %s", cpuOnlinePath, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cpuID, err := getCPUID(cpuPath)
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +450,7 @@ func (fs *realSysFs) IsCPUOnline(cpuPath string) bool {
 | 
			
		|||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isOnline, err := isCPUOnline(onlinePath, cpuID)
 | 
			
		||||
	isOnline, err := isCPUOnline(cpuOnlinePath, cpuID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.V(1).Infof("Unable to get online CPUs list: %s", err)
 | 
			
		||||
		return false
 | 
			
		||||
| 
						 | 
				
			
			@ -422,7 +476,7 @@ func getCPUID(dir string) (uint16, error) {
 | 
			
		|||
// It parses CPU list (such as: 0,3-5,10) into a struct that allows to determine quickly if CPU or particular ID is online.
 | 
			
		||||
// see: https://github.com/opencontainers/runc/blob/ab27e12cebf148aa5d1ee3ad13d9fc7ae12bf0b6/libcontainer/cgroups/fs/cpuset.go#L45
 | 
			
		||||
func isCPUOnline(path string, cpuID uint16) (bool, error) {
 | 
			
		||||
	fileContent, err := ioutil.ReadFile(path)
 | 
			
		||||
	fileContent, err := os.ReadFile(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -448,10 +502,9 @@ func isCPUOnline(path string, cpuID uint16) (bool, error) {
 | 
			
		|||
			if min > max {
 | 
			
		||||
				return false, fmt.Errorf("invalid values in %s", path)
 | 
			
		||||
			}
 | 
			
		||||
			for i := min; i <= max; i++ {
 | 
			
		||||
				if uint16(i) == cpuID {
 | 
			
		||||
					return true, nil
 | 
			
		||||
				}
 | 
			
		||||
			// Return true, if the CPU under consideration is in the range of online CPUs.
 | 
			
		||||
			if cpuID >= uint16(min) && cpuID <= uint16(max) {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
		case 1:
 | 
			
		||||
			value, err := strconv.ParseUint(s, 10, 16)
 | 
			
		||||
| 
						 | 
				
			
			@ -469,21 +522,21 @@ func isCPUOnline(path string, cpuID uint16) (bool, error) {
 | 
			
		|||
 | 
			
		||||
// Looks for sysfs cpu path containing given CPU property, e.g. core_id or physical_package_id
 | 
			
		||||
// and returns number of unique values of given property, exemplary usage: getting number of CPU physical cores
 | 
			
		||||
func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int {
 | 
			
		||||
	absCPUBusPath, err := filepath.Abs(cpuBusPath)
 | 
			
		||||
func GetUniqueCPUPropertyCount(cpuAttributesPath string, propertyName string) int {
 | 
			
		||||
	absCPUAttributesPath, err := filepath.Abs(cpuAttributesPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.Errorf("Cannot make %s absolute", cpuBusPath)
 | 
			
		||||
		klog.Errorf("Cannot make %s absolute", cpuAttributesPath)
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	pathPattern := absCPUBusPath + "/cpu*[0-9]"
 | 
			
		||||
	pathPattern := absCPUAttributesPath + "/cpu*[0-9]"
 | 
			
		||||
	sysCPUPaths, err := filepath.Glob(pathPattern)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.Errorf("Cannot find files matching pattern (pathPattern: %s),  number of unique %s set to 0", pathPattern, propertyName)
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	onlinePath, err := filepath.Abs(cpuBusPath + "/online")
 | 
			
		||||
	cpuOnlinePath, err := filepath.Abs(cpuAttributesPath + "/online")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		klog.V(1).Infof("Unable to get absolute path for %s", cpuBusPath+"/../online")
 | 
			
		||||
		klog.V(1).Infof("Unable to get absolute path for %s", cpuAttributesPath+"/../online")
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -498,7 +551,7 @@ func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int {
 | 
			
		|||
			klog.V(1).Infof("Unable to get CPU ID from path %s: %s", sysCPUPath, err)
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
		isOnline, err := isCPUOnline(onlinePath, cpuID)
 | 
			
		||||
		isOnline, err := isCPUOnline(cpuOnlinePath, cpuID)
 | 
			
		||||
		if err != nil && !os.IsNotExist(err) {
 | 
			
		||||
			klog.V(1).Infof("Unable to determine CPU online state: %s", err)
 | 
			
		||||
			continue
 | 
			
		||||
| 
						 | 
				
			
			@ -507,13 +560,13 @@ func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int {
 | 
			
		|||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		propertyPath := filepath.Join(sysCPUPath, sysFsCPUTopology, propertyName)
 | 
			
		||||
		propertyVal, err := ioutil.ReadFile(propertyPath)
 | 
			
		||||
		propertyVal, err := os.ReadFile(propertyPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.Warningf("Cannot open %s, assuming 0 for %s of CPU %d", propertyPath, propertyName, cpuID)
 | 
			
		||||
			propertyVal = []byte("0")
 | 
			
		||||
		}
 | 
			
		||||
		packagePath := filepath.Join(sysCPUPath, sysFsCPUTopology, CPUPhysicalPackageID)
 | 
			
		||||
		packageVal, err := ioutil.ReadFile(packagePath)
 | 
			
		||||
		packageVal, err := os.ReadFile(packagePath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.Warningf("Cannot open %s, assuming 0 %s of CPU %d", packagePath, CPUPhysicalPackageID, cpuID)
 | 
			
		||||
			packageVal = []byte("0")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,16 @@ func GetBlockDeviceInfo(sysfs sysfs.SysFs) (map[string]info.DiskInfo, error) {
 | 
			
		|||
		if strings.HasPrefix(name, "loop") || strings.HasPrefix(name, "ram") || strings.HasPrefix(name, "sr") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// Ignore "hidden" devices (i.e. nvme path device sysfs entries).
 | 
			
		||||
		// These devices are in the form of /dev/nvme$Xc$Yn$Z and will
 | 
			
		||||
		// not have a device handle (i.e. "hidden")
 | 
			
		||||
		isHidden, err := sysfs.IsBlockDeviceHidden(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if isHidden {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		diskInfo := info.DiskInfo{
 | 
			
		||||
			Name: name,
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +114,7 @@ func GetNetworkDevices(sysfs sysfs.SysFs) ([]info.NetInfo, error) {
 | 
			
		|||
	for _, dev := range devs {
 | 
			
		||||
		name := dev.Name()
 | 
			
		||||
		// Ignore docker, loopback, and veth devices.
 | 
			
		||||
		ignoredDevices := []string{"lo", "veth", "docker"}
 | 
			
		||||
		ignoredDevices := []string{"lo", "veth", "docker", "nerdctl"}
 | 
			
		||||
		ignored := false
 | 
			
		||||
		for _, prefix := range ignoredDevices {
 | 
			
		||||
			if strings.HasPrefix(name, prefix) {
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +210,7 @@ func GetNodesInfo(sysFs sysfs.SysFs) ([]info.Node, int, error) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if len(nodesDirs) == 0 {
 | 
			
		||||
		klog.Warningf("Nodes topology is not available, providing CPU topology")
 | 
			
		||||
		klog.V(4).Info("Nodes topology is not available, providing CPU topology")
 | 
			
		||||
		return getCPUTopology(sysFs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -119,6 +119,7 @@ type Env struct {
 | 
			
		|||
	appliedFeatures map[int]bool
 | 
			
		||||
	libraries       map[string]bool
 | 
			
		||||
	validators      []ASTValidator
 | 
			
		||||
	costOptions     []checker.CostOption
 | 
			
		||||
 | 
			
		||||
	// Internal parser representation
 | 
			
		||||
	prsr     *parser.Parser
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +182,7 @@ func NewCustomEnv(opts ...EnvOption) (*Env, error) {
 | 
			
		|||
		libraries:       map[string]bool{},
 | 
			
		||||
		validators:      []ASTValidator{},
 | 
			
		||||
		progOpts:        []ProgramOption{},
 | 
			
		||||
		costOptions:     []checker.CostOption{},
 | 
			
		||||
	}).configure(opts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -356,6 +358,8 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
 | 
			
		|||
	}
 | 
			
		||||
	validatorsCopy := make([]ASTValidator, len(e.validators))
 | 
			
		||||
	copy(validatorsCopy, e.validators)
 | 
			
		||||
	costOptsCopy := make([]checker.CostOption, len(e.costOptions))
 | 
			
		||||
	copy(costOptsCopy, e.costOptions)
 | 
			
		||||
 | 
			
		||||
	ext := &Env{
 | 
			
		||||
		Container:       e.Container,
 | 
			
		||||
| 
						 | 
				
			
			@ -371,6 +375,7 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
 | 
			
		|||
		provider:        provider,
 | 
			
		||||
		chkOpts:         chkOptsCopy,
 | 
			
		||||
		prsrOpts:        prsrOptsCopy,
 | 
			
		||||
		costOptions:     costOptsCopy,
 | 
			
		||||
	}
 | 
			
		||||
	return ext.configure(opts)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -557,7 +562,10 @@ func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator, opts ...ch
 | 
			
		|||
		TypeMap:      ast.typeMap,
 | 
			
		||||
		ReferenceMap: ast.refMap,
 | 
			
		||||
	}
 | 
			
		||||
	return checker.Cost(checked, estimator, opts...)
 | 
			
		||||
	extendedOpts := make([]checker.CostOption, 0, len(e.costOptions))
 | 
			
		||||
	extendedOpts = append(extendedOpts, opts...)
 | 
			
		||||
	extendedOpts = append(extendedOpts, e.costOptions...)
 | 
			
		||||
	return checker.Cost(checked, estimator, extendedOpts...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// configure applies a series of EnvOptions to the current environment.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ import (
 | 
			
		|||
	"google.golang.org/protobuf/reflect/protoregistry"
 | 
			
		||||
	"google.golang.org/protobuf/types/dynamicpb"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/cel-go/checker"
 | 
			
		||||
	"github.com/google/cel-go/common/containers"
 | 
			
		||||
	"github.com/google/cel-go/common/functions"
 | 
			
		||||
	"github.com/google/cel-go/common/types"
 | 
			
		||||
| 
						 | 
				
			
			@ -469,6 +470,24 @@ func InterruptCheckFrequency(checkFrequency uint) ProgramOption {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CostEstimatorOptions configure type-check time options for estimating expression cost.
 | 
			
		||||
func CostEstimatorOptions(costOpts ...checker.CostOption) EnvOption {
 | 
			
		||||
	return func(e *Env) (*Env, error) {
 | 
			
		||||
		e.costOptions = append(e.costOptions, costOpts...)
 | 
			
		||||
		return e, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CostTrackerOptions configures a set of options for cost-tracking.
 | 
			
		||||
//
 | 
			
		||||
// Note, CostTrackerOptions is a no-op unless CostTracking is also enabled.
 | 
			
		||||
func CostTrackerOptions(costOpts ...interpreter.CostTrackerOption) ProgramOption {
 | 
			
		||||
	return func(p *prog) (*prog, error) {
 | 
			
		||||
		p.costOptions = append(p.costOptions, costOpts...)
 | 
			
		||||
		return p, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CostTracking enables cost tracking and registers a ActualCostEstimator that can optionally provide a runtime cost estimate for any function calls.
 | 
			
		||||
func CostTracking(costEstimator interpreter.ActualCostEstimator) ProgramOption {
 | 
			
		||||
	return func(p *prog) (*prog, error) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -106,7 +106,7 @@ func (ed *EvalDetails) State() interpreter.EvalState {
 | 
			
		|||
// ActualCost returns the tracked cost through the course of execution when `CostTracking` is enabled.
 | 
			
		||||
// Otherwise, returns nil if the cost was not enabled.
 | 
			
		||||
func (ed *EvalDetails) ActualCost() *uint64 {
 | 
			
		||||
	if ed.costTracker == nil {
 | 
			
		||||
	if ed == nil || ed.costTracker == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	cost := ed.costTracker.ActualCost()
 | 
			
		||||
| 
						 | 
				
			
			@ -130,10 +130,14 @@ type prog struct {
 | 
			
		|||
	// Interpretable configured from an Ast and aggregate decorator set based on program options.
 | 
			
		||||
	interpretable     interpreter.Interpretable
 | 
			
		||||
	callCostEstimator interpreter.ActualCostEstimator
 | 
			
		||||
	costOptions       []interpreter.CostTrackerOption
 | 
			
		||||
	costLimit         *uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *prog) clone() *prog {
 | 
			
		||||
	costOptsCopy := make([]interpreter.CostTrackerOption, len(p.costOptions))
 | 
			
		||||
	copy(costOptsCopy, p.costOptions)
 | 
			
		||||
 | 
			
		||||
	return &prog{
 | 
			
		||||
		Env:                     p.Env,
 | 
			
		||||
		evalOpts:                p.evalOpts,
 | 
			
		||||
| 
						 | 
				
			
			@ -155,9 +159,10 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
 | 
			
		|||
	// Ensure the default attribute factory is set after the adapter and provider are
 | 
			
		||||
	// configured.
 | 
			
		||||
	p := &prog{
 | 
			
		||||
		Env:        e,
 | 
			
		||||
		decorators: []interpreter.InterpretableDecorator{},
 | 
			
		||||
		dispatcher: disp,
 | 
			
		||||
		Env:         e,
 | 
			
		||||
		decorators:  []interpreter.InterpretableDecorator{},
 | 
			
		||||
		dispatcher:  disp,
 | 
			
		||||
		costOptions: []interpreter.CostTrackerOption{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Configure the program via the ProgramOption values.
 | 
			
		||||
| 
						 | 
				
			
			@ -242,6 +247,12 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
 | 
			
		|||
		factory := func(state interpreter.EvalState, costTracker *interpreter.CostTracker) (Program, error) {
 | 
			
		||||
			costTracker.Estimator = p.callCostEstimator
 | 
			
		||||
			costTracker.Limit = p.costLimit
 | 
			
		||||
			for _, costOpt := range p.costOptions {
 | 
			
		||||
				err := costOpt(costTracker)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			// Limit capacity to guarantee a reallocation when calling 'append(decs, ...)' below. This
 | 
			
		||||
			// prevents the underlying memory from being shared between factory function calls causing
 | 
			
		||||
			// undesired mutations.
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +382,11 @@ type progGen struct {
 | 
			
		|||
// the test is successful.
 | 
			
		||||
func newProgGen(factory progFactory) (Program, error) {
 | 
			
		||||
	// Test the factory to make sure that configuration errors are spotted at config
 | 
			
		||||
	_, err := factory(interpreter.NewEvalState(), &interpreter.CostTracker{})
 | 
			
		||||
	tracker, err := interpreter.NewCostTracker(nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = factory(interpreter.NewEvalState(), tracker)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -384,7 +399,10 @@ func (gen *progGen) Eval(input any) (ref.Val, *EvalDetails, error) {
 | 
			
		|||
	// new EvalState instance for each call to ensure that unique evaluations yield unique stateful
 | 
			
		||||
	// results.
 | 
			
		||||
	state := interpreter.NewEvalState()
 | 
			
		||||
	costTracker := &interpreter.CostTracker{}
 | 
			
		||||
	costTracker, err := interpreter.NewCostTracker(nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	det := &EvalDetails{state: state, costTracker: costTracker}
 | 
			
		||||
 | 
			
		||||
	// Generate a new instance of the interpretable using the factory configured during the call to
 | 
			
		||||
| 
						 | 
				
			
			@ -412,7 +430,10 @@ func (gen *progGen) ContextEval(ctx context.Context, input any) (ref.Val, *EvalD
 | 
			
		|||
	// new EvalState instance for each call to ensure that unique evaluations yield unique stateful
 | 
			
		||||
	// results.
 | 
			
		||||
	state := interpreter.NewEvalState()
 | 
			
		||||
	costTracker := &interpreter.CostTracker{}
 | 
			
		||||
	costTracker, err := interpreter.NewCostTracker(nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	det := &EvalDetails{state: state, costTracker: costTracker}
 | 
			
		||||
 | 
			
		||||
	// Generate a new instance of the interpretable using the factory configured during the call to
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -230,7 +230,7 @@ func addUint64NoOverflow(x, y uint64) uint64 {
 | 
			
		|||
// multiplyUint64NoOverflow multiplies non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64
 | 
			
		||||
// is returned.
 | 
			
		||||
func multiplyUint64NoOverflow(x, y uint64) uint64 {
 | 
			
		||||
	if x > 0 && y > 0 && x > math.MaxUint64/y {
 | 
			
		||||
	if y != 0 && x > math.MaxUint64/y {
 | 
			
		||||
		return math.MaxUint64
 | 
			
		||||
	}
 | 
			
		||||
	return x * y
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +242,11 @@ func multiplyByCostFactor(x uint64, y float64) uint64 {
 | 
			
		|||
	if xFloat > 0 && y > 0 && xFloat > math.MaxUint64/y {
 | 
			
		||||
		return math.MaxUint64
 | 
			
		||||
	}
 | 
			
		||||
	return uint64(math.Ceil(xFloat * y))
 | 
			
		||||
	ceil := math.Ceil(xFloat * y)
 | 
			
		||||
	if ceil >= doubleTwoTo64 {
 | 
			
		||||
		return math.MaxUint64
 | 
			
		||||
	}
 | 
			
		||||
	return uint64(ceil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			@ -260,9 +264,10 @@ type coster struct {
 | 
			
		|||
	// iterRanges tracks the iterRange of each iterVar.
 | 
			
		||||
	iterRanges iterRangeScopes
 | 
			
		||||
	// computedSizes tracks the computed sizes of call results.
 | 
			
		||||
	computedSizes map[int64]SizeEstimate
 | 
			
		||||
	checkedAST    *ast.CheckedAST
 | 
			
		||||
	estimator     CostEstimator
 | 
			
		||||
	computedSizes      map[int64]SizeEstimate
 | 
			
		||||
	checkedAST         *ast.CheckedAST
 | 
			
		||||
	estimator          CostEstimator
 | 
			
		||||
	overloadEstimators map[string]FunctionEstimator
 | 
			
		||||
	// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
 | 
			
		||||
	presenceTestCost CostEstimate
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -291,6 +296,7 @@ func (vs iterRangeScopes) peek(varName string) (int64, bool) {
 | 
			
		|||
type CostOption func(*coster) error
 | 
			
		||||
 | 
			
		||||
// PresenceTestHasCost determines whether presence testing has a cost of one or zero.
 | 
			
		||||
//
 | 
			
		||||
// Defaults to presence test has a cost of one.
 | 
			
		||||
func PresenceTestHasCost(hasCost bool) CostOption {
 | 
			
		||||
	return func(c *coster) error {
 | 
			
		||||
| 
						 | 
				
			
			@ -303,15 +309,30 @@ func PresenceTestHasCost(hasCost bool) CostOption {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FunctionEstimator provides a CallEstimate given the target and arguments for a specific function, overload pair.
 | 
			
		||||
type FunctionEstimator func(estimator CostEstimator, target *AstNode, args []AstNode) *CallEstimate
 | 
			
		||||
 | 
			
		||||
// OverloadCostEstimate binds a FunctionCoster to a specific function overload ID.
 | 
			
		||||
//
 | 
			
		||||
// When a OverloadCostEstimate is provided, it will override the cost calculation of the CostEstimator provided to
 | 
			
		||||
// the Cost() call.
 | 
			
		||||
func OverloadCostEstimate(overloadID string, functionCoster FunctionEstimator) CostOption {
 | 
			
		||||
	return func(c *coster) error {
 | 
			
		||||
		c.overloadEstimators[overloadID] = functionCoster
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cost estimates the cost of the parsed and type checked CEL expression.
 | 
			
		||||
func Cost(checker *ast.CheckedAST, estimator CostEstimator, opts ...CostOption) (CostEstimate, error) {
 | 
			
		||||
	c := &coster{
 | 
			
		||||
		checkedAST:       checker,
 | 
			
		||||
		estimator:        estimator,
 | 
			
		||||
		exprPath:         map[int64][]string{},
 | 
			
		||||
		iterRanges:       map[string][]int64{},
 | 
			
		||||
		computedSizes:    map[int64]SizeEstimate{},
 | 
			
		||||
		presenceTestCost: CostEstimate{Min: 1, Max: 1},
 | 
			
		||||
		checkedAST:         checker,
 | 
			
		||||
		estimator:          estimator,
 | 
			
		||||
		overloadEstimators: map[string]FunctionEstimator{},
 | 
			
		||||
		exprPath:           map[int64][]string{},
 | 
			
		||||
		iterRanges:         map[string][]int64{},
 | 
			
		||||
		computedSizes:      map[int64]SizeEstimate{},
 | 
			
		||||
		presenceTestCost:   CostEstimate{Min: 1, Max: 1},
 | 
			
		||||
	}
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
		err := opt(c)
 | 
			
		||||
| 
						 | 
				
			
			@ -532,7 +553,14 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
 | 
			
		|||
		}
 | 
			
		||||
		return sum
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.overloadEstimators) != 0 {
 | 
			
		||||
		if estimator, found := c.overloadEstimators[overloadID]; found {
 | 
			
		||||
			if est := estimator(c.estimator, target, args); est != nil {
 | 
			
		||||
				callEst := *est
 | 
			
		||||
				return CallEstimate{CostEstimate: callEst.Add(argCostSum()), ResultSize: est.ResultSize}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if est := c.estimator.EstimateCallCost(function, overloadID, target, args); est != nil {
 | 
			
		||||
		callEst := *est
 | 
			
		||||
		return CallEstimate{CostEstimate: callEst.Add(argCostSum()), ResultSize: est.ResultSize}
 | 
			
		||||
| 
						 | 
				
			
			@ -682,3 +710,7 @@ func isScalar(t *types.Type) bool {
 | 
			
		|||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	doubleTwoTo64 = math.Ldexp(1.0, 64)
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ go_library(
 | 
			
		|||
    visibility = ["//visibility:public"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//cel:go_default_library",
 | 
			
		||||
        "//checker:go_default_library",
 | 
			
		||||
        "//checker/decls:go_default_library",
 | 
			
		||||
        "//common/overloads:go_default_library",
 | 
			
		||||
        "//common/types:go_default_library",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,10 +15,14 @@
 | 
			
		|||
package ext
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/cel-go/cel"
 | 
			
		||||
	"github.com/google/cel-go/checker"
 | 
			
		||||
	"github.com/google/cel-go/common/types"
 | 
			
		||||
	"github.com/google/cel-go/common/types/ref"
 | 
			
		||||
	"github.com/google/cel-go/common/types/traits"
 | 
			
		||||
	"github.com/google/cel-go/interpreter"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Sets returns a cel.EnvOption to configure namespaced set relationship
 | 
			
		||||
| 
						 | 
				
			
			@ -95,12 +99,24 @@ func (setsLib) CompileOptions() []cel.EnvOption {
 | 
			
		|||
		cel.Function("sets.intersects",
 | 
			
		||||
			cel.Overload("list_sets_intersects_list", []*cel.Type{listType, listType}, cel.BoolType,
 | 
			
		||||
				cel.BinaryBinding(setsIntersects))),
 | 
			
		||||
		cel.CostEstimatorOptions(
 | 
			
		||||
			checker.OverloadCostEstimate("list_sets_contains_list", estimateSetsCost(1)),
 | 
			
		||||
			checker.OverloadCostEstimate("list_sets_intersects_list", estimateSetsCost(1)),
 | 
			
		||||
			// equivalence requires potentially two m*n comparisons to ensure each list is contained by the other
 | 
			
		||||
			checker.OverloadCostEstimate("list_sets_equivalent_list", estimateSetsCost(2)),
 | 
			
		||||
		),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProgramOptions implements the Library interface method.
 | 
			
		||||
func (setsLib) ProgramOptions() []cel.ProgramOption {
 | 
			
		||||
	return []cel.ProgramOption{}
 | 
			
		||||
	return []cel.ProgramOption{
 | 
			
		||||
		cel.CostTrackerOptions(
 | 
			
		||||
			interpreter.OverloadCostTracker("list_sets_contains_list", trackSetsCost(1)),
 | 
			
		||||
			interpreter.OverloadCostTracker("list_sets_intersects_list", trackSetsCost(1)),
 | 
			
		||||
			interpreter.OverloadCostTracker("list_sets_equivalent_list", trackSetsCost(2)),
 | 
			
		||||
		),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setsIntersects(listA, listB ref.Val) ref.Val {
 | 
			
		||||
| 
						 | 
				
			
			@ -136,3 +152,46 @@ func setsEquivalent(listA, listB ref.Val) ref.Val {
 | 
			
		|||
	}
 | 
			
		||||
	return setsContains(listB, listA)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func estimateSetsCost(costFactor float64) checker.FunctionEstimator {
 | 
			
		||||
	return func(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
 | 
			
		||||
		if len(args) == 2 {
 | 
			
		||||
			arg0Size := estimateSize(estimator, args[0])
 | 
			
		||||
			arg1Size := estimateSize(estimator, args[1])
 | 
			
		||||
			costEstimate := arg0Size.Multiply(arg1Size).MultiplyByCostFactor(costFactor).Add(callCostEstimate)
 | 
			
		||||
			return &checker.CallEstimate{CostEstimate: costEstimate}
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func estimateSize(estimator checker.CostEstimator, node checker.AstNode) checker.SizeEstimate {
 | 
			
		||||
	if l := node.ComputedSize(); l != nil {
 | 
			
		||||
		return *l
 | 
			
		||||
	}
 | 
			
		||||
	if l := estimator.EstimateSize(node); l != nil {
 | 
			
		||||
		return *l
 | 
			
		||||
	}
 | 
			
		||||
	return checker.SizeEstimate{Min: 0, Max: math.MaxUint64}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func trackSetsCost(costFactor float64) interpreter.FunctionTracker {
 | 
			
		||||
	return func(args []ref.Val, _ ref.Val) *uint64 {
 | 
			
		||||
		lhsSize := actualSize(args[0])
 | 
			
		||||
		rhsSize := actualSize(args[1])
 | 
			
		||||
		cost := callCost + uint64(float64(lhsSize*rhsSize)*costFactor)
 | 
			
		||||
		return &cost
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func actualSize(value ref.Val) uint64 {
 | 
			
		||||
	if sz, ok := value.(traits.Sizer); ok {
 | 
			
		||||
		return uint64(sz.Size().(types.Int))
 | 
			
		||||
	}
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	callCostEstimate = checker.CostEstimate{Min: 1, Max: 1}
 | 
			
		||||
	callCost         = uint64(1)
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,6 +133,7 @@ func PresenceTestHasCost(hasCost bool) CostTrackerOption {
 | 
			
		|||
func NewCostTracker(estimator ActualCostEstimator, opts ...CostTrackerOption) (*CostTracker, error) {
 | 
			
		||||
	tracker := &CostTracker{
 | 
			
		||||
		Estimator:           estimator,
 | 
			
		||||
		overloadTrackers:    map[string]FunctionTracker{},
 | 
			
		||||
		presenceTestHasCost: true,
 | 
			
		||||
	}
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
| 
						 | 
				
			
			@ -144,9 +145,24 @@ func NewCostTracker(estimator ActualCostEstimator, opts ...CostTrackerOption) (*
 | 
			
		|||
	return tracker, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OverloadCostTracker binds an overload ID to a runtime FunctionTracker implementation.
 | 
			
		||||
//
 | 
			
		||||
// OverloadCostTracker instances augment or override ActualCostEstimator decisions, allowing for  versioned and/or
 | 
			
		||||
// optional cost tracking changes.
 | 
			
		||||
func OverloadCostTracker(overloadID string, fnTracker FunctionTracker) CostTrackerOption {
 | 
			
		||||
	return func(tracker *CostTracker) error {
 | 
			
		||||
		tracker.overloadTrackers[overloadID] = fnTracker
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FunctionTracker computes the actual cost of evaluating the functions with the given arguments and result.
 | 
			
		||||
type FunctionTracker func(args []ref.Val, result ref.Val) *uint64
 | 
			
		||||
 | 
			
		||||
// CostTracker represents the information needed for tracking runtime cost.
 | 
			
		||||
type CostTracker struct {
 | 
			
		||||
	Estimator           ActualCostEstimator
 | 
			
		||||
	overloadTrackers    map[string]FunctionTracker
 | 
			
		||||
	Limit               *uint64
 | 
			
		||||
	presenceTestHasCost bool
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -159,10 +175,19 @@ func (c *CostTracker) ActualCost() uint64 {
 | 
			
		|||
	return c.cost
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *CostTracker) costCall(call InterpretableCall, argValues []ref.Val, result ref.Val) uint64 {
 | 
			
		||||
func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result ref.Val) uint64 {
 | 
			
		||||
	var cost uint64
 | 
			
		||||
	if len(c.overloadTrackers) != 0 {
 | 
			
		||||
		if tracker, found := c.overloadTrackers[call.OverloadID()]; found {
 | 
			
		||||
			callCost := tracker(args, result)
 | 
			
		||||
			if callCost != nil {
 | 
			
		||||
				cost += *callCost
 | 
			
		||||
				return cost
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if c.Estimator != nil {
 | 
			
		||||
		callCost := c.Estimator.CallCost(call.Function(), call.OverloadID(), argValues, result)
 | 
			
		||||
		callCost := c.Estimator.CallCost(call.Function(), call.OverloadID(), args, result)
 | 
			
		||||
		if callCost != nil {
 | 
			
		||||
			cost += *callCost
 | 
			
		||||
			return cost
 | 
			
		||||
| 
						 | 
				
			
			@ -173,11 +198,11 @@ func (c *CostTracker) costCall(call InterpretableCall, argValues []ref.Val, resu
 | 
			
		|||
	switch call.OverloadID() {
 | 
			
		||||
	// O(n) functions
 | 
			
		||||
	case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString:
 | 
			
		||||
		cost += uint64(math.Ceil(float64(c.actualSize(argValues[0])) * common.StringTraversalCostFactor))
 | 
			
		||||
		cost += uint64(math.Ceil(float64(c.actualSize(args[0])) * common.StringTraversalCostFactor))
 | 
			
		||||
	case overloads.InList:
 | 
			
		||||
		// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
 | 
			
		||||
		// We just assume all list containment checks are O(n).
 | 
			
		||||
		cost += c.actualSize(argValues[1])
 | 
			
		||||
		cost += c.actualSize(args[1])
 | 
			
		||||
	// O(min(m, n)) functions
 | 
			
		||||
	case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString,
 | 
			
		||||
		overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes,
 | 
			
		||||
| 
						 | 
				
			
			@ -185,8 +210,8 @@ func (c *CostTracker) costCall(call InterpretableCall, argValues []ref.Val, resu
 | 
			
		|||
		// When we check the equality of 2 scalar values (e.g. 2 integers, 2 floating-point numbers, 2 booleans etc.),
 | 
			
		||||
		// the CostTracker.actualSize() function by definition returns 1 for each operand, resulting in an overall cost
 | 
			
		||||
		// of 1.
 | 
			
		||||
		lhsSize := c.actualSize(argValues[0])
 | 
			
		||||
		rhsSize := c.actualSize(argValues[1])
 | 
			
		||||
		lhsSize := c.actualSize(args[0])
 | 
			
		||||
		rhsSize := c.actualSize(args[1])
 | 
			
		||||
		minSize := lhsSize
 | 
			
		||||
		if rhsSize < minSize {
 | 
			
		||||
			minSize = rhsSize
 | 
			
		||||
| 
						 | 
				
			
			@ -195,23 +220,23 @@ func (c *CostTracker) costCall(call InterpretableCall, argValues []ref.Val, resu
 | 
			
		|||
	// O(m+n) functions
 | 
			
		||||
	case overloads.AddString, overloads.AddBytes:
 | 
			
		||||
		// In the worst case scenario, we would need to reallocate a new backing store and copy both operands over.
 | 
			
		||||
		cost += uint64(math.Ceil(float64(c.actualSize(argValues[0])+c.actualSize(argValues[1])) * common.StringTraversalCostFactor))
 | 
			
		||||
		cost += uint64(math.Ceil(float64(c.actualSize(args[0])+c.actualSize(args[1])) * common.StringTraversalCostFactor))
 | 
			
		||||
	// O(nm) functions
 | 
			
		||||
	case overloads.MatchesString:
 | 
			
		||||
		// https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL
 | 
			
		||||
		// Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0
 | 
			
		||||
		// in case where string is empty but regex is still expensive.
 | 
			
		||||
		strCost := uint64(math.Ceil((1.0 + float64(c.actualSize(argValues[0]))) * common.StringTraversalCostFactor))
 | 
			
		||||
		strCost := uint64(math.Ceil((1.0 + float64(c.actualSize(args[0]))) * common.StringTraversalCostFactor))
 | 
			
		||||
		// We don't know how many expressions are in the regex, just the string length (a huge
 | 
			
		||||
		// improvement here would be to somehow get a count the number of expressions in the regex or
 | 
			
		||||
		// how many states are in the regex state machine and use that to measure regex cost).
 | 
			
		||||
		// For now, we're making a guess that each expression in a regex is typically at least 4 chars
 | 
			
		||||
		// in length.
 | 
			
		||||
		regexCost := uint64(math.Ceil(float64(c.actualSize(argValues[1])) * common.RegexStringLengthCostFactor))
 | 
			
		||||
		regexCost := uint64(math.Ceil(float64(c.actualSize(args[1])) * common.RegexStringLengthCostFactor))
 | 
			
		||||
		cost += strCost * regexCost
 | 
			
		||||
	case overloads.ContainsString:
 | 
			
		||||
		strCost := uint64(math.Ceil(float64(c.actualSize(argValues[0])) * common.StringTraversalCostFactor))
 | 
			
		||||
		substrCost := uint64(math.Ceil(float64(c.actualSize(argValues[1])) * common.StringTraversalCostFactor))
 | 
			
		||||
		strCost := uint64(math.Ceil(float64(c.actualSize(args[0])) * common.StringTraversalCostFactor))
 | 
			
		||||
		substrCost := uint64(math.Ceil(float64(c.actualSize(args[1])) * common.StringTraversalCostFactor))
 | 
			
		||||
		cost += strCost * substrCost
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ package cmpopts
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"time"
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +17,10 @@ import (
 | 
			
		|||
 | 
			
		||||
func equateAlways(_, _ interface{}) bool { return true }
 | 
			
		||||
 | 
			
		||||
// EquateEmpty returns a Comparer option that determines all maps and slices
 | 
			
		||||
// EquateEmpty returns a [cmp.Comparer] option that determines all maps and slices
 | 
			
		||||
// with a length of zero to be equal, regardless of whether they are nil.
 | 
			
		||||
//
 | 
			
		||||
// EquateEmpty can be used in conjunction with SortSlices and SortMaps.
 | 
			
		||||
// EquateEmpty can be used in conjunction with [SortSlices] and [SortMaps].
 | 
			
		||||
func EquateEmpty() cmp.Option {
 | 
			
		||||
	return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +32,7 @@ func isEmpty(x, y interface{}) bool {
 | 
			
		|||
		(vx.Len() == 0 && vy.Len() == 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EquateApprox returns a Comparer option that determines float32 or float64
 | 
			
		||||
// EquateApprox returns a [cmp.Comparer] option that determines float32 or float64
 | 
			
		||||
// values to be equal if they are within a relative fraction or absolute margin.
 | 
			
		||||
// This option is not used when either x or y is NaN or infinite.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +46,7 @@ func isEmpty(x, y interface{}) bool {
 | 
			
		|||
//
 | 
			
		||||
//	|x-y| ≤ max(fraction*min(|x|, |y|), margin)
 | 
			
		||||
//
 | 
			
		||||
// EquateApprox can be used in conjunction with EquateNaNs.
 | 
			
		||||
// EquateApprox can be used in conjunction with [EquateNaNs].
 | 
			
		||||
func EquateApprox(fraction, margin float64) cmp.Option {
 | 
			
		||||
	if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) {
 | 
			
		||||
		panic("margin or fraction must be a non-negative number")
 | 
			
		||||
| 
						 | 
				
			
			@ -73,10 +74,10 @@ func (a approximator) compareF32(x, y float32) bool {
 | 
			
		|||
	return a.compareF64(float64(x), float64(y))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EquateNaNs returns a Comparer option that determines float32 and float64
 | 
			
		||||
// EquateNaNs returns a [cmp.Comparer] option that determines float32 and float64
 | 
			
		||||
// NaN values to be equal.
 | 
			
		||||
//
 | 
			
		||||
// EquateNaNs can be used in conjunction with EquateApprox.
 | 
			
		||||
// EquateNaNs can be used in conjunction with [EquateApprox].
 | 
			
		||||
func EquateNaNs() cmp.Option {
 | 
			
		||||
	return cmp.Options{
 | 
			
		||||
		cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)),
 | 
			
		||||
| 
						 | 
				
			
			@ -91,8 +92,8 @@ func areNaNsF32s(x, y float32) bool {
 | 
			
		|||
	return areNaNsF64s(float64(x), float64(y))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EquateApproxTime returns a Comparer option that determines two non-zero
 | 
			
		||||
// time.Time values to be equal if they are within some margin of one another.
 | 
			
		||||
// EquateApproxTime returns a [cmp.Comparer] option that determines two non-zero
 | 
			
		||||
// [time.Time] values to be equal if they are within some margin of one another.
 | 
			
		||||
// If both times have a monotonic clock reading, then the monotonic time
 | 
			
		||||
// difference will be used. The margin must be non-negative.
 | 
			
		||||
func EquateApproxTime(margin time.Duration) cmp.Option {
 | 
			
		||||
| 
						 | 
				
			
			@ -131,8 +132,8 @@ type anyError struct{}
 | 
			
		|||
func (anyError) Error() string     { return "any error" }
 | 
			
		||||
func (anyError) Is(err error) bool { return err != nil }
 | 
			
		||||
 | 
			
		||||
// EquateErrors returns a Comparer option that determines errors to be equal
 | 
			
		||||
// if errors.Is reports them to match. The AnyError error can be used to
 | 
			
		||||
// EquateErrors returns a [cmp.Comparer] option that determines errors to be equal
 | 
			
		||||
// if [errors.Is] reports them to match. The [AnyError] error can be used to
 | 
			
		||||
// match any non-nil error.
 | 
			
		||||
func EquateErrors() cmp.Option {
 | 
			
		||||
	return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors))
 | 
			
		||||
| 
						 | 
				
			
			@ -154,3 +155,31 @@ func compareErrors(x, y interface{}) bool {
 | 
			
		|||
	ye := y.(error)
 | 
			
		||||
	return errors.Is(xe, ye) || errors.Is(ye, xe)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EquateComparable returns a [cmp.Option] that determines equality
 | 
			
		||||
// of comparable types by directly comparing them using the == operator in Go.
 | 
			
		||||
// The types to compare are specified by passing a value of that type.
 | 
			
		||||
// This option should only be used on types that are documented as being
 | 
			
		||||
// safe for direct == comparison. For example, [net/netip.Addr] is documented
 | 
			
		||||
// as being semantically safe to use with ==, while [time.Time] is documented
 | 
			
		||||
// to discourage the use of == on time values.
 | 
			
		||||
func EquateComparable(typs ...interface{}) cmp.Option {
 | 
			
		||||
	types := make(typesFilter)
 | 
			
		||||
	for _, typ := range typs {
 | 
			
		||||
		switch t := reflect.TypeOf(typ); {
 | 
			
		||||
		case !t.Comparable():
 | 
			
		||||
			panic(fmt.Sprintf("%T is not a comparable Go type", typ))
 | 
			
		||||
		case types[t]:
 | 
			
		||||
			panic(fmt.Sprintf("%T is already specified", typ))
 | 
			
		||||
		default:
 | 
			
		||||
			types[t] = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cmp.FilterPath(types.filter, cmp.Comparer(equateAny))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type typesFilter map[reflect.Type]bool
 | 
			
		||||
 | 
			
		||||
func (tf typesFilter) filter(p cmp.Path) bool { return tf[p.Last().Type()] }
 | 
			
		||||
 | 
			
		||||
func equateAny(x, y interface{}) bool { return x == y }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ import (
 | 
			
		|||
	"github.com/google/go-cmp/cmp/internal/function"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IgnoreFields returns an Option that ignores fields of the
 | 
			
		||||
// IgnoreFields returns an [cmp.Option] that ignores fields of the
 | 
			
		||||
// given names on a single struct type. It respects the names of exported fields
 | 
			
		||||
// that are forwarded due to struct embedding.
 | 
			
		||||
// The struct type is specified by passing in a value of that type.
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ func IgnoreFields(typ interface{}, names ...string) cmp.Option {
 | 
			
		|||
	return cmp.FilterPath(sf.filter, cmp.Ignore())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IgnoreTypes returns an Option that ignores all values assignable to
 | 
			
		||||
// IgnoreTypes returns an [cmp.Option] that ignores all values assignable to
 | 
			
		||||
// certain types, which are specified by passing in a value of each type.
 | 
			
		||||
func IgnoreTypes(typs ...interface{}) cmp.Option {
 | 
			
		||||
	tf := newTypeFilter(typs...)
 | 
			
		||||
| 
						 | 
				
			
			@ -59,10 +59,10 @@ func (tf typeFilter) filter(p cmp.Path) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IgnoreInterfaces returns an Option that ignores all values or references of
 | 
			
		||||
// IgnoreInterfaces returns an [cmp.Option] that ignores all values or references of
 | 
			
		||||
// values assignable to certain interface types. These interfaces are specified
 | 
			
		||||
// by passing in an anonymous struct with the interface types embedded in it.
 | 
			
		||||
// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}.
 | 
			
		||||
// For example, to ignore [sync.Locker], pass in struct{sync.Locker}{}.
 | 
			
		||||
func IgnoreInterfaces(ifaces interface{}) cmp.Option {
 | 
			
		||||
	tf := newIfaceFilter(ifaces)
 | 
			
		||||
	return cmp.FilterPath(tf.filter, cmp.Ignore())
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +107,7 @@ func (tf ifaceFilter) filter(p cmp.Path) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IgnoreUnexported returns an Option that only ignores the immediate unexported
 | 
			
		||||
// IgnoreUnexported returns an [cmp.Option] that only ignores the immediate unexported
 | 
			
		||||
// fields of a struct, including anonymous fields of unexported types.
 | 
			
		||||
// In particular, unexported fields within the struct's exported fields
 | 
			
		||||
// of struct types, including anonymous fields, will not be ignored unless the
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ func (tf ifaceFilter) filter(p cmp.Path) bool {
 | 
			
		|||
//
 | 
			
		||||
// Avoid ignoring unexported fields of a type which you do not control (i.e. a
 | 
			
		||||
// type from another repository), as changes to the implementation of such types
 | 
			
		||||
// may change how the comparison behaves. Prefer a custom Comparer instead.
 | 
			
		||||
// may change how the comparison behaves. Prefer a custom [cmp.Comparer] instead.
 | 
			
		||||
func IgnoreUnexported(typs ...interface{}) cmp.Option {
 | 
			
		||||
	ux := newUnexportedFilter(typs...)
 | 
			
		||||
	return cmp.FilterPath(ux.filter, cmp.Ignore())
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +148,7 @@ func isExported(id string) bool {
 | 
			
		|||
	return unicode.IsUpper(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IgnoreSliceElements returns an Option that ignores elements of []V.
 | 
			
		||||
// IgnoreSliceElements returns an [cmp.Option] that ignores elements of []V.
 | 
			
		||||
// The discard function must be of the form "func(T) bool" which is used to
 | 
			
		||||
// ignore slice elements of type V, where V is assignable to T.
 | 
			
		||||
// Elements are ignored if the function reports true.
 | 
			
		||||
| 
						 | 
				
			
			@ -176,7 +176,7 @@ func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
 | 
			
		|||
	}, cmp.Ignore())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IgnoreMapEntries returns an Option that ignores entries of map[K]V.
 | 
			
		||||
// IgnoreMapEntries returns an [cmp.Option] that ignores entries of map[K]V.
 | 
			
		||||
// The discard function must be of the form "func(T, R) bool" which is used to
 | 
			
		||||
// ignore map entries of type K and V, where K and V are assignable to T and R.
 | 
			
		||||
// Entries are ignored if the function reports true.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ import (
 | 
			
		|||
	"github.com/google/go-cmp/cmp/internal/function"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SortSlices returns a Transformer option that sorts all []V.
 | 
			
		||||
// SortSlices returns a [cmp.Transformer] option that sorts all []V.
 | 
			
		||||
// The less function must be of the form "func(T, T) bool" which is used to
 | 
			
		||||
// sort any slice with element type V that is assignable to T.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ import (
 | 
			
		|||
// The less function does not have to be "total". That is, if !less(x, y) and
 | 
			
		||||
// !less(y, x) for two elements x and y, their relative order is maintained.
 | 
			
		||||
//
 | 
			
		||||
// SortSlices can be used in conjunction with EquateEmpty.
 | 
			
		||||
// SortSlices can be used in conjunction with [EquateEmpty].
 | 
			
		||||
func SortSlices(lessFunc interface{}) cmp.Option {
 | 
			
		||||
	vf := reflect.ValueOf(lessFunc)
 | 
			
		||||
	if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,13 +82,13 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
 | 
			
		|||
	return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SortMaps returns a Transformer option that flattens map[K]V types to be a
 | 
			
		||||
// SortMaps returns a [cmp.Transformer] option that flattens map[K]V types to be a
 | 
			
		||||
// sorted []struct{K, V}. The less function must be of the form
 | 
			
		||||
// "func(T, T) bool" which is used to sort any map with key K that is
 | 
			
		||||
// assignable to T.
 | 
			
		||||
//
 | 
			
		||||
// Flattening the map into a slice has the property that cmp.Equal is able to
 | 
			
		||||
// use Comparers on K or the K.Equal method if it exists.
 | 
			
		||||
// Flattening the map into a slice has the property that [cmp.Equal] is able to
 | 
			
		||||
// use [cmp.Comparer] options on K or the K.Equal method if it exists.
 | 
			
		||||
//
 | 
			
		||||
// The less function must be:
 | 
			
		||||
//   - Deterministic: less(x, y) == less(x, y)
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +96,7 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
 | 
			
		|||
//   - Transitive: if !less(x, y) and !less(y, z), then !less(x, z)
 | 
			
		||||
//   - Total: if x != y, then either less(x, y) or less(y, x)
 | 
			
		||||
//
 | 
			
		||||
// SortMaps can be used in conjunction with EquateEmpty.
 | 
			
		||||
// SortMaps can be used in conjunction with [EquateEmpty].
 | 
			
		||||
func SortMaps(lessFunc interface{}) cmp.Option {
 | 
			
		||||
	vf := reflect.ValueOf(lessFunc)
 | 
			
		||||
	if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ func (xf xformFilter) filter(p cmp.Path) bool {
 | 
			
		|||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AcyclicTransformer returns a Transformer with a filter applied that ensures
 | 
			
		||||
// AcyclicTransformer returns a [cmp.Transformer] with a filter applied that ensures
 | 
			
		||||
// that the transformer cannot be recursively applied upon its own output.
 | 
			
		||||
//
 | 
			
		||||
// An example use case is a transformer that splits a string by lines:
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ func (xf xformFilter) filter(p cmp.Path) bool {
 | 
			
		|||
//		return strings.Split(s, "\n")
 | 
			
		||||
//	})
 | 
			
		||||
//
 | 
			
		||||
// Had this been an unfiltered Transformer instead, this would result in an
 | 
			
		||||
// Had this been an unfiltered [cmp.Transformer] instead, this would result in an
 | 
			
		||||
// infinite cycle converting a string to []string to [][]string and so on.
 | 
			
		||||
func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option {
 | 
			
		||||
	xf := xformFilter{cmp.Transformer(name, xformFunc)}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@
 | 
			
		|||
// Package cmp determines equality of values.
 | 
			
		||||
//
 | 
			
		||||
// This package is intended to be a more powerful and safer alternative to
 | 
			
		||||
// reflect.DeepEqual for comparing whether two values are semantically equal.
 | 
			
		||||
// [reflect.DeepEqual] for comparing whether two values are semantically equal.
 | 
			
		||||
// It is intended to only be used in tests, as performance is not a goal and
 | 
			
		||||
// it may panic if it cannot compare the values. Its propensity towards
 | 
			
		||||
// panicking means that its unsuitable for production environments where a
 | 
			
		||||
| 
						 | 
				
			
			@ -18,16 +18,17 @@
 | 
			
		|||
//     For example, an equality function may report floats as equal so long as
 | 
			
		||||
//     they are within some tolerance of each other.
 | 
			
		||||
//
 | 
			
		||||
//   - Types with an Equal method may use that method to determine equality.
 | 
			
		||||
//     This allows package authors to determine the equality operation
 | 
			
		||||
//     for the types that they define.
 | 
			
		||||
//   - Types with an Equal method (e.g., [time.Time.Equal]) may use that method
 | 
			
		||||
//     to determine equality. This allows package authors to determine
 | 
			
		||||
//     the equality operation for the types that they define.
 | 
			
		||||
//
 | 
			
		||||
//   - If no custom equality functions are used and no Equal method is defined,
 | 
			
		||||
//     equality is determined by recursively comparing the primitive kinds on
 | 
			
		||||
//     both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual,
 | 
			
		||||
//     both values, much like [reflect.DeepEqual]. Unlike [reflect.DeepEqual],
 | 
			
		||||
//     unexported fields are not compared by default; they result in panics
 | 
			
		||||
//     unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported)
 | 
			
		||||
//     or explicitly compared using the Exporter option.
 | 
			
		||||
//     unless suppressed by using an [Ignore] option
 | 
			
		||||
//     (see [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported])
 | 
			
		||||
//     or explicitly compared using the [Exporter] option.
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
| 
						 | 
				
			
			@ -45,14 +46,14 @@ import (
 | 
			
		|||
// Equal reports whether x and y are equal by recursively applying the
 | 
			
		||||
// following rules in the given order to x and y and all of their sub-values:
 | 
			
		||||
//
 | 
			
		||||
//   - Let S be the set of all Ignore, Transformer, and Comparer options that
 | 
			
		||||
//   - Let S be the set of all [Ignore], [Transformer], and [Comparer] options that
 | 
			
		||||
//     remain after applying all path filters, value filters, and type filters.
 | 
			
		||||
//     If at least one Ignore exists in S, then the comparison is ignored.
 | 
			
		||||
//     If the number of Transformer and Comparer options in S is non-zero,
 | 
			
		||||
//     If at least one [Ignore] exists in S, then the comparison is ignored.
 | 
			
		||||
//     If the number of [Transformer] and [Comparer] options in S is non-zero,
 | 
			
		||||
//     then Equal panics because it is ambiguous which option to use.
 | 
			
		||||
//     If S contains a single Transformer, then use that to transform
 | 
			
		||||
//     If S contains a single [Transformer], then use that to transform
 | 
			
		||||
//     the current values and recursively call Equal on the output values.
 | 
			
		||||
//     If S contains a single Comparer, then use that to compare the current values.
 | 
			
		||||
//     If S contains a single [Comparer], then use that to compare the current values.
 | 
			
		||||
//     Otherwise, evaluation proceeds to the next rule.
 | 
			
		||||
//
 | 
			
		||||
//   - If the values have an Equal method of the form "(T) Equal(T) bool" or
 | 
			
		||||
| 
						 | 
				
			
			@ -66,21 +67,22 @@ import (
 | 
			
		|||
//     Functions are only equal if they are both nil, otherwise they are unequal.
 | 
			
		||||
//
 | 
			
		||||
// Structs are equal if recursively calling Equal on all fields report equal.
 | 
			
		||||
// If a struct contains unexported fields, Equal panics unless an Ignore option
 | 
			
		||||
// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option
 | 
			
		||||
// explicitly permits comparing the unexported field.
 | 
			
		||||
// If a struct contains unexported fields, Equal panics unless an [Ignore] option
 | 
			
		||||
// (e.g., [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) ignores that field
 | 
			
		||||
// or the [Exporter] option explicitly permits comparing the unexported field.
 | 
			
		||||
//
 | 
			
		||||
// Slices are equal if they are both nil or both non-nil, where recursively
 | 
			
		||||
// calling Equal on all non-ignored slice or array elements report equal.
 | 
			
		||||
// Empty non-nil slices and nil slices are not equal; to equate empty slices,
 | 
			
		||||
// consider using cmpopts.EquateEmpty.
 | 
			
		||||
// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty].
 | 
			
		||||
//
 | 
			
		||||
// Maps are equal if they are both nil or both non-nil, where recursively
 | 
			
		||||
// calling Equal on all non-ignored map entries report equal.
 | 
			
		||||
// Map keys are equal according to the == operator.
 | 
			
		||||
// To use custom comparisons for map keys, consider using cmpopts.SortMaps.
 | 
			
		||||
// To use custom comparisons for map keys, consider using
 | 
			
		||||
// [github.com/google/go-cmp/cmp/cmpopts.SortMaps].
 | 
			
		||||
// Empty non-nil maps and nil maps are not equal; to equate empty maps,
 | 
			
		||||
// consider using cmpopts.EquateEmpty.
 | 
			
		||||
// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty].
 | 
			
		||||
//
 | 
			
		||||
// Pointers and interfaces are equal if they are both nil or both non-nil,
 | 
			
		||||
// where they have the same underlying concrete type and recursively
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,9 +2,6 @@
 | 
			
		|||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
//go:build !purego
 | 
			
		||||
// +build !purego
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
| 
						 | 
				
			
			@ -12,8 +9,6 @@ import (
 | 
			
		|||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const supportExporters = true
 | 
			
		||||
 | 
			
		||||
// retrieveUnexportedField uses unsafe to forcibly retrieve any field from
 | 
			
		||||
// a struct such that the value has read-write permissions.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +0,0 @@
 | 
			
		|||
// Copyright 2017, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
//go:build purego
 | 
			
		||||
// +build purego
 | 
			
		||||
 | 
			
		||||
package cmp
 | 
			
		||||
 | 
			
		||||
import "reflect"
 | 
			
		||||
 | 
			
		||||
const supportExporters = false
 | 
			
		||||
 | 
			
		||||
func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value {
 | 
			
		||||
	panic("no support for forcibly accessing unexported fields")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,9 +2,6 @@
 | 
			
		|||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
//go:build !purego
 | 
			
		||||
// +build !purego
 | 
			
		||||
 | 
			
		||||
package value
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
							
								
								
									
										34
									
								
								cluster-autoscaler/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										34
									
								
								cluster-autoscaler/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -1,34 +0,0 @@
 | 
			
		|||
// Copyright 2018, The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
//go:build purego
 | 
			
		||||
// +build purego
 | 
			
		||||
 | 
			
		||||
package value
 | 
			
		||||
 | 
			
		||||
import "reflect"
 | 
			
		||||
 | 
			
		||||
// Pointer is an opaque typed pointer and is guaranteed to be comparable.
 | 
			
		||||
type Pointer struct {
 | 
			
		||||
	p uintptr
 | 
			
		||||
	t reflect.Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PointerOf returns a Pointer from v, which must be a
 | 
			
		||||
// reflect.Ptr, reflect.Slice, or reflect.Map.
 | 
			
		||||
func PointerOf(v reflect.Value) Pointer {
 | 
			
		||||
	// NOTE: Storing a pointer as an uintptr is technically incorrect as it
 | 
			
		||||
	// assumes that the GC implementation does not use a moving collector.
 | 
			
		||||
	return Pointer{v.Pointer(), v.Type()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsNil reports whether the pointer is nil.
 | 
			
		||||
func (p Pointer) IsNil() bool {
 | 
			
		||||
	return p.p == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Uintptr returns the pointer as a uintptr.
 | 
			
		||||
func (p Pointer) Uintptr() uintptr {
 | 
			
		||||
	return p.p
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,15 +13,15 @@ import (
 | 
			
		|||
	"github.com/google/go-cmp/cmp/internal/function"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Option configures for specific behavior of Equal and Diff. In particular,
 | 
			
		||||
// the fundamental Option functions (Ignore, Transformer, and Comparer),
 | 
			
		||||
// Option configures for specific behavior of [Equal] and [Diff]. In particular,
 | 
			
		||||
// the fundamental Option functions ([Ignore], [Transformer], and [Comparer]),
 | 
			
		||||
// configure how equality is determined.
 | 
			
		||||
//
 | 
			
		||||
// The fundamental options may be composed with filters (FilterPath and
 | 
			
		||||
// FilterValues) to control the scope over which they are applied.
 | 
			
		||||
// The fundamental options may be composed with filters ([FilterPath] and
 | 
			
		||||
// [FilterValues]) to control the scope over which they are applied.
 | 
			
		||||
//
 | 
			
		||||
// The cmp/cmpopts package provides helper functions for creating options that
 | 
			
		||||
// may be used with Equal and Diff.
 | 
			
		||||
// The [github.com/google/go-cmp/cmp/cmpopts] package provides helper functions
 | 
			
		||||
// for creating options that may be used with [Equal] and [Diff].
 | 
			
		||||
type Option interface {
 | 
			
		||||
	// filter applies all filters and returns the option that remains.
 | 
			
		||||
	// Each option may only read s.curPath and call s.callTTBFunc.
 | 
			
		||||
| 
						 | 
				
			
			@ -56,9 +56,9 @@ type core struct{}
 | 
			
		|||
 | 
			
		||||
func (core) isCore() {}
 | 
			
		||||
 | 
			
		||||
// Options is a list of Option values that also satisfies the Option interface.
 | 
			
		||||
// Options is a list of [Option] values that also satisfies the [Option] interface.
 | 
			
		||||
// Helper comparison packages may return an Options value when packing multiple
 | 
			
		||||
// Option values into a single Option. When this package processes an Options,
 | 
			
		||||
// [Option] values into a single [Option]. When this package processes an Options,
 | 
			
		||||
// it will be implicitly expanded into a flat list.
 | 
			
		||||
//
 | 
			
		||||
// Applying a filter on an Options is equivalent to applying that same filter
 | 
			
		||||
| 
						 | 
				
			
			@ -105,16 +105,16 @@ func (opts Options) String() string {
 | 
			
		|||
	return fmt.Sprintf("Options{%s}", strings.Join(ss, ", "))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterPath returns a new Option where opt is only evaluated if filter f
 | 
			
		||||
// returns true for the current Path in the value tree.
 | 
			
		||||
// FilterPath returns a new [Option] where opt is only evaluated if filter f
 | 
			
		||||
// returns true for the current [Path] in the value tree.
 | 
			
		||||
//
 | 
			
		||||
// This filter is called even if a slice element or map entry is missing and
 | 
			
		||||
// provides an opportunity to ignore such cases. The filter function must be
 | 
			
		||||
// symmetric such that the filter result is identical regardless of whether the
 | 
			
		||||
// missing value is from x or y.
 | 
			
		||||
//
 | 
			
		||||
// The option passed in may be an Ignore, Transformer, Comparer, Options, or
 | 
			
		||||
// a previously filtered Option.
 | 
			
		||||
// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or
 | 
			
		||||
// a previously filtered [Option].
 | 
			
		||||
func FilterPath(f func(Path) bool, opt Option) Option {
 | 
			
		||||
	if f == nil {
 | 
			
		||||
		panic("invalid path filter function")
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +142,7 @@ func (f pathFilter) String() string {
 | 
			
		|||
	return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterValues returns a new Option where opt is only evaluated if filter f,
 | 
			
		||||
// FilterValues returns a new [Option] where opt is only evaluated if filter f,
 | 
			
		||||
// which is a function of the form "func(T, T) bool", returns true for the
 | 
			
		||||
// current pair of values being compared. If either value is invalid or
 | 
			
		||||
// the type of the values is not assignable to T, then this filter implicitly
 | 
			
		||||
| 
						 | 
				
			
			@ -154,8 +154,8 @@ func (f pathFilter) String() string {
 | 
			
		|||
// If T is an interface, it is possible that f is called with two values with
 | 
			
		||||
// different concrete types that both implement T.
 | 
			
		||||
//
 | 
			
		||||
// The option passed in may be an Ignore, Transformer, Comparer, Options, or
 | 
			
		||||
// a previously filtered Option.
 | 
			
		||||
// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or
 | 
			
		||||
// a previously filtered [Option].
 | 
			
		||||
func FilterValues(f interface{}, opt Option) Option {
 | 
			
		||||
	v := reflect.ValueOf(f)
 | 
			
		||||
	if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() {
 | 
			
		||||
| 
						 | 
				
			
			@ -192,9 +192,9 @@ func (f valuesFilter) String() string {
 | 
			
		|||
	return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ignore is an Option that causes all comparisons to be ignored.
 | 
			
		||||
// This value is intended to be combined with FilterPath or FilterValues.
 | 
			
		||||
// It is an error to pass an unfiltered Ignore option to Equal.
 | 
			
		||||
// Ignore is an [Option] that causes all comparisons to be ignored.
 | 
			
		||||
// This value is intended to be combined with [FilterPath] or [FilterValues].
 | 
			
		||||
// It is an error to pass an unfiltered Ignore option to [Equal].
 | 
			
		||||
func Ignore() Option { return ignore{} }
 | 
			
		||||
 | 
			
		||||
type ignore struct{ core }
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +234,8 @@ func (validator) apply(s *state, vx, vy reflect.Value) {
 | 
			
		|||
			name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType
 | 
			
		||||
			if _, ok := reflect.New(t).Interface().(error); ok {
 | 
			
		||||
				help = "consider using cmpopts.EquateErrors to compare error values"
 | 
			
		||||
			} else if t.Comparable() {
 | 
			
		||||
				help = "consider using cmpopts.EquateComparable to compare comparable Go types"
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Unnamed type with unexported fields. Derive PkgPath from field.
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +256,7 @@ const identRx = `[_\p{L}][_\p{L}\p{N}]*`
 | 
			
		|||
 | 
			
		||||
var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
 | 
			
		||||
 | 
			
		||||
// Transformer returns an Option that applies a transformation function that
 | 
			
		||||
// Transformer returns an [Option] that applies a transformation function that
 | 
			
		||||
// converts values of a certain type into that of another.
 | 
			
		||||
//
 | 
			
		||||
// The transformer f must be a function "func(T) R" that converts values of
 | 
			
		||||
| 
						 | 
				
			
			@ -265,13 +267,14 @@ var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
 | 
			
		|||
// same transform to the output of itself (e.g., in the case where the
 | 
			
		||||
// input and output types are the same), an implicit filter is added such that
 | 
			
		||||
// a transformer is applicable only if that exact transformer is not already
 | 
			
		||||
// in the tail of the Path since the last non-Transform step.
 | 
			
		||||
// in the tail of the [Path] since the last non-[Transform] step.
 | 
			
		||||
// For situations where the implicit filter is still insufficient,
 | 
			
		||||
// consider using cmpopts.AcyclicTransformer, which adds a filter
 | 
			
		||||
// to prevent the transformer from being recursively applied upon itself.
 | 
			
		||||
// consider using [github.com/google/go-cmp/cmp/cmpopts.AcyclicTransformer],
 | 
			
		||||
// which adds a filter to prevent the transformer from
 | 
			
		||||
// being recursively applied upon itself.
 | 
			
		||||
//
 | 
			
		||||
// The name is a user provided label that is used as the Transform.Name in the
 | 
			
		||||
// transformation PathStep (and eventually shown in the Diff output).
 | 
			
		||||
// The name is a user provided label that is used as the [Transform.Name] in the
 | 
			
		||||
// transformation [PathStep] (and eventually shown in the [Diff] output).
 | 
			
		||||
// The name must be a valid identifier or qualified identifier in Go syntax.
 | 
			
		||||
// If empty, an arbitrary name is used.
 | 
			
		||||
func Transformer(name string, f interface{}) Option {
 | 
			
		||||
| 
						 | 
				
			
			@ -329,7 +332,7 @@ func (tr transformer) String() string {
 | 
			
		|||
	return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Comparer returns an Option that determines whether two values are equal
 | 
			
		||||
// Comparer returns an [Option] that determines whether two values are equal
 | 
			
		||||
// to each other.
 | 
			
		||||
//
 | 
			
		||||
// The comparer f must be a function "func(T, T) bool" and is implicitly
 | 
			
		||||
| 
						 | 
				
			
			@ -377,35 +380,32 @@ func (cm comparer) String() string {
 | 
			
		|||
	return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exporter returns an Option that specifies whether Equal is allowed to
 | 
			
		||||
// Exporter returns an [Option] that specifies whether [Equal] is allowed to
 | 
			
		||||
// introspect into the unexported fields of certain struct types.
 | 
			
		||||
//
 | 
			
		||||
// Users of this option must understand that comparing on unexported fields
 | 
			
		||||
// from external packages is not safe since changes in the internal
 | 
			
		||||
// implementation of some external package may cause the result of Equal
 | 
			
		||||
// implementation of some external package may cause the result of [Equal]
 | 
			
		||||
// to unexpectedly change. However, it may be valid to use this option on types
 | 
			
		||||
// defined in an internal package where the semantic meaning of an unexported
 | 
			
		||||
// field is in the control of the user.
 | 
			
		||||
//
 | 
			
		||||
// In many cases, a custom Comparer should be used instead that defines
 | 
			
		||||
// In many cases, a custom [Comparer] should be used instead that defines
 | 
			
		||||
// equality as a function of the public API of a type rather than the underlying
 | 
			
		||||
// unexported implementation.
 | 
			
		||||
//
 | 
			
		||||
// For example, the reflect.Type documentation defines equality to be determined
 | 
			
		||||
// For example, the [reflect.Type] documentation defines equality to be determined
 | 
			
		||||
// by the == operator on the interface (essentially performing a shallow pointer
 | 
			
		||||
// comparison) and most attempts to compare *regexp.Regexp types are interested
 | 
			
		||||
// comparison) and most attempts to compare *[regexp.Regexp] types are interested
 | 
			
		||||
// in only checking that the regular expression strings are equal.
 | 
			
		||||
// Both of these are accomplished using Comparers:
 | 
			
		||||
// Both of these are accomplished using [Comparer] options:
 | 
			
		||||
//
 | 
			
		||||
//	Comparer(func(x, y reflect.Type) bool { return x == y })
 | 
			
		||||
//	Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() })
 | 
			
		||||
//
 | 
			
		||||
// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore
 | 
			
		||||
// all unexported fields on specified struct types.
 | 
			
		||||
// In other cases, the [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]
 | 
			
		||||
// option can be used to ignore all unexported fields on specified struct types.
 | 
			
		||||
func Exporter(f func(reflect.Type) bool) Option {
 | 
			
		||||
	if !supportExporters {
 | 
			
		||||
		panic("Exporter is not supported on purego builds")
 | 
			
		||||
	}
 | 
			
		||||
	return exporter(f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -415,10 +415,10 @@ func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableO
 | 
			
		|||
	panic("not implemented")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AllowUnexported returns an Options that allows Equal to forcibly introspect
 | 
			
		||||
// AllowUnexported returns an [Option] that allows [Equal] to forcibly introspect
 | 
			
		||||
// unexported fields of the specified struct types.
 | 
			
		||||
//
 | 
			
		||||
// See Exporter for the proper use of this option.
 | 
			
		||||
// See [Exporter] for the proper use of this option.
 | 
			
		||||
func AllowUnexported(types ...interface{}) Option {
 | 
			
		||||
	m := make(map[reflect.Type]bool)
 | 
			
		||||
	for _, typ := range types {
 | 
			
		||||
| 
						 | 
				
			
			@ -432,7 +432,7 @@ func AllowUnexported(types ...interface{}) Option {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Result represents the comparison result for a single node and
 | 
			
		||||
// is provided by cmp when calling Report (see Reporter).
 | 
			
		||||
// is provided by cmp when calling Report (see [Reporter]).
 | 
			
		||||
type Result struct {
 | 
			
		||||
	_     [0]func() // Make Result incomparable
 | 
			
		||||
	flags resultFlags
 | 
			
		||||
| 
						 | 
				
			
			@ -445,7 +445,7 @@ func (r Result) Equal() bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ByIgnore reports whether the node is equal because it was ignored.
 | 
			
		||||
// This never reports true if Equal reports false.
 | 
			
		||||
// This never reports true if [Result.Equal] reports false.
 | 
			
		||||
func (r Result) ByIgnore() bool {
 | 
			
		||||
	return r.flags&reportByIgnore != 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +455,7 @@ func (r Result) ByMethod() bool {
 | 
			
		|||
	return r.flags&reportByMethod != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ByFunc reports whether a Comparer function determined equality.
 | 
			
		||||
// ByFunc reports whether a [Comparer] function determined equality.
 | 
			
		||||
func (r Result) ByFunc() bool {
 | 
			
		||||
	return r.flags&reportByFunc != 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -478,7 +478,7 @@ const (
 | 
			
		|||
	reportByCycle
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Reporter is an Option that can be passed to Equal. When Equal traverses
 | 
			
		||||
// Reporter is an [Option] that can be passed to [Equal]. When [Equal] traverses
 | 
			
		||||
// the value trees, it calls PushStep as it descends into each node in the
 | 
			
		||||
// tree and PopStep as it ascend out of the node. The leaves of the tree are
 | 
			
		||||
// either compared (determined to be equal or not equal) or ignored and reported
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,9 +14,9 @@ import (
 | 
			
		|||
	"github.com/google/go-cmp/cmp/internal/value"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Path is a list of PathSteps describing the sequence of operations to get
 | 
			
		||||
// Path is a list of [PathStep] describing the sequence of operations to get
 | 
			
		||||
// from some root type to the current position in the value tree.
 | 
			
		||||
// The first Path element is always an operation-less PathStep that exists
 | 
			
		||||
// The first Path element is always an operation-less [PathStep] that exists
 | 
			
		||||
// simply to identify the initial type.
 | 
			
		||||
//
 | 
			
		||||
// When traversing structs with embedded structs, the embedded struct will
 | 
			
		||||
| 
						 | 
				
			
			@ -29,8 +29,13 @@ type Path []PathStep
 | 
			
		|||
// a value's tree structure. Users of this package never need to implement
 | 
			
		||||
// these types as values of this type will be returned by this package.
 | 
			
		||||
//
 | 
			
		||||
// Implementations of this interface are
 | 
			
		||||
// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform.
 | 
			
		||||
// Implementations of this interface:
 | 
			
		||||
//   - [StructField]
 | 
			
		||||
//   - [SliceIndex]
 | 
			
		||||
//   - [MapIndex]
 | 
			
		||||
//   - [Indirect]
 | 
			
		||||
//   - [TypeAssertion]
 | 
			
		||||
//   - [Transform]
 | 
			
		||||
type PathStep interface {
 | 
			
		||||
	String() string
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,8 +75,9 @@ func (pa *Path) pop() {
 | 
			
		|||
	*pa = (*pa)[:len(*pa)-1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Last returns the last PathStep in the Path.
 | 
			
		||||
// If the path is empty, this returns a non-nil PathStep that reports a nil Type.
 | 
			
		||||
// Last returns the last [PathStep] in the Path.
 | 
			
		||||
// If the path is empty, this returns a non-nil [PathStep]
 | 
			
		||||
// that reports a nil [PathStep.Type].
 | 
			
		||||
func (pa Path) Last() PathStep {
 | 
			
		||||
	return pa.Index(-1)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +85,8 @@ func (pa Path) Last() PathStep {
 | 
			
		|||
// Index returns the ith step in the Path and supports negative indexing.
 | 
			
		||||
// A negative index starts counting from the tail of the Path such that -1
 | 
			
		||||
// refers to the last step, -2 refers to the second-to-last step, and so on.
 | 
			
		||||
// If index is invalid, this returns a non-nil PathStep that reports a nil Type.
 | 
			
		||||
// If index is invalid, this returns a non-nil [PathStep]
 | 
			
		||||
// that reports a nil [PathStep.Type].
 | 
			
		||||
func (pa Path) Index(i int) PathStep {
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		i = len(pa) + i
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +175,8 @@ func (ps pathStep) String() string {
 | 
			
		|||
	return fmt.Sprintf("{%s}", s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StructField represents a struct field access on a field called Name.
 | 
			
		||||
// StructField is a [PathStep] that represents a struct field access
 | 
			
		||||
// on a field called [StructField.Name].
 | 
			
		||||
type StructField struct{ *structField }
 | 
			
		||||
type structField struct {
 | 
			
		||||
	pathStep
 | 
			
		||||
| 
						 | 
				
			
			@ -204,10 +212,11 @@ func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) }
 | 
			
		|||
func (sf StructField) Name() string { return sf.name }
 | 
			
		||||
 | 
			
		||||
// Index is the index of the field in the parent struct type.
 | 
			
		||||
// See reflect.Type.Field.
 | 
			
		||||
// See [reflect.Type.Field].
 | 
			
		||||
func (sf StructField) Index() int { return sf.idx }
 | 
			
		||||
 | 
			
		||||
// SliceIndex is an index operation on a slice or array at some index Key.
 | 
			
		||||
// SliceIndex is a [PathStep] that represents an index operation on
 | 
			
		||||
// a slice or array at some index [SliceIndex.Key].
 | 
			
		||||
type SliceIndex struct{ *sliceIndex }
 | 
			
		||||
type sliceIndex struct {
 | 
			
		||||
	pathStep
 | 
			
		||||
| 
						 | 
				
			
			@ -247,12 +256,12 @@ func (si SliceIndex) Key() int {
 | 
			
		|||
// all of the indexes to be shifted. If an index is -1, then that
 | 
			
		||||
// indicates that the element does not exist in the associated slice.
 | 
			
		||||
//
 | 
			
		||||
// Key is guaranteed to return -1 if and only if the indexes returned
 | 
			
		||||
// by SplitKeys are not the same. SplitKeys will never return -1 for
 | 
			
		||||
// [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes
 | 
			
		||||
// returned by SplitKeys are not the same. SplitKeys will never return -1 for
 | 
			
		||||
// both indexes.
 | 
			
		||||
func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey }
 | 
			
		||||
 | 
			
		||||
// MapIndex is an index operation on a map at some index Key.
 | 
			
		||||
// MapIndex is a [PathStep] that represents an index operation on a map at some index Key.
 | 
			
		||||
type MapIndex struct{ *mapIndex }
 | 
			
		||||
type mapIndex struct {
 | 
			
		||||
	pathStep
 | 
			
		||||
| 
						 | 
				
			
			@ -266,7 +275,7 @@ func (mi MapIndex) String() string                 { return fmt.Sprintf("[%#v]",
 | 
			
		|||
// Key is the value of the map key.
 | 
			
		||||
func (mi MapIndex) Key() reflect.Value { return mi.key }
 | 
			
		||||
 | 
			
		||||
// Indirect represents pointer indirection on the parent type.
 | 
			
		||||
// Indirect is a [PathStep] that represents pointer indirection on the parent type.
 | 
			
		||||
type Indirect struct{ *indirect }
 | 
			
		||||
type indirect struct {
 | 
			
		||||
	pathStep
 | 
			
		||||
| 
						 | 
				
			
			@ -276,7 +285,7 @@ func (in Indirect) Type() reflect.Type             { return in.typ }
 | 
			
		|||
func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy }
 | 
			
		||||
func (in Indirect) String() string                 { return "*" }
 | 
			
		||||
 | 
			
		||||
// TypeAssertion represents a type assertion on an interface.
 | 
			
		||||
// TypeAssertion is a [PathStep] that represents a type assertion on an interface.
 | 
			
		||||
type TypeAssertion struct{ *typeAssertion }
 | 
			
		||||
type typeAssertion struct {
 | 
			
		||||
	pathStep
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +295,8 @@ func (ta TypeAssertion) Type() reflect.Type             { return ta.typ }
 | 
			
		|||
func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy }
 | 
			
		||||
func (ta TypeAssertion) String() string                 { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) }
 | 
			
		||||
 | 
			
		||||
// Transform is a transformation from the parent type to the current type.
 | 
			
		||||
// Transform is a [PathStep] that represents a transformation
 | 
			
		||||
// from the parent type to the current type.
 | 
			
		||||
type Transform struct{ *transform }
 | 
			
		||||
type transform struct {
 | 
			
		||||
	pathStep
 | 
			
		||||
| 
						 | 
				
			
			@ -297,13 +307,13 @@ func (tf Transform) Type() reflect.Type             { return tf.typ }
 | 
			
		|||
func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy }
 | 
			
		||||
func (tf Transform) String() string                 { return fmt.Sprintf("%s()", tf.trans.name) }
 | 
			
		||||
 | 
			
		||||
// Name is the name of the Transformer.
 | 
			
		||||
// Name is the name of the [Transformer].
 | 
			
		||||
func (tf Transform) Name() string { return tf.trans.name }
 | 
			
		||||
 | 
			
		||||
// Func is the function pointer to the transformer function.
 | 
			
		||||
func (tf Transform) Func() reflect.Value { return tf.trans.fnc }
 | 
			
		||||
 | 
			
		||||
// Option returns the originally constructed Transformer option.
 | 
			
		||||
// Option returns the originally constructed [Transformer] option.
 | 
			
		||||
// The == operator can be used to detect the exact option used.
 | 
			
		||||
func (tf Transform) Option() Option { return tf.trans }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,7 +199,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
 | 
			
		|||
				break
 | 
			
		||||
			}
 | 
			
		||||
			sf := t.Field(i)
 | 
			
		||||
			if supportExporters && !isExported(sf.Name) {
 | 
			
		||||
			if !isExported(sf.Name) {
 | 
			
		||||
				vv = retrieveUnexportedField(v, sf, true)
 | 
			
		||||
			}
 | 
			
		||||
			s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue