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.
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ type Route struct {
|
|||
ResponseErrors map[int]ResponseError
|
||||
DefaultResponse *ResponseError
|
||||
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
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ type RouteBuilder struct {
|
|||
doc string
|
||||
notes string
|
||||
operation string
|
||||
readSample, writeSample interface{}
|
||||
readSample interface{}
|
||||
writeSamples []interface{}
|
||||
parameters []*Parameter
|
||||
errorMap map[int]ResponseError
|
||||
defaultResponse *ResponseError
|
||||
|
|
@ -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 |
|
||||
| Backend | OS | Status |
|
||||
| :-------------------- | :--------- | :------------------------------------------------------------------------ |
|
||||
| inotify | Linux | 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) |
|
||||
| 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.
|
||||
|
|
@ -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 {
|
||||
// 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
|
||||
|
||||
|
|
@ -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)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return w.watches.updatePath(name, func(existing *watch) (*watch, error) {
|
||||
if existing != nil {
|
||||
flags |= existing.flags | unix.IN_MASK_ADD
|
||||
}
|
||||
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
|
||||
|
||||
wd, err := unix.InotifyAddWatch(w.fd, name, flags)
|
||||
if wd == -1 {
|
||||
return errno
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if watchEntry == nil {
|
||||
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
||||
w.paths[wd] = name
|
||||
} else {
|
||||
watchEntry.wd = uint32(wd)
|
||||
watchEntry.flags = flags
|
||||
if existing == nil {
|
||||
return &watch{
|
||||
wd: uint32(wd),
|
||||
path: name,
|
||||
flags: flags,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return 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"
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (
|
||||
|
|
@ -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
|
||||
mu sync.Mutex // Protects access to watches, closed
|
||||
watches watchMap // Map of watches (key: i-number)
|
||||
isClosed bool // Set to true when Close() is first called
|
||||
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),
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -324,6 +391,7 @@ type input struct {
|
|||
op int
|
||||
path string
|
||||
flags uint32
|
||||
bufsize int
|
||||
reply chan error
|
||||
}
|
||||
|
||||
|
|
@ -336,11 +404,12 @@ type inode struct {
|
|||
type watch struct {
|
||||
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 [65536]byte // 64K buffer
|
||||
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
|
||||
|
|
@ -436,6 +508,8 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
|||
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.
|
||||
|
|
@ -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)
|
||||
|
|
@ -747,8 +747,8 @@ 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
|
||||
// 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
|
||||
}
|
||||
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
|
||||
}
|
||||
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...))
|
||||
|
||||
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.",
|
||||
|
|
|
|||
1
cluster-autoscaler/vendor/github.com/google/cadvisor/metrics/prometheus_fake.go
generated
vendored
1
cluster-autoscaler/vendor/github.com/google/cadvisor/metrics/prometheus_fake.go
generated
vendored
|
|
@ -353,6 +353,7 @@ func (p testSubcontainersInfoProvider) GetRequestedContainersInfo(string, v2.Req
|
|||
Cache: 14,
|
||||
RSS: 15,
|
||||
MappedFile: 16,
|
||||
KernelUsage: 17,
|
||||
Swap: 8192,
|
||||
},
|
||||
Hugetlb: map[string]info.HugetlbStats{
|
||||
|
|
|
|||
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,11 +502,10 @@ 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, 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)
|
||||
if err != nil {
|
||||
|
|
@ -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,
|
||||
|
|
@ -158,6 +162,7 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
|||
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 (
|
||||
|
|
@ -263,6 +267,7 @@ type coster struct {
|
|||
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,11 +309,26 @@ 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,
|
||||
overloadEstimators: map[string]FunctionEstimator{},
|
||||
exprPath: map[int64][]string{},
|
||||
iterRanges: map[string][]int64{},
|
||||
computedSizes: map[int64]SizeEstimate{},
|
||||
|
|
@ -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