From 0dfe4e3ccf92b83042c840b0076024fe02479dbd Mon Sep 17 00:00:00 2001 From: Eoghan Russell Date: Tue, 24 Jan 2023 10:06:43 +0000 Subject: [PATCH] Updates - Update README - Update to go1.18 - Update to k8s v1.24.3 - Reworked vfstats collector - Implemented endpoint unit tests - Add netlink support detection - Add image building to Makefile - Remove deprecated references - Add Mellanox driver to drivers DB - Refactor code to enable testing - Support for NFD SR-IOV feature label - Changes to ensure more uniform Makefile - Implemented initial unit tests - Implemented vfstats package unit tests Co-Authored-By: Eoghan1232 Co-Authored-By: eoghanlawless Co-Authored-By: Ipawlikx Co-Authored-By: nhennigan --- Dockerfile | 2 - Makefile | 42 +- README.md | 49 +- cmd/sriov-network-metrics-exporter.go | 38 +- cmd/sriov-network-metrics-exporter_test.go | 80 ++ collectors/collectors.go | 64 +- collectors/collectors_test.go | 101 +++ collectors/pod_cpu_link.go | 379 +++++---- collectors/pod_cpu_link_test.go | 303 +++++++ collectors/pod_dev_link.go | 104 ++- collectors/pod_dev_link_test.go | 39 + collectors/sriovdev.go | 319 ++++---- collectors/sriovdev_readers.go | 107 +-- collectors/sriovdev_readers_test.go | 115 +++ collectors/sriovdev_test.go | 462 +++++++++++ deployment/daemonset.yaml | 25 +- go.mod | 59 +- go.sum | 868 +++++++-------------- pkg/drvinfo/drvinfo.go | 98 +++ pkg/drvinfo/drvinfo_test.go | 103 +++ pkg/utils/test/target | 1 + pkg/utils/utils.go | 65 +- pkg/utils/utils_test.go | 98 +++ pkg/vfstats/netlink.go | 8 +- pkg/vfstats/netlink_test.go | 41 + 25 files changed, 2495 insertions(+), 1075 deletions(-) create mode 100644 cmd/sriov-network-metrics-exporter_test.go create mode 100644 collectors/collectors_test.go create mode 100644 collectors/pod_cpu_link_test.go create mode 100644 collectors/pod_dev_link_test.go create mode 100644 collectors/sriovdev_readers_test.go create mode 100644 collectors/sriovdev_test.go create mode 100644 pkg/drvinfo/drvinfo.go create mode 100644 pkg/drvinfo/drvinfo_test.go create mode 100644 pkg/utils/test/target create mode 100644 pkg/utils/utils_test.go create mode 100644 pkg/vfstats/netlink_test.go diff --git a/Dockerfile b/Dockerfile index f3f4adc..ffda2ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ FROM golang:alpine as builder -ENV HTTP_PROXY $http_proxy -ENV HTTPS_PROXY $https_proxy RUN apk add --no-cache --virtual build-dependencies build-base linux-headers git COPY ./ /usr/src/sriov-network-metrics-exporter WORKDIR /usr/src/sriov-network-metrics-exporter diff --git a/Makefile b/Makefile index aafa70c..45d08eb 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,44 @@ +IMAGE_REGISTRY?=localhost:5000/ +IMAGE_VERSION?=latest + +IMAGE_NAME?=$(IMAGE_REGISTRY)sriov-metrics-exporter:$(IMAGE_VERSION) + +DOCKERARGS?= +ifdef HTTP_PROXY + DOCKERARGS += --build-arg http_proxy=$(HTTP_PROXY) +endif +ifdef HTTPS_PROXY + DOCKERARGS += --build-arg https_proxy=$(HTTPS_PROXY) +endif + +all: build docker-build test + clean: rm -rf bin - go clean --modcache - + go clean -modcache -testcache + build: + GO111MODULE=on go build -ldflags "-s -w" -buildmode=pie -o bin/sriov-exporter cmd/sriov-network-metrics-exporter.go + +docker-build: + @echo "Bulding Docker image $(IMAGE_NAME)" + docker build -f Dockerfile -t $(IMAGE_NAME) $(DOCKERARGS) . + +docker-push: + docker push $(IMAGE_NAME) + +test: + go test ./... -coverprofile cover.out + +test-coverage: + ginkgo -v -r -cover -coverprofile=cover.out --output-dir=. + go tool cover -html=cover.out + +go-lint: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49 go mod tidy go fmt ./... - golangci-lint run - GO111MODULE=on go build -ldflags "-s -w" -buildmode=pie -o bin/sriov-exporter cmd/sriov-network-metrics-exporter.go + golangci-lint run --color always -v ./... + +go-lint-report: + golangci-lint run --color always -v ./... &> golangci-lint.txt diff --git a/README.md b/README.md index 25b0917..70de7c2 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,14 @@ The SR-IOV Network Metrics Exporter is designed with the Kubernetes SR-IOV stack **This software is a pre-production alpha version and should not be deployed to production servers.** ## Hardware support -The default netlink implementation for Virtual Function telemetry relies on driver support and a kernel version of 4.4 or higher. This version requires i40e driver of 2.11+ for Intel® 700 series NICs. Updated i40e drivers can be fould at the [Intel Download Center](https://downloadcenter.intel.com/download/24411/Intel-Network-Adapter-Driver-for-PCIe-40-Gigabit-Ethernet-Network-Connections-under-Linux-?v=t) +The sysfs collector for Virtual Function telemetry supports NICs with drivers that implement the SR-IOV sysfs management interface e.g. i40e, mlnx_en and mlnx_ofed. -For kernels older than 4.4 a driver specific collector is enabled which is compatible with Intel® 700 series NICs using and i40e driver of 2.11 or above. To check your current driver version run: ``modinfo i40e | grep ^version`` -To upgrade visit the [official driver download site](https://downloadcenter.intel.com/download/24411/Intel-Network-Adapter-Driver-for-PCIe-40-Gigabit-Ethernet-Network-Connections-Under-Linux-). -To use this version the flag collector.netlink must be set to "false". +The netlink collector relies on driver support and a kernel version of 4.4 or higher. +To support netlink, we recommend these driver versions: i40e driver of 2.11+ for Intel® 700 series NICs and ice driver 1.2+ for Intel® 800 series NICs. + +To check your current driver version run: `modinfo | grep ^version` where driver is `i40e` or `ice`\ +i40e drivers: [Intel Download Center](https://downloadcenter.intel.com/download/18026/), [Source Forge](https://sourceforge.net/projects/e1000/files/i40e%20stable/)\ +ice drivers: [Intel Download Center](https://www.intel.com/content/www/us/en/download/19630/), [Sorce Forge](https://sourceforge.net/projects/e1000/files/ice%20stable/) ## Metrics This exporter will make the following metrics available: @@ -42,6 +45,8 @@ Once available through Prometheus VF metrics can be used by metrics applications ## Installation ### Kubernetes installation + +#### Building images Typical deployment is as a daemonset in a cluster. A daemonset requires the image to be available on each node in the cluster or at a registry accessible from each node. The following assumes a local Docker registry available at localhost:5000, and assumes Docker is being used to build and manage containers in the cluster. @@ -49,10 +54,26 @@ In order to build the container and load it to a local registry run: ``` docker build . -t localhost:5000/sriov-metrics-exporter && docker push localhost:5000/sriov-metrics-exporter + +or + +make docker-build && make docker-push ``` The above assumes a registry available across the cluster at localhost:5000, for example on using the [Docker Registry Proxy](https://github.com/kubernetes-sigs/kubespray/blob/master/roles/kubernetes-apps/registry/README.md). If your registry is at a different address the image name will need to be changed to reflect that in the [Kubernetes daemonset](/deployment/daemonset.yaml) +#### Labeling nodes + +SR-IOV Network Metrics Exporter will only be deployed on nodes labeled with `"feature.node.kubernetes.io/network-sriov.capable": "true"` label. You can label the nodes automatically using [Node Feature Discovery](https://github.com/kubernetes-sigs/node-feature-discovery), or manually, executing the following `kubectl` command: + +``` +kubectl label node feature.node.kubernetes.io/network-sriov.capable="true" +``` + +If you prefer to use the `Node Feature Discovery` you can refer to the [Quick-start guide](https://github.com/kubernetes-sigs/node-feature-discovery#quick-start--the-short-short-version) on the project's repository. + +#### Deploying SR-IOV Network Metrics Exporter + Create monitoring namespace: ``` kubectl create namespace monitoring @@ -98,7 +119,7 @@ In order to expose these metrics to Prometheus we need to configure the database ``` The above should be added to the Prometheus configuration as a new target. For more about configuring Prometheus see the [official guide.](https://prometheus.io/docs/prometheus/latest/configuration/configuration/) Once Prometheus is started with this included in its config sriov-metrics should appear on the "Targets page". Metrics should be available by querying the Prometheus API or in the web interface. -In this mode it will serve stats on an endpoint inside the cluster. Prometheus will detect the label on the service endpoint throught the above configuration. +In this mode it will serve stats on an endpoint inside the cluster. Prometheus will detect the label on the service endpoint through the above configuration. ### Standalone installation to an endpoint on the host. @@ -145,21 +166,25 @@ The above should be added to the Prometheus configuration as a new target. For m ### Configuration A number of configuration flags can be passed to the SR-IOV Network Metrics Exporter in order to change enabled collectors, the paths it reads from and some properties of its web endpoint. +The collector.vfstatspriority flag defines the priority of vf stats collectors, each pf will use the first supported collector in the list.\ +Example: using the priority, "sysfs,netlink", with Intel® 700 and 800 series NICs installed and vfs initialized, the sysfs collector will be used for the 700 series NIC, and netlink for the 800 series NIC since it doesn't support sysfs collection, therefore it falls back to the netlink driver. + | Flag | Type | Description | Default Value | |----|:----|:----|:----| | collector.kubepodcpu | boolean | Enables the kubepodcpu collector | false | | collector.kubepoddevice | boolean | Enables the kubepoddevice collector | false | -| collector.vfstats | boolean |Enables the vfstats collector | true | -| collector.netlink | boolean |Enables using netlink for vfstats collection | true | +| collector.vfstatspriority | string | Sets the priority of vfstats collectors | sysfs,netlink | +| collector.sysfs | boolean | Enables using sr-iov sysfs for vfstats collection | true | +| collector.netlink | boolean | Enables using netlink for vfstats collection | true | | path.cpucheckpoint | string | Path for location of cpu manager checkpoint file | /var/lib/kubelet/cpu_manager_state | -| path.kubecgroup |string | Path for location of kubernetes cgroups on the host system | /sys/fs/cgroup/cpuset/kubepods/| -| path.kubeletSocket | string | Path to kubelet resources socket | /var/lib/kubelet/pod-resources/kubelet.sock | +| path.kubecgroup |string | Path for location of kubernetes cgroups on the host system | /sys/fs/cgroup/cpuset/kubepods/ | +| path.kubeletsocket | string | Path to kubelet resources socket | /var/lib/kubelet/pod-resources/kubelet.sock | | path.nodecpuinfo | string | Path for location of system cpu information | /sys/devices/system/node/ | | path.sysbuspci | string | Path to sys/bus/pci on host | /sys/bus/pci/devices | | path.sysclassnet | string | Path to sys/class/net on host | /sys/class/net/ | -| web.listen-address | string | Address to listen on for web interface and telemetry. | :9808 | -| web.rate-burst | int | Maximum per second burst rate for requests. | 10 | -| web.rate-limit | int | Limit for requests per second. | 1 | +| web.listen-address | string | Address to listen on for web interface and telemetry | :9808 | +| web.rate-burst | int | Maximum per second burst rate for requests | 10 | +| web.rate-limit | int | Limit for requests per second | 1 | ## Communication and contribution diff --git a/cmd/sriov-network-metrics-exporter.go b/cmd/sriov-network-metrics-exporter.go index 4135c4f..4dec030 100644 --- a/cmd/sriov-network-metrics-exporter.go +++ b/cmd/sriov-network-metrics-exporter.go @@ -1,36 +1,37 @@ // The SR-IOV networks exporter makes metrics from SR-IOV Virtual Functions available in a prometheus format. // Different classes of metrics are implemented as individual collectors. + package main import ( "flag" "log" "net/http" - "sriov-network-metrics-exporter/collectors" - "github.com/prometheus/client_golang/prometheus/promhttp" + "sriov-network-metrics-exporter/collectors" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/time/rate" ) var ( - addr = flag.String("web.listen-address", ":9808", "Address to listen on for web interface and telemetry.") + addr = flag.String("web.listen-address", ":9808", "Port to listen on for web interface and telemetry.") rateLimit = flag.Int("web.rate-limit", 1, "Limit for requests per second.") rateBurst = flag.Int("web.rate-burst", 10, "Maximum per second burst rate for requests.") metricsEndpoint = "/metrics" ) func main() { - flag.Parse() - verifyFlags() - enabledCollectors := collectors.Enabled() - err := prometheus.Register(enabledCollectors) + parseAndVerifyFlags() + + err := prometheus.Register(collectors.Enabled()) if err != nil { log.Fatalf("collector could not be registered: %v", err) return } - //Use the default promhttp handler wrapped with middleware to serve at the metrics endpoint + + // Use the default promhttp handler wrapped with middleware to serve at the metrics endpoint handlerWithMiddleware := limitRequests( getOnly( endpointOnly( @@ -41,7 +42,12 @@ func main() { log.Fatalf("ListenAndServe error: %v", http.ListenAndServe(*addr, handlerWithMiddleware)) } -//enpointOnly restricts all responses to 404 where the passed endpoint isn't used. Used to minimize the possible outputs of the server. +func parseAndVerifyFlags() { + flag.Parse() + verifyFlags() +} + +// endpointOnly restricts all responses to 404 where the passed endpoint isn't used. Used to minimize the possible outputs of the server. func endpointOnly(next http.Handler, endpoint string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != endpoint { @@ -56,7 +62,7 @@ func endpointOnly(next http.Handler, endpoint string) http.Handler { }) } -//getOnly restricts the possible verbs used in a http request to GET only +// getOnly restricts the possible verbs used in a http request to GET only func getOnly(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { @@ -71,7 +77,7 @@ func getOnly(next http.Handler) http.Handler { }) } -//noBody returns a 400 to any request that contains a body +// noBody returns a 400 to any request that contains a body func noBody(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Body != http.NoBody { @@ -86,12 +92,12 @@ func noBody(next http.Handler) http.Handler { }) } -//limitRequests sets a rate limit and a burst limit for requests to the endpoint +// limitRequests sets a rate limit and a burst limit for requests to the endpoint func limitRequests(next http.Handler, rateLimit rate.Limit, burstLimit int) http.Handler { limiter := rate.NewLimiter(rateLimit, burstLimit) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !limiter.Allow() { - http.Error(w, http.StatusText(429), http.StatusTooManyRequests) + http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) return } next.ServeHTTP(w, r) @@ -99,7 +105,7 @@ func limitRequests(next http.Handler, rateLimit rate.Limit, burstLimit int) http } func verifyFlags() { - collectors.ResolveSriovDevFilepaths() - collectors.ResolveKubePodCPUFilepaths() - collectors.ResolveKubePodDeviceFilepaths() + if err := collectors.ResolveFilepaths(); err != nil { + log.Panicf("failed to resolve paths\n%v", err) + } } diff --git a/cmd/sriov-network-metrics-exporter_test.go b/cmd/sriov-network-metrics-exporter_test.go new file mode 100644 index 0000000..efd97fd --- /dev/null +++ b/cmd/sriov-network-metrics-exporter_test.go @@ -0,0 +1,80 @@ +package main + +import ( + "bytes" + "io" + "net/http" + "net/http/httptest" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/prometheus/client_golang/prometheus/promhttp" + "golang.org/x/time/rate" +) + +func TestMain(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "main test suite") +} + +var _ = DescribeTable("test endpointOnly handler", // endpointOnly + func(endpoint string, expectedResponse int) { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, endpoint, nil) + handler := endpointOnly(promhttp.Handler(), metricsEndpoint) + + handler.ServeHTTP(recorder, request) + + Expect(recorder.Code).To(Equal(expectedResponse)) + }, + Entry("returns status 'OK' when request endpoint is '/metrics'", "/metrics", http.StatusOK), + Entry("returns status 'Not Found' when request endpoint is not '/metrics'", "/invalidendpoint", http.StatusNotFound), +) + +var _ = DescribeTable("test getOnly handler", // getOnly + func(method string, expectedResponse int) { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(method, metricsEndpoint, nil) + handler := getOnly(promhttp.Handler()) + + handler.ServeHTTP(recorder, request) + + Expect(recorder.Code).To(Equal(expectedResponse)) + }, + Entry("returns status 'OK' when request method is 'GET'", http.MethodGet, http.StatusOK), + Entry("returns status 'MethodNotAllowed' when request method is not 'GET'", http.MethodPost, http.StatusMethodNotAllowed), +) + +var _ = DescribeTable("test noBody handler", // noBody + func(body io.Reader, expectedResponse int) { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, metricsEndpoint, body) + handler := noBody(promhttp.Handler()) + + handler.ServeHTTP(recorder, request) + + Expect(recorder.Code).To(Equal(expectedResponse)) + }, + Entry("returns status 'OK' when request body is empty", nil, http.StatusOK), + Entry("returns status 'Bad Request' when request body is not empty", bytes.NewReader([]byte("body")), http.StatusBadRequest), +) + +var _ = DescribeTable("test limitRequests handler", // limitRequests + func(limit int, requests int, expectedResponse int) { + handler := limitRequests(promhttp.Handler(), rate.Limit(limit), limit) + + code := http.StatusOK + for i := 0; i < requests; i++ { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, metricsEndpoint, nil) + handler.ServeHTTP(recorder, request) + + code = recorder.Code + } + + Expect(code).To(Equal(expectedResponse)) + }, + Entry("returns status 'OK' when the number of requests does not exceed the request limit", 10, 10, http.StatusOK), + Entry("returns status 'Too Many Requests' when number of requests exceeds the request limit", 10, 11, http.StatusTooManyRequests), +) diff --git a/collectors/collectors.go b/collectors/collectors.go index 1896605..762b9f7 100644 --- a/collectors/collectors.go +++ b/collectors/collectors.go @@ -1,5 +1,5 @@ -//Package Collectors defines the structure of the collector aggregator and contains the individual collectors used to gather metrics -//Each collector should be created in its own file with any required command line flags, its collection behavior and its registration method defined. +// Package Collectors defines the structure of the collector aggregator and contains the individual collectors used to gather metrics +// Each collector should be created in its own file with any required command line flags, its collection behavior and its registration method defined. package collectors @@ -7,7 +7,6 @@ import ( "flag" "fmt" "log" - "os" "github.com/prometheus/client_golang/prometheus" ) @@ -16,54 +15,63 @@ var ( collectorNamespace = "sriov" enabled = true disabled = false - enabledCollectors = make(map[string]*bool) - allCollectors = make(map[string]func() prometheus.Collector) + collectorState = make(map[string]*bool) + collectorFunctions = make(map[string]func() prometheus.Collector) ) -//SriovCollector registers the collectors used for specific data and exposes a Collect method to gather the data +// SriovCollector registers the collectors used for specific data and exposes a Collect method to gather the data type SriovCollector []prometheus.Collector -//Collect metrics from all enabled collectors in unordered sequence. +// Register defines a flag for a collector and adds it to the registry of enabled collectors if the flag is set to true - either through the default option or the flag passed on start +// Run by each individual collector in its init function. +func register(name string, enabled bool, collector func() prometheus.Collector) { + collectorState[name] = &enabled + collectorFunctions[name] = collector + flag.BoolVar(collectorState[name], "collector."+name, enabled, fmt.Sprintf("Enables the %v collector", name)) +} + +// Collect metrics from all enabled collectors in unordered sequence. func (s SriovCollector) Collect(ch chan<- prometheus.Metric) { for _, collector := range s { collector.Collect(ch) } } -//Describe each collector in unordered sequence +// Describe each collector in unordered sequence func (s SriovCollector) Describe(ch chan<- *prometheus.Desc) { for _, collector := range s { collector.Describe(ch) } } -//register defines a flag for a collector and adds it to the registry of enabled collectors if the flag is set to true - either through the default option or the flag passed on start -//Run by each individual collector in its init function. -func register(name string, isDefault bool, collector func() prometheus.Collector) { - enabledCollectors[name] = &isDefault - allCollectors[name] = collector - flag.BoolVar(enabledCollectors[name], "collector."+name, isDefault, fmt.Sprintf("Enables the %v collector", name)) -} - -//Enabled adds collectors enabled by default or command line flag to an SriovCollector object +// Enabled adds collectors enabled by default or command line flag to an SriovCollector object func Enabled() SriovCollector { collectors := make([]prometheus.Collector, 0) - for k, v := range enabledCollectors { - if v != nil && *v { - log.Printf("The %v collector is enabled", k) - collectors = append(collectors, allCollectors[k]()) + for collector, enabled := range collectorState { + if enabled != nil && *enabled { + log.Printf("The %v collector is enabled", collector) + collectors = append(collectors, collectorFunctions[collector]()) } } return collectors } -func isSymLink(filename string) bool { - info, err := os.Lstat(filename) - if err != nil { - return false +func ResolveFilepaths() error { + resolveFuncs := []func() error{ + resolveSriovDevFilepaths, + resolveKubePodCPUFilepaths, + resolveKubePodDeviceFilepaths, } - if info.Mode()&os.ModeSymlink == os.ModeSymlink { - return true + + for _, resolveFunc := range resolveFuncs { + if err := resolveFunc(); err != nil { + return err + } } - return false + + return nil +} + +var logFatal = func(msg string, args ...any) { + log.Fatalf(msg, args...) } diff --git a/collectors/collectors_test.go b/collectors/collectors_test.go new file mode 100644 index 0000000..8ed59b6 --- /dev/null +++ b/collectors/collectors_test.go @@ -0,0 +1,101 @@ +package collectors + +import ( + "fmt" + "io/fs" + "log" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/prometheus/client_golang/prometheus" + + "sriov-network-metrics-exporter/pkg/utils" +) + +var buffer gbytes.Buffer + +func TestCollectors(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "collectors test suite") +} + +var _ = BeforeSuite(func() { + utils.EvalSymlinks = evalSymlinks + + logFatal = func(msg string, args ...any) { + log.Printf(msg, args...) + } + + log.SetFlags(0) +}) + +var _ = BeforeEach(func() { + buffer = *gbytes.NewBuffer() + log.SetOutput(&buffer) +}) + +type metric struct { + labels map[string]string + counter float64 +} + +type testCollector struct { + name string +} + +func createTestCollector() prometheus.Collector { + return testCollector{ + name: "collector.test", + } +} + +func (c testCollector) Collect(ch chan<- prometheus.Metric) {} +func (c testCollector) Describe(chan<- *prometheus.Desc) {} + +var _ = DescribeTable("test registering collector", // register + func(name string, enabled bool, collector func() prometheus.Collector) { + register(name, enabled, collector) + + Expect(collectorState).To(HaveKey(name)) + Expect(collectorState[name]).To(Equal(&enabled)) + + Expect(collectorFunctions).To(HaveKey(name)) + // Expect(allCollectors[name]).To(Equal(collector)) // TODO: verify expected collector is returned + + }, + Entry("the correct collector is enabled when default is true", + "test_true", + true, + createTestCollector), + Entry("the correct collector is not enabled when default is false", + "test_false", + false, + createTestCollector), +) + +// TODO: create Enabled unit test + +func assertLogs(logs []string) { + for _, log := range logs { + Eventually(&buffer).WithTimeout(time.Duration(2 * time.Second)).Should(gbytes.Say(log)) + } +} + +// Replaces filepath.EvalSymlinks with an emulated evaluation to work with the in-memory fs. +var evalSymlinks = func(path string) (string, error) { + path = filepath.Join(filepath.Base(filepath.Dir(path)), filepath.Base(path)) + + if stat, err := fs.Stat(devfs, path); err == nil && stat.Mode() == fs.ModeSymlink { + if target, err := fs.ReadFile(devfs, path); err == nil { + return string(target), nil + } else { + return "", fmt.Errorf("error") + } + } else { + return "", fmt.Errorf("error") + } +} diff --git a/collectors/pod_cpu_link.go b/collectors/pod_cpu_link.go index 68cc7ae..038f198 100644 --- a/collectors/pod_cpu_link.go +++ b/collectors/pod_cpu_link.go @@ -1,13 +1,14 @@ package collectors -//kubepodCPUCollector is a Kubernetes focused collector that exposes information about CPUs linked to specific Kubernetes pods through the CPU Manager component in Kubelet +// kubepodCPUCollector is a Kubernetes focused collector that exposes information about CPUs linked to specific Kubernetes pods through the CPU Manager component in Kubelet import ( "encoding/json" "flag" "fmt" - "io/ioutil" + "io/fs" "log" + "os" "path/filepath" "regexp" "sriov-network-metrics-exporter/pkg/utils" @@ -22,142 +23,234 @@ var ( kubePodCgroupPath = flag.String("path.kubecgroup", "/sys/fs/cgroup/cpuset/kubepods.slice/", "Path for location of kubernetes cgroups on the host system") sysDevSysNodePath = flag.String("path.nodecpuinfo", "/sys/devices/system/node/", "Path for location of system cpu information") cpuCheckPointFile = flag.String("path.cpucheckpoint", "/var/lib/kubelet/cpu_manager_state", "Path for location of cpu manager checkpoint file") + + kubecgroupfs fs.FS + cpuinfofs fs.FS + cpucheckpointfs fs.FS ) -//init runs the registration for this collector on package import -func init() { - register(kubepodcpu, disabled, createKubepodCPUCollector) +// kubepodCPUCollector holds a static representation of node cpu topology and uses it to update information about kubernetes pod cpu usage. +type kubepodCPUCollector struct { + cpuInfo map[string]string + name string +} + +// podCPULink contains the information about the pod and container a single cpu is attached to +type podCPULink struct { + podID string + containerID string + cpu string } -//cpuManagerCheckpoint is the structure needed to extract the default cpuSet information from the kubelet checkpoint file +// cpuManagerCheckpoint is the structure needed to extract the default cpuSet information from the kubelet checkpoint file type cpuManagerCheckpoint struct { DefaultCPUSet string "json:\"defaultCpuSet\"" } -//readDefaultSet extracts the information about the "default" set of cpus available to kubernetes -func readDefaultSet() string { - cpuRaw, err := ioutil.ReadFile(*cpuCheckPointFile) - if err != nil { - log.Printf("cpu checkpoint file can not be read: %v", err) - return "" +// init runs the registration for this collector on package import +func init() { + register(kubepodcpu, disabled, createKubepodCPUCollector) +} + +// Collect publishes the cpu information and all kubernetes pod cpu information to the prometheus channel +// On each run it reads the guaranteed pod cpus and exposes the pod, container, and NUMA IDs to the collector +func (c kubepodCPUCollector) Collect(ch chan<- prometheus.Metric) { + // This exposes the basic cpu alignment to prometheus. + for cpu, numa := range c.cpuInfo { + cpuID := "cpu" + cpu + desc := prometheus.NewDesc( + prometheus.BuildFQName(collectorNamespace, "", "cpu_info"), + c.name, + []string{"cpu", "numa_node"}, nil, + ) + + ch <- prometheus.MustNewConstMetric( + desc, + prometheus.CounterValue, + 1, + cpuID, + numa, + ) } - checkpointFile := cpuManagerCheckpoint{} - err = json.Unmarshal(cpuRaw, &checkpointFile) + + links, err := getGuaranteedPodCPUs() if err != nil { - log.Printf("cpu checkpoint file can not be read: %v", err) - return "" + log.Printf("pod cpu links not available: %v", err) + return } - return checkpointFile.DefaultCPUSet -} -//kubepodCPUCollector holds a static representation of node cpu topology and uses it to update information about kubernetes pod cpu usage. -type kubepodCPUCollector struct { - cpuInfo map[string]string - name string + for _, link := range links { + desc := prometheus.NewDesc( + prometheus.BuildFQName(collectorNamespace, "", c.name), + "pod_cpu", + []string{"cpu_id", "numa_node", "uid", "container_id"}, nil, + ) + + ch <- prometheus.MustNewConstMetric( + desc, + prometheus.CounterValue, + 1, + link.cpu, + c.cpuInfo[link.cpu], + link.podID, + link.containerID, + ) + } } -//podCPULink contains the information about the pod and container a single cpu is attached to -type podCPULink struct { - podID string - containerID string - cpu string +// Describe is not defined for this collector +func (c kubepodCPUCollector) Describe(ch chan<- *prometheus.Desc) { } -//createKubepodCPUCollector creates a static picture of the cpu topology of the system and returns a collector -//It also creates a static list of cpus in the kubernetes parent cgroup. +// createKubepodCPUCollector creates a static picture of the cpu topology of the system and returns a collector +// It also creates a static list of cpus in the kubernetes parent cgroup. func createKubepodCPUCollector() prometheus.Collector { cpuInfo, err := getCPUInfo() if err != nil { //Exporter will fail here if file can not be read. - log.Fatal("Fatal Error:"+"CPU Info for node can not be collected", err) + logFatal("Fatal Error: cpu info for node can not be collected, %v", err.Error()) } + return kubepodCPUCollector{ cpuInfo: cpuInfo, name: kubepodcpu, } } -//getKubernetesCPUList returns the information about the CPUs being used by Kubernetes overall -func getKubernetesCPUList() (string, error) { - kubeCPUString, err := parseCPUFile(filepath.Join(*kubePodCgroupPath, "cpuset.cpus")) +// getCPUInfo looks in the sys directory for information on CPU IDs and NUMA topology. This method runs once on initialization of the pod. +func getCPUInfo() (map[string]string, error) { + cpuInfo := make(map[string]string, 0) + files, err := fs.ReadDir(cpuinfofs, ".") if err != nil { - return "", err + return cpuInfo, fmt.Errorf("failed to read directory '%s'\n%v", *sysDevSysNodePath, err) } - return kubeCPUString, nil -} -//guaranteedPodCPUs creates a podCPULink for each CPU that is guaranteed -//This information is exposed under the cpuset in the cgroup file system with Kubernetes1.18/Docker/ -//This accounting will create an entry for each guaranteed pod, even if that pod isn't managed by CPU manager -//i.e. it will still create an entry if the pod is looking for millis of CPU -//Todo: validate regex matching and evaluate performance of this approach -//Todo: validate assumptions about directory structure against other runtimes and kubelet config. Plausibly problematic with CgroupsPerQos and other possible future cgroup changes -func (c kubepodCPUCollector) guaranteedPodCPUs() ([]podCPULink, error) { - //This generate method should be updated to create the non-exclusive cores mask used by guaranteed pods with fractional core usage as well as maximum Kubernetes usage. - kubeCPUString, err := getKubernetesCPUList() - if err != nil { - //Exporter killed here as CPU collector can not work without this information. - log.Fatal("Fatal error: Cannot get information on Kubernetes CPU usage", err) - } - defaultSet := readDefaultSet() - if err != nil { - //Exporter killed here as CPU collector can not work without this information. - log.Fatal("Fatal error: Cannot get information on Kubernetes CPU usage", err) - } - links := make([]podCPULink, 0) - files, err := ioutil.ReadDir(*kubePodCgroupPath) - if err != nil { - return links, fmt.Errorf("could not open path kubePod cgroups") - } - fileRE := regexp.MustCompile("pod[0-9a-f]{8}-[0-9a-f]*") - cpuSetFileRE := regexp.MustCompile("[a-z0-9]{20,}") + fileRE := regexp.MustCompile(`node\d+`) + cpuFileRE := regexp.MustCompile(`cpu\d+`) for _, f := range files { if f.IsDir() { - //Searching for files matching uuid pattern of 8-4-4-4-12 - if matches := fileRE.MatchString(f.Name()); matches { - if len(f.Name()) < 4 { - continue - } - podUID := f.Name()[3:] - containerFiles, err := ioutil.ReadDir(filepath.Join(*kubePodCgroupPath, f.Name())) + if fileRE.MatchString(f.Name()) { + numaNode := f.Name()[4:] + cpuFiles, err := fs.ReadDir(cpuinfofs, f.Name()) if err != nil { - return links, fmt.Errorf("could not open cpu files: %v", err) + return cpuInfo, fmt.Errorf("failed to read directory '%s'\n%v", filepath.Join(*sysDevSysNodePath, numaNode), err) } - for _, cpusetFile := range containerFiles { - if matches := cpuSetFileRE.MatchString(cpusetFile.Name()); matches { - cpuSetDesc, err := parseCPUFile(filepath.Join(*kubePodCgroupPath, f.Name(), cpusetFile.Name(), "cpuset.cpus")) - if err != nil { - return links, err - } - cpuList, err := parseCPURange(cpuSetDesc) - if err != nil { - return links, err - } - if cpuSetDesc == kubeCPUString || cpuSetDesc == defaultSet { - continue - } - for _, c := range cpuList { - links = append(links, podCPULink{podID: podUID, containerID: cpusetFile.Name(), cpu: c}) - } + + for _, cpu := range cpuFiles { + if cpuFileRE.MatchString(cpu.Name()) { + cpuID := cpu.Name()[3:] + cpuInfo[cpuID] = numaNode } } } } } + return cpuInfo, nil +} + +// getGuaranteedPodCPUs creates a podCPULink for each CPU that is guaranteed +// This information is exposed under the cpuset in the cgroup file system with Kubernetes1.18/Docker/ +// This accounting will create an entry for each guaranteed pod, even if that pod isn't managed by CPU manager +// i.e. it will still create an entry if the pod is looking for millis of CPU +// Todo: validate regex matching and evaluate performance of this approach +// Todo: validate assumptions about directory structure against other runtimes and kubelet config. Plausibly problematic with CgroupsPerQos and other possible future cgroup changes +func getGuaranteedPodCPUs() ([]podCPULink, error) { + links := make([]podCPULink, 0) + + kubeCPUString, kubeDefaultSet := getKubeDefaults() + + podDirectoryFilenames, err := getPodDirectories() + if err != nil { + return links, err + } + + for _, directory := range podDirectoryFilenames { + containerIDs, err := getContainerIDs(directory) + if err != nil { + return links, err + } + + for _, container := range containerIDs { + cpuSet, err := readCPUSet(filepath.Join(directory, container, "cpuset.cpus")) + if err != nil { + return links, err + } + if cpuSet == kubeCPUString || cpuSet == kubeDefaultSet { + continue + } + + cpuRange, err := parseCPURange(cpuSet) + if err != nil { + return links, err + } + + for _, link := range cpuRange { + links = append(links, podCPULink{directory[12 : len(directory)-6], container, link}) + } + } + } return links, nil } -//parseCPUFile can read cpuFiles in the Kernel cpuset format -func parseCPUFile(path string) (string, error) { - cpuRaw, err := ioutil.ReadFile(path) +func getPodDirectories() ([]string, error) { + podDirectoryFilenames := make([]string, 0) + + files, err := fs.ReadDir(kubecgroupfs, ".") // all files in the directory + if err != nil { + return podDirectoryFilenames, fmt.Errorf("could not open path kubePod cgroups: %v", err) + } + + podDirectoryRegex := regexp.MustCompile("pod[[:xdigit:]]{8}[_-][[:xdigit:]]{4}[_-][[:xdigit:]]{4}[_-][[:xdigit:]]{4}[_-][[:xdigit:]]{12}") + for _, podDirectory := range files { + podDirectoryFilename := podDirectory.Name() + if match := podDirectoryRegex.MatchString(podDirectoryFilename); match { + podDirectoryFilenames = append(podDirectoryFilenames, podDirectoryFilename) + } + } + return podDirectoryFilenames, nil +} + +func getContainerIDs(podDirectoryFilename string) ([]string, error) { + containerDirectoryFilenames := make([]string, 0) + + files, err := fs.ReadDir(kubecgroupfs, podDirectoryFilename) if err != nil { - return "", fmt.Errorf("could not open cgroup cpuset files: %v", err) + return containerDirectoryFilenames, fmt.Errorf("could not read cpu files directory: %v", err) } - cpuString := strings.TrimSpace(string(cpuRaw)) - return cpuString, err + + containerIDRegex := regexp.MustCompile("[[:xdigit:]]{20,}") // change regexback + for _, containerDirectory := range files { + containerID := containerDirectory.Name() + if match := containerIDRegex.MatchString(containerID); match { + containerDirectoryFilenames = append(containerDirectoryFilenames, containerID) + } + } + + return containerDirectoryFilenames, nil } -//parseCPURanges can read cpuFiles in the Kernel cpuset format +// readDefaultSet extracts the information about the "default" set of cpus available to kubernetes +func readDefaultSet(data []byte) string { + checkpointFile := cpuManagerCheckpoint{} + + if err := json.Unmarshal(data, &checkpointFile); err != nil { + log.Printf("cpu checkpoint file could not be unmarshalled, error: %v", err) + return "" + } + + return checkpointFile.DefaultCPUSet +} + +// readCPUSet can read cpuFiles in the Kernel cpuset format +func readCPUSet(cpuSetFilepath string) (string, error) { + cpuSetBytes, err := fs.ReadFile(kubecgroupfs, cpuSetFilepath) + if err != nil { + return "", fmt.Errorf("could not open cgroup cpuset files, error: %v", err) + } + return strings.TrimSpace(string(cpuSetBytes)), err +} + +// parseCPURanges can read cpuFiles in the Kernel cpuset format func parseCPURange(cpuString string) ([]string, error) { cpuList := make([]string, 0) cpuRanges := strings.Split(cpuString, ",") @@ -170,102 +263,52 @@ func parseCPURange(cpuString string) ([]string, error) { if err != nil { return cpuList, err } + end, err := strconv.Atoi(endpoints[1]) if err != nil { return cpuList, err } + for e := start; e <= end; e++ { cpuList = append(cpuList, strconv.Itoa(e)) } } } + return cpuList, nil } -//Collect publishes the cpu information and all kubernetes pod cpu information to the prometheus channel -//On each run it reads the guaranteed pod cpus and exposes the pod, container, and NUMA IDs to the collector -func (c kubepodCPUCollector) Collect(ch chan<- prometheus.Metric) { - //This exposes the basic cpu alignment to prometheus. - for cpu, numa := range c.cpuInfo { - cpuID := "cpu" + cpu - desc := prometheus.NewDesc( - prometheus.BuildFQName(collectorNamespace, "", "cpu_info"), - c.name, - []string{"cpu", "numa_node"}, nil, - ) - ch <- prometheus.MustNewConstMetric( - desc, - prometheus.CounterValue, - 1, - cpuID, - numa, - ) - } - podCPULinks, err := c.guaranteedPodCPUs() +func getKubeDefaults() (string, string) { + kubeCPUString, err := readCPUSet("cpuset.cpus") if err != nil { - log.Printf("pod cpu links not available: %v", err) - return + // Exporter killed here as CPU collector can not work without this information. + logFatal("Fatal Error: cannot get information on Kubernetes CPU usage, %v", err.Error()) } - for _, link := range podCPULinks { - desc := prometheus.NewDesc( - prometheus.BuildFQName(collectorNamespace, "", c.name), - "pod_cpu", - []string{"cpu_id", "numa_node", "uid", "container_id"}, nil, - ) - ch <- prometheus.MustNewConstMetric( - desc, - prometheus.CounterValue, - 1, - link.cpu, - c.cpuInfo[link.cpu], - link.podID, - link.containerID, - ) + + cpuRawBytes, err := fs.ReadFile(cpucheckpointfs, filepath.Base(*cpuCheckPointFile)) + if err != nil { + log.Printf("unable to read cpu checkpoint file '%s', error: %v", *cpuCheckPointFile, err) } + + return kubeCPUString, readDefaultSet(cpuRawBytes) } -//getCPUInfo looks in the sys directory for information on CPU IDs and NUMA topology. This method runs once on initialization of the pod. -func getCPUInfo() (map[string]string, error) { - cpuInfo := make(map[string]string, 0) - files, err := ioutil.ReadDir(*sysDevSysNodePath) - if err != nil { - return cpuInfo, fmt.Errorf("could not open path to cpu info") +func resolveKubePodCPUFilepaths() error { + if err := utils.ResolveFlag("path.kubecgroup", kubePodCgroupPath); err != nil { + return err } - fileRE := regexp.MustCompile("node[0-9]+") - cpuFileRE := regexp.MustCompile("cpu[0-9]+") - for _, f := range files { - if f.IsDir() { - if matches := fileRE.MatchString(f.Name()); matches { - if len(f.Name()) < 5 { - continue - } - numaNode := f.Name()[4:] - cpuFiles, err := ioutil.ReadDir(filepath.Join(*sysDevSysNodePath, f.Name())) - if err != nil { - return cpuInfo, fmt.Errorf("could not open cpu files: %v", err) - } - for _, cpu := range cpuFiles { - if matches := cpuFileRE.MatchString(cpu.Name()); matches { - if len(cpu.Name()) < 4 { - continue - } - cpuID := cpu.Name()[3:] - cpuInfo[cpuID] = numaNode - } - } - } - } + + if err := utils.ResolveFlag("path.nodecpuinfo", sysDevSysNodePath); err != nil { + return err } - return cpuInfo, nil -} -//Describe is not defined for this collector -func (c kubepodCPUCollector) Describe(ch chan<- *prometheus.Desc) { + if err := utils.ResolveFlag("path.cpucheckpoint", cpuCheckPointFile); err != nil { + return err + } -} + kubecgroupfs = os.DirFS(*kubePodCgroupPath) + cpuinfofs = os.DirFS(*sysDevSysNodePath) + cpucheckpointfs = os.DirFS(filepath.Dir(*cpuCheckPointFile)) -func ResolveKubePodCPUFilepaths() { - utils.ResolvePath(kubePodCgroupPath) - utils.ResolvePath(sysDevSysNodePath) - utils.ResolvePath(cpuCheckPointFile) + return nil } diff --git a/collectors/pod_cpu_link_test.go b/collectors/pod_cpu_link_test.go new file mode 100644 index 0000000..9475590 --- /dev/null +++ b/collectors/pod_cpu_link_test.go @@ -0,0 +1,303 @@ +package collectors + +import ( + "errors" + "fmt" + "io/fs" + "strconv" + "testing/fstest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" +) + +var _ = DescribeTable("test pod cpu link collection", // Collect + func(fsys fs.FS, expected []metric, logs ...string) { + cpuinfofs = fsys + kubecgroupfs = fsys + cpucheckpointfs = fsys + + ch := make(chan prometheus.Metric, 1) + go createKubepodCPUCollector().Collect(ch) + + for i := 0; i < len(expected); i++ { + m := dto.Metric{} + err := (<-ch).Write(&m) + Expect(err).ToNot(HaveOccurred()) + + labels := make(map[string]string, 4) + for _, label := range m.Label { + labels[*label.Name] = *label.Value + } + + metric := metric{labels: labels, counter: *m.Counter.Value} + + Expect(metric).To(BeElementOf(expected)) + } + + assertLogs(logs) + }, + Entry("test numa node and cpuset collection", + fstest.MapFS{ + "node0/cpu0": {Mode: fs.ModeDir}, + "node0/cpu2": {Mode: fs.ModeDir}, + "node1/cpu1": {Mode: fs.ModeDir}, + "node1/cpu3": {Mode: fs.ModeDir}, + "cpuset.cpus": {Data: []byte("4-7")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"4-7\",\"checksum\":1353318690}")}, + "kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice/0123456789abcdefaaaa/cpuset.cpus": {Data: []byte("0-3")}}, + []metric{ + {map[string]string{"cpu": "cpu0", "numa_node": "0"}, 1}, + {map[string]string{"cpu": "cpu2", "numa_node": "0"}, 1}, + {map[string]string{"cpu": "cpu1", "numa_node": "1"}, 1}, + {map[string]string{"cpu": "cpu3", "numa_node": "1"}, 1}, + {map[string]string{"cpu_id": "0", "numa_node": "0", "uid": "6b5b533a_6307_48d1_911f_07bf5d4e1c82", "container_id": "0123456789abcdefaaaa"}, 1}, + {map[string]string{"cpu_id": "2", "numa_node": "0", "uid": "6b5b533a_6307_48d1_911f_07bf5d4e1c82", "container_id": "0123456789abcdefaaaa"}, 1}, + {map[string]string{"cpu_id": "1", "numa_node": "1", "uid": "6b5b533a_6307_48d1_911f_07bf5d4e1c82", "container_id": "0123456789abcdefaaaa"}, 1}, + {map[string]string{"cpu_id": "3", "numa_node": "1", "uid": "6b5b533a_6307_48d1_911f_07bf5d4e1c82", "container_id": "0123456789abcdefaaaa"}, 1}}), + Entry("test unavailable kube cgroup directory", + fstest.MapFS{ + "node0/cpu0": {Mode: fs.ModeDir}, + "node0/cpu2": {Mode: fs.ModeDir}, + "node1/cpu1": {Mode: fs.ModeDir}, + "node1/cpu3": {Mode: fs.ModeDir}, + "cpuset.cpus": {Data: []byte("4-7")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"4-7\",\"checksum\":1353318690}")}, + "kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c83.slice": {Mode: fs.ModeExclusive}}, + []metric{ + {map[string]string{"cpu": "cpu0", "numa_node": "0"}, 1}, + {map[string]string{"cpu": "cpu2", "numa_node": "0"}, 1}, + {map[string]string{"cpu": "cpu1", "numa_node": "1"}, 1}, + {map[string]string{"cpu": "cpu3", "numa_node": "1"}, 1}}, + "pod cpu links not available: could not read cpu files directory: readdir kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c83.slice: not implemented"), +) + +var _ = DescribeTable("test reading default cpu set", // readDefaultSet + func(data []byte, expected string, logs ...string) { + Expect(readDefaultSet(data)).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("read empty", + []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"\",\"checksum\":1353318690}"), + ""), + Entry("read successful", + []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"1,2,3,4\",\"checksum\":1353318690}"), + "1,2,3,4"), + Entry("read failed with malformed data", + []byte("\"policyName\":\"none\",\"checksum\":1353318690"), + "", + "cpu checkpoint file could not be unmarshalled, error: invalid character ':' after top-level value"), +) + +var _ = DescribeTable("test creating kubepodCPU collector", // createKubepodCPUCollector + func(fsys fs.FS, expectedCollector kubepodCPUCollector, logs ...string) { + cpuinfofs = fsys + + collector := createKubepodCPUCollector() + Expect(collector).To(Equal(expectedCollector)) + + assertLogs(logs) + }, + Entry("successful creation", + fstest.MapFS{ + "node0/cpu0": {Mode: fs.ModeDir}, + "node0/cpu2": {Mode: fs.ModeDir}, + "node1/cpu1": {Mode: fs.ModeDir}, + "node1/cpu3": {Mode: fs.ModeDir}}, + kubepodCPUCollector{cpuInfo: map[string]string{"0": "0", "2": "0", "1": "1", "3": "1"}, name: kubepodcpu}), + Entry("directory doesn't exist", + fstest.MapFS{".": {Mode: fs.ModeExclusive}}, // to emulate the directory doesn't exist + kubepodCPUCollector{cpuInfo: map[string]string{}, name: kubepodcpu}, + "Fatal Error: cpu info for node can not be collected, failed to read directory '/sys/devices/system/node/'\nreaddir .: not implemented"), +) + +var _ = DescribeTable("test getting kubernetes cpu list", // getKubeDefaults + func(fsys fs.FS, expectedKubeCPUString, expectedDefaultSet string) { + kubecgroupfs = fsys + cpucheckpointfs = fsys + + kubeCPUString, kubeDefaultSet := getKubeDefaults() + Expect(kubeCPUString).To(Equal(expectedKubeCPUString)) + Expect(kubeDefaultSet).To(Equal(expectedDefaultSet)) + }, + Entry("read empty", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("")}, + "cpu_manager_state": {Data: []byte("")}}, + "", + ""), + Entry("read successful", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("0-87")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"static\",\"defaultCpuSet\":\"0-63\",\"checksum\":1058907510}")}}, + "0-87", + "0-63"), + Entry("read successful with malformed data", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte(" 0-87 ")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"static\",\"defaultCpuSet\":\"0-63\",\"checksum\":1058 907 51 0}")}}, + "0-87", + ""), + Entry("read failed, file doesn't exist", + fstest.MapFS{}, + "", + ""), +) + +var _ = DescribeTable("test getting guaranteed pod cpus", // guaranteedPodCPUs + func(fsys fs.FS, expected []podCPULink, expectedErr error, logs ...string) { + kubecgroupfs = fsys + cpucheckpointfs = fsys + + data, err := getGuaranteedPodCPUs() + Expect(data).To(Equal(expected)) + + if expectedErr != nil { + Expect(err).To(MatchError(expectedErr)) + } + + assertLogs(logs) + }, + Entry("container cpuset available", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("0-3")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"4-7\",\"checksum\":1353318690}")}, + "kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice/0123456789abcdefaaaa/cpuset.cpus": {Data: []byte("8-11")}}, + []podCPULink{ + {"6b5b533a_6307_48d1_911f_07bf5d4e1c82", "0123456789abcdefaaaa", "8"}, + {"6b5b533a_6307_48d1_911f_07bf5d4e1c82", "0123456789abcdefaaaa", "9"}, + {"6b5b533a_6307_48d1_911f_07bf5d4e1c82", "0123456789abcdefaaaa", "10"}, + {"6b5b533a_6307_48d1_911f_07bf5d4e1c82", "0123456789abcdefaaaa", "11"}}, + nil), + Entry("cgroup directory doesn't exist", + fstest.MapFS{".": {Mode: fs.ModeExclusive}}, + []podCPULink{}, + fmt.Errorf("could not open path kubePod cgroups: readdir .: not implemented"), + "cannot get information on Kubernetes CPU usage, could not open cgroup cpuset files, error: open cpuset.cpus: file does not exist", + "unable to read cpu checkpoint file '/var/lib/kubelet/cpu_manager_state', error: open cpu_manager_state: file does not exist", + "cpu checkpoint file could not be unmarshalled, error: unexpected end of JSON input"), + Entry("unable to read pod cgroup directory", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("0-3")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"4-7\",\"checksum\":1353318690}")}, + "kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice": {Mode: fs.ModeExclusive}}, + []podCPULink{}, + fmt.Errorf("could not read cpu files directory: readdir kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice: not implemented")), + Entry("unable to read container cpuset file", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("0-3")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"4-7\",\"checksum\":1353318690}")}, + "kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice/0123456789abcdefaaaa/cpuset.cpus": {Mode: fs.ModeDir}}, + []podCPULink{}, + fmt.Errorf("could not open cgroup cpuset files, error: read kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice/0123456789abcdefaaaa/cpuset.cpus: invalid argument")), + Entry("container cpuset range covered by defaults", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("0-3")}, + "cpu_manager_state": {Data: []byte("{\"policyName\":\"none\",\"defaultCpuSet\":\"4-7\",\"checksum\":1353318690}")}, + "kubepods-pod6b5b533a_6307_48d1_911f_07bf5d4e1c82.slice/0123456789abcdefaaaa/cpuset.cpus": {Data: []byte("0-3")}}, + []podCPULink{}, + nil), +) + +var _ = DescribeTable("test parsing cpu file", // parseCPUFile + func(path string, fsys fs.FS, expectedString string, expectedErr error) { + kubecgroupfs = fsys + + data, err := readCPUSet(path) + Expect(data).To(Equal(expectedString)) + + if expectedErr != nil { + Expect(err).To(Equal(expectedErr)) + } + }, + Entry("read empty", + "cpuset.cpus", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("")}}, + "", + nil), + Entry("read successful", + "cpuset.cpus", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte("0-87")}}, + "0-87", + nil), + Entry("read successful with malformed data", + "cpuset.cpus", + fstest.MapFS{ + "cpuset.cpus": {Data: []byte(" 0-87 ")}}, + "0-87", + nil), + Entry("read failed, file doesn't exist", + "cpuset.cpus", + fstest.MapFS{}, + "", + fmt.Errorf("could not open cgroup cpuset files, error: open cpuset.cpus: file does not exist")), +) + +var _ = DescribeTable("test parsing cpu range", // parseCPURange + func(cpuString string, expected []string, expectedErr error) { + data, err := parseCPURange(cpuString) + Expect(data).To(Equal(expected)) + + if expectedErr != nil { + Expect(err).To(MatchError(expectedErr)) + } + }, + Entry("valid range '0-3,7-9'", + "0-3,7-9", + []string{"0", "1", "2", "3", "7", "8", "9"}, + nil), + Entry("valid range '0-3'", + "0-3", + []string{"0", "1", "2", "3"}, + nil), + Entry("valid range '7'", + "7", + []string{"7"}, + nil), + Entry("invalid range '-1'", + "-1", + []string{}, + strconv.ErrSyntax), + Entry("invalid range '0-'", + "0-", + []string{}, + strconv.ErrSyntax), +) + +var _ = DescribeTable("test getting cpu info", // getCPUInfo + func(fsys fs.FS, expectedData map[string]string, expectedErr error) { + cpuinfofs = fsys + + data, err := getCPUInfo() + + for k, v := range expectedData { + Expect(data).To(HaveKey(k)) + Expect(data[k]).To(Equal(v)) + } + + Expect(data).To(Equal(expectedData)) + + if expectedErr != nil { + Expect(err).To(MatchError(expectedErr)) + } + }, + Entry("valid info", + fstest.MapFS{ + "node0/cpu0": {Mode: fs.ModeDir}, + "node0/cpu2": {Mode: fs.ModeDir}, + "node1/cpu1": {Mode: fs.ModeDir}, + "node1/cpu3": {Mode: fs.ModeDir}}, + map[string]string{"0": "0", "2": "0", "1": "1", "3": "1"}, + nil), + Entry("directory doesn't exist", + fstest.MapFS{".": {Mode: fs.ModeExclusive}}, // to emulate the directory doesn't exist + map[string]string{}, + errors.New("failed to read directory '/sys/devices/system/node/'\nreaddir .: not implemented")), +) + +// TODO: create integration tests for GetV1Client and PodResources, they require the kubelet API diff --git a/collectors/pod_dev_link.go b/collectors/pod_dev_link.go index f520635..364e889 100644 --- a/collectors/pod_dev_link.go +++ b/collectors/pod_dev_link.go @@ -1,49 +1,47 @@ package collectors -//pod_dev_link publishes which devices are connected to which pods in Kubernetes by querying the Kubelet api +// pod_dev_link publishes which devices are connected to which pods in Kubernetes by querying the Kubelet api import ( "context" "flag" + "fmt" + "log" + "net" + "net/url" "regexp" - "sriov-network-metrics-exporter/pkg/utils" "strings" + "time" "github.com/prometheus/client_golang/prometheus" - "k8s.io/kubernetes/pkg/kubelet/apis/podresources" - podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + v1 "k8s.io/kubelet/pkg/apis/podresources/v1" - "log" - "time" + "sriov-network-metrics-exporter/pkg/utils" ) var ( defaultPodResourcesMaxSize = 1024 * 1024 * 16 // 16 Mb podDevLinkName = "kubepoddevice" - podResourcesPath = flag.String("path.kubeletSocket", "/var/lib/kubelet/pod-resources/kubelet.sock", "Path to kubelet resources socket") - pciAddressPattern = regexp.MustCompile(`^[[:xdigit:]]{4}:([[:xdigit:]]{2}):([[:xdigit:]]{2})\.([[:xdigit:]])$`) + podResourcesPath = flag.String("path.kubeletsocket", "/var/lib/kubelet/pod-resources/kubelet.sock", "Path to kubelet resources socket") + pciAddressPattern = regexp.MustCompile(`^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}\.\d$`) ) -//init runs the registration for this collector on package import -func init() { - register(podDevLinkName, disabled, createPodDevLinkCollector) -} - -//podDevLinkCollector the basic type used to collect information on kubernetes device links +// podDevLinkCollector the basic type used to collect information on kubernetes device links type podDevLinkCollector struct { name string } -func createPodDevLinkCollector() prometheus.Collector { - return podDevLinkCollector{ - name: podDevLinkName, - } +// init runs the registration for this collector on package import +func init() { + register(podDevLinkName, disabled, createPodDevLinkCollector) } -//This collector starts by making a call to the kubelet api which could create a delay. +// This collector starts by making a call to the kubelet api which could create a delay. // This information could be cached on a loop after the previous call to improve prometheus scraping performance. -//Collect scrapes the kubelet api and structures the returned value into a prometheus info metric. +// Collect scrapes the kubelet api and structures the returned value into a prometheus info metric. func (c podDevLinkCollector) Collect(ch chan<- prometheus.Metric) { resources := PodResources() for _, podRes := range resources { @@ -57,17 +55,18 @@ func (c podDevLinkCollector) Collect(ch chan<- prometheus.Metric) { if !isPci(dev) { continue } - devID := dev + desc := prometheus.NewDesc( prometheus.BuildFQName(collectorNamespace, "", c.name), c.name, []string{"pciAddr", "dev_type", "pod", "namespace", "container"}, nil, ) + ch <- prometheus.MustNewConstMetric( desc, prometheus.CounterValue, 1, - devID, + dev, devType, podName, podNamespace, @@ -79,38 +78,77 @@ func (c podDevLinkCollector) Collect(ch chan<- prometheus.Metric) { } } +// Describe has no defined behaviour for this collector +func (c podDevLinkCollector) Describe(ch chan<- *prometheus.Desc) { +} + +func createPodDevLinkCollector() prometheus.Collector { + return podDevLinkCollector{ + name: podDevLinkName, + } +} + //PodResources uses the kubernetes kubelet api to get information about the devices and the pods they are attached to. //We create and close a new connection here on each run. The performance impact of this seems marginal - but sharing a connection might save cpu time -func PodResources() []*podresourcesapi.PodResources { - var podResource []*podresourcesapi.PodResources +func PodResources() []*v1.PodResources { + var podResource []*v1.PodResources + kubeletSocket := strings.Join([]string{"unix:///", *podResourcesPath}, "") - client, conn, err := podresources.GetClient(kubeletSocket, 10*time.Second, defaultPodResourcesMaxSize) + client, conn, err := GetV1Client(kubeletSocket, 10*time.Second, defaultPodResourcesMaxSize) if err != nil { log.Print(err) return podResource } + defer conn.Close() ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - resp, err := client.List(ctx, &podresourcesapi.ListPodResourcesRequest{}) + resp, err := client.List(ctx, &v1.ListPodResourcesRequest{}) if err != nil { log.Printf("getPodResources: failed to list pod resources, %v.Get(_) = _, %v", client, err) return podResource } + podResource = resp.PodResources return podResource } -//Describe has no defined behaviour for this collector -func (c podDevLinkCollector) Describe(ch chan<- *prometheus.Desc) { -} - -//checks to see if a device id matches a pci address. If not we're able to discard it. +// Checks to see if a device id matches a pci address. If not we're able to discard it. func isPci(id string) bool { return pciAddressPattern.MatchString(id) } -func ResolveKubePodDeviceFilepaths() { - utils.ResolvePath(podResourcesPath) +func resolveKubePodDeviceFilepaths() error { + if err := utils.ResolveFlag("path.kubeletSocket", podResourcesPath); err != nil { + return err + } + + return nil +} + +// GetV1Client returns a client for the PodResourcesLister grpc service +// Extracted from package k8s.io/kubernetes/pkg/kubelet/apis/podresources client.go v1.24.3 +// This is what is recommended for consumers of this package +func GetV1Client(socket string, connectionTimeout time.Duration, maxMsgSize int) (v1.PodResourcesListerClient, *grpc.ClientConn, error) { + url, err := url.Parse(socket) + if err != nil { + return nil, nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout) + defer cancel() + + conn, err := grpc.DialContext(ctx, url.Path, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithContextDialer(dialer), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize))) + if err != nil { + return nil, nil, fmt.Errorf("error dialing socket %s: %v", socket, err) + } + return v1.NewPodResourcesListerClient(conn), conn, nil +} + +func dialer(ctx context.Context, addr string) (net.Conn, error) { + return (&net.Dialer{}).DialContext(ctx, "unix", addr) } diff --git a/collectors/pod_dev_link_test.go b/collectors/pod_dev_link_test.go new file mode 100644 index 0000000..0e49c29 --- /dev/null +++ b/collectors/pod_dev_link_test.go @@ -0,0 +1,39 @@ +package collectors + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// TODO: create Collector and dialer unit tests + +var _ = Describe("test creating podDevLink collector", func() { // createPodDevLinkCollector + It("returns the correct collector", func() { + collector := createPodDevLinkCollector() + Expect(collector).To(Equal(podDevLinkCollector{name: podDevLinkName})) + }) +}) + +var _ = DescribeTable("test pci address regexp: "+pciAddressPattern.String(), // isPci + func(pciAddr string, expected bool) { + Expect(isPci(pciAddr)).To(Equal(expected)) + }, + Entry("valid, 0000:00:00.0", "0000:00:00.0", true), + Entry("valid, ffff:00:00.0", "ffff:00:00.0", true), + Entry("valid, 0000:ff:00.0", "0000:ff:00.0", true), + Entry("valid, 0000:00:ff.0", "0000:00:ff.0", true), + Entry("valid, 0000:00:00.0", "0000:00:00.0", true), + Entry("invalid, 0000.00:00.0", "0000.00:00.0", false), + Entry("invalid, 0000:00.00.0", "0000:00.00.0", false), + Entry("invalid, 0000:00:00:0", "0000:00:00:0", false), + Entry("invalid, gggg:00:00.0", "gggg:00:00.0", false), + Entry("invalid, 0000:gg:00.0", "0000:gg:00.0", false), + Entry("invalid, 0000:00:gg.0", "0000:00:gg.0", false), + Entry("invalid, 0000:00:00.a", "0000:00:00.a", false), + Entry("invalid, 00000:00:00.0", "00000:00:00.0", false), + Entry("invalid, 0000:000:00.0", "0000:000:00.0", false), + Entry("invalid, 0000:00:000.0", "0000:00:000.0", false), + Entry("invalid, 0000:00:00.00", "0000:00:00.00", false), +) + +// TODO: create integration tests for GetV1Client and PodResources, they require the kubelet API diff --git a/collectors/sriovdev.go b/collectors/sriovdev.go index c91e728..898ca7c 100644 --- a/collectors/sriovdev.go +++ b/collectors/sriovdev.go @@ -1,118 +1,99 @@ package collectors -//sriovDev has the methods for implementing an sriov stats reader and publishing its information to Prometheus +// sriovDev has the methods for implementing an sriov stats reader and publishing its information to Prometheus import ( - "errors" "flag" "fmt" - "io/ioutil" + "io/fs" "log" "os" "path/filepath" - "sriov-network-metrics-exporter/pkg/utils" "strconv" "strings" "github.com/prometheus/client_golang/prometheus" + + "sriov-network-metrics-exporter/pkg/drvinfo" + "sriov-network-metrics-exporter/pkg/utils" ) const ( - noNumaInfo = "-1" + noNumaInfo = "-1" + supportedDriversDbPath = "/etc/sriov-network-metrics-exporter/drivers.yaml" ) var ( - sysBusPci = flag.String("path.sysbuspci", "/sys/bus/pci/devices", "Path to sys/bus/pci on host") - sysClassNet = flag.String("path.sysclassnet", "/sys/class/net/", "Path to sys/class/net on host") - netlinkEnabled = flag.Bool("collector.netlink", true, "Enable or disable use of netlink for VF stats collection in favor of driver specific collectors.") - totalVfFile = "sriov_totalvfs" - pfNameFile = "/net" - netClassFile = "/class" - driverFile = "/driver" - netClass int64 = 0x020000 - vfStatSubsystem = "vf" - sriovDev = "vfstats" - sriovPFs = make([]string, 0) + collectorPriority utils.StringListFlag + defaultPriority = utils.StringListFlag{"sysfs", "netlink"} + sysBusPci = flag.String("path.sysbuspci", "/sys/bus/pci/devices", "Path to sys/bus/pci/devices/ on host") + sysClassNet = flag.String("path.sysclassnet", "/sys/class/net", "Path to sys/class/net/ on host") + pfNameFile = "net" + netClassFile = "class" + netClass int64 = 0x020000 + vfStatsSubsystem = "vf" + vfStatsCollectorName = "vfstats" + + supportedDrivers drvinfo.SupportedDrivers + + devfs fs.FS + netfs fs.FS ) -//vfList contains a list of addresses for VFs with the name of the physical interface as value -type vfWithRoot map[string]string +// vfsPCIAddr is a map of VF IDs to VF PCI addresses i.e. {"0": "0000:3b:02.0", "1": "0000:3b:02.1"} +type vfsPCIAddr map[string]string -//init runs the registration for this collector on package import +// init runs the registration for this collector on package import func init() { - register(sriovDev, enabled, createSriovdevCollector) + flag.Var(&collectorPriority, "collector.vfstatspriority", "Priority of collectors") + supportedDrivers = drvinfo.NewSupportedDrivers(supportedDriversDbPath) + register(vfStatsCollectorName, enabled, createSriovDevCollector) } -//this is the generic collector for VF stats. -type sriovdevCollector struct { - name string - pfWithNuma map[string]string +// This is the generic collector for VF stats. +type sriovDevCollector struct { + name string + pfsWithNumaInfo map[string]string } -//SriovdevCollector is initialized with the physical functions on the host. This is not updated after initialization. -func createSriovdevCollector() prometheus.Collector { - pfList, err := getSriovPFs() - numaList := sriovNumaNodes(pfList) - if err != nil { - log.Fatal(err) - } - return sriovdevCollector{ - name: sriovDev, - pfWithNuma: numaList, - } +type sriovDev struct { + name string + reader sriovStatReader + vfs vfsPCIAddr } -//sriovNumaNodes returns the numa location for each of the PFs with SR-IOV capabilities -func sriovNumaNodes(pfList []string) map[string]string { - numaList := make(map[string]string) - for _, pf := range pfList { - path := filepath.Join(*sysBusPci, pf, "numa_node") - numaRaw, err := ioutil.ReadFile(path) - if err != nil { - log.Printf("Could not read numa node for card at %v", path) - numaList[pf] = "" - } - numaAsString := strings.TrimSpace(string(numaRaw)) - if err == nil && numaAsString != noNumaInfo { - numaList[pf] = numaAsString - } else { - log.Printf("Could not read numa node for card at %v", path) - numaList[pf] = "" - } +// Collect runs the appropriate collector for each SR-IOV vf on the system and publishes its statistics. +func (c sriovDevCollector) Collect(ch chan<- prometheus.Metric) { + log.Printf("collecting sr-iov device metrics") + + priority := collectorPriority + if len(priority) == 0 { + log.Printf("collector.priority not specified in flags, using default priority") + priority = defaultPriority } - return numaList -} -//Collect runs the appropriate collector for each SR-IOV vf on the system and publishes its statistics. -func (c sriovdevCollector) Collect(ch chan<- prometheus.Metric) { - for pfAddr, numaNode := range c.pfWithNuma { - pfName := getPFName(pfAddr) - if pfName == "" { - continue - } - // appropriate reader for VF is returned based on the PF. - //This reader may also be the moment for reading the stats from each device as in the netlink reader. - reader := statReaderForPF(pfName) - if reader == nil { - continue - } - vfs, err := vfList(pfAddr) - if err != nil { + log.Printf("collector priority: %s", priority) + for pfAddr, numaNode := range c.pfsWithNumaInfo { + pf := getSriovDev(pfAddr, priority) + + if pf.reader == nil { continue } - for id, address := range vfs { - stats := reader.ReadStats(pfName, id) + + for id, address := range pf.vfs { + stats := pf.reader.ReadStats(pf.name, id) for name, v := range stats { desc := prometheus.NewDesc( - prometheus.BuildFQName(collectorNamespace, vfStatSubsystem, name), + prometheus.BuildFQName(collectorNamespace, vfStatsSubsystem, name), fmt.Sprintf("Statistic %s.", name), []string{"pf", "vf", "pciAddr", "numa_node"}, nil, ) + ch <- prometheus.MustNewConstMetric( desc, prometheus.CounterValue, float64(v), - pfName, + pf.name, id, address, numaNode, @@ -122,104 +103,158 @@ func (c sriovdevCollector) Collect(ch chan<- prometheus.Metric) { } } -//getSriovPFs returns the SRIOV capable Physical Network functions for the host. -func getSriovPFs() ([]string, error) { - devs := getPCIDevs() - if len(devs) == 0 { - return sriovPFs, errors.New("pci devices could not be found") - } - for _, device := range devs { - if isSriovNetPF(device.Name()) { - sriovPFs = append(sriovPFs, device.Name()) - } - } - if len(sriovPFs) == 0 { - return sriovPFs, errors.New("no sriov net devices found on host") +// Describe isn't implemented for this collector +func (c sriovDevCollector) Describe(ch chan<- *prometheus.Desc) { +} + +// sriovDevCollector is initialized with the physical functions on the host. This is not updated after initialization. +func createSriovDevCollector() prometheus.Collector { + devs := getSriovDevAddrs() + numaNodes := getNumaNodes(devs) + + return sriovDevCollector{ + name: vfStatsCollectorName, + pfsWithNumaInfo: numaNodes, } - return sriovPFs, nil } -//isSriovNetPF checks if is device SRIOV capable net device. It checks if the sriov_totalvfs file exists for the given PCI address -func isSriovNetPF(pciAddr string) bool { - totalVfFilePath := filepath.Join(*sysBusPci, pciAddr, totalVfFile) - devClassFilePath := filepath.Join(*sysBusPci, pciAddr, netClassFile) - if !isNetDevice(devClassFilePath) { - return false +// getSriovDevAddrs returns the PCI addresses of the SRIOV capable Physical Functions on the host. +func getSriovDevAddrs() []string { + sriovDevs := make([]string, 0) + + devs, err := fs.Glob(devfs, "*/sriov_totalvfs") + if err != nil { + log.Printf("Invalid pattern\n%v", err) // unreachable code } - if _, err := os.Stat(totalVfFilePath); err != nil { - return false + + if len(devs) == 0 { + log.Printf("no sriov net devices found") + } + + for _, dev := range devs { + devAddr := filepath.Dir(dev) + if isNetDevice(filepath.Join(devAddr, netClassFile)) { + sriovDevs = append(sriovDevs, devAddr) + } } - return true + + return sriovDevs } -//isNetDevice checks if the device is a net device by checking its device class -func isNetDevice(filepath string) bool { - file, err := ioutil.ReadFile(filepath) +// getSriovDev returns a sriovDev record containing the physical function interface name, stats reader and initialized virtual functions. +func getSriovDev(pfAddr string, priority []string) sriovDev { + name := getPFName(pfAddr) + vfs, err := vfList(pfAddr) if err != nil { - return false + log.Printf("error getting vf address\n%v", err) } - classHex := strings.TrimSpace(string(file)) - deviceClass, err := strconv.ParseInt(classHex, 0, 64) - if err != nil { - log.Printf("could not parse class file: %v", err) - return false + + return sriovDev{ + name, + getStatsReader(name, priority), + vfs, } - return deviceClass == netClass } -//getPCIDevs returns all of the PCI device files available on the host -func getPCIDevs() []os.FileInfo { - links, err := ioutil.ReadDir(*sysBusPci) - if err != nil { - return make([]os.FileInfo, 0) +// getNumaNodes returns the numa location for each of the PFs with SR-IOV capabilities +func getNumaNodes(devs []string) map[string]string { + pfNumaInfo := make(map[string]string) + + for _, dev := range devs { + numaFilepath := filepath.Join(dev, "numa_node") + numaRaw, err := fs.ReadFile(devfs, numaFilepath) + if err != nil { + log.Printf("could not read numa_node file for device '%s'\n%v", dev, err) + pfNumaInfo[dev] = "" + continue + } + + numaNode := strings.TrimSpace(string(numaRaw)) + if numaNode == noNumaInfo { + log.Printf("no numa node information for device '%s'", dev) + pfNumaInfo[dev] = "" + continue + } + + pfNumaInfo[dev] = numaNode } - return links + + return pfNumaInfo } -//vfList returns the Virtual Functions associated with a specific SRIOV Physical Function -func vfList(pfAddress string) (vfWithRoot, error) { - vfList := make(vfWithRoot, 0) - pfDir := filepath.Join(*sysBusPci, pfAddress) - _, err := os.Lstat(pfDir) +// vfList returns the virtual functions associated with the specified SRIOV physical function +func vfList(dev string) (vfsPCIAddr, error) { + vfList := make(vfsPCIAddr, 0) + + vfs, err := fs.Glob(devfs, filepath.Join(dev, "virtfn*")) if err != nil { - err = fmt.Errorf("could not get PF directory information for device: %s, Err: %v", pfAddress, err) - return vfList, err + log.Printf("Invalid pattern\n%v", err) // unreachable code } - vfDirs, err := filepath.Glob(filepath.Join(pfDir, "virtfn*")) - if err != nil { - err = fmt.Errorf("error reading VF directories %v", err) - return vfList, err - } - //Read all VF directory and get add VF PCI addr to the vfList - for _, dir := range vfDirs { - dirInfo, err := os.Lstat(dir) - if err == nil && (dirInfo.Mode()&os.ModeSymlink != 0) { - linkName, err := filepath.EvalSymlinks(dir) - if err == nil { - vfLink := filepath.Base(linkName) - vfID := dirInfo.Name()[6:] - vfList[vfID] = vfLink - } + + // Read all VF directories and add VF PCI addr to the vfList + for _, vf := range vfs { + if id, link := vfData(vf); id != "" && link != "" { + vfList[id] = link } } + + if len(vfList) == 0 { + return vfList, fmt.Errorf("no virtual functions found for pf '%s'", dev) + } + return vfList, nil } -//getPFName resolves the system's name for a physical interface from the PCI address linked to it. +// vfData gets vf id and pci address from the path specified +func vfData(vfDir string) (string, string) { + if link, err := utils.EvalSymlinks(filepath.Join(*sysBusPci, vfDir)); err == nil { + return filepath.Base(vfDir)[6:], filepath.Base(link) + } else { + log.Printf("error evaluating symlink '%s'\n%v", vfDir, err) + return "", "" + } +} + +// getPFName resolves the system's name for a physical interface from the PCI address linked to it. func getPFName(device string) string { - pfdir, err := ioutil.ReadDir(filepath.Join(*sysBusPci, device, pfNameFile)) + pfDevPath := filepath.Join(device, pfNameFile) + pfdir, err := fs.ReadDir(devfs, pfDevPath) if err != nil || len(pfdir) == 0 { - log.Printf("Could not get name for pf: %v", err) + log.Printf("%s - could not get pf interface name in path '%s'\n%v", device, pfDevPath, err) return "" } + return pfdir[0].Name() } -//Describe isn't implemented for this collector -func (c sriovdevCollector) Describe(chan<- *prometheus.Desc) { +//isNetDevice checks if the device is a net device by checking its device class +func isNetDevice(filepath string) bool { + file, err := fs.ReadFile(devfs, filepath) + if err != nil { + return false + } + + classHex := strings.TrimSpace(string(file)) + deviceClass, err := strconv.ParseInt(classHex, 0, 64) + if err != nil { + log.Printf("could not parse class file: %v", err) + return false + } + + return deviceClass == netClass } -func ResolveSriovDevFilepaths() { - utils.ResolvePath(sysBusPci) - utils.ResolvePath(sysClassNet) +func resolveSriovDevFilepaths() error { + if err := utils.ResolveFlag("path.sysbuspci", sysBusPci); err != nil { + return err + } + + if err := utils.ResolveFlag("path.sysclassnet", sysClassNet); err != nil { + return err + } + + devfs = os.DirFS(*sysBusPci) + netfs = os.DirFS(*sysClassNet) + + return nil } diff --git a/collectors/sriovdev_readers.go b/collectors/sriovdev_readers.go index 295b92c..2d77f24 100644 --- a/collectors/sriovdev_readers.go +++ b/collectors/sriovdev_readers.go @@ -1,71 +1,81 @@ -//This file should contain different sriov stat implementations for different drivers and versions. +// This file should contain different sriov stat implementations for different drivers and versions. package collectors import ( "fmt" - "io/ioutil" + "io/fs" "log" "os" "path/filepath" - "sriov-network-metrics-exporter/pkg/vfstats" "strconv" "strings" + + "sriov-network-metrics-exporter/pkg/drvinfo" + "sriov-network-metrics-exporter/pkg/utils" + "sriov-network-metrics-exporter/pkg/vfstats" ) +const sriovVFStatsDir = "%s/device/sriov/%s/stats" + type sriovStats map[string]int64 -//sriovStatReader is an interface which takes in the Physical Function name and vf id and returns the stats for the VF +// sriovStatReader is an interface which takes in the Physical Function name and vf id and returns the stats for the VF type sriovStatReader interface { ReadStats(vfID string, pfName string) sriovStats } -//netlinkReader is able to read stats from drivers that support the netlink interface +// netlinkReader is able to read stats from drivers that support the netlink interface type netlinkReader struct { data vfstats.PerPF } -//statsReader is able to read stats from Physical Functions running the i40e or ice driver. -//other drivers that store all VF stats in files under one folder could use this reader. -type statsReader struct { +// sysfsReader is able to read stats from Physical Functions running the i40e or ice driver. +// Other drivers that store all VF stats in files under one folder could use this reader. +type sysfsReader struct { statsFS string } -//statReaderForPF returns the correct stat reader for the given PF -//currently only i40e and ice are implemented, but other drivers can be implemented and picked up here. -func statReaderForPF(pf string) sriovStatReader { - if *netlinkEnabled { - log.Printf("using netlink for %v", pf) - return netlinkReader{vfstats.VfStats(pf)} - } - pfDriverPath := filepath.Join(*sysClassNet, pf, "device", driverFile) - //driver type is found by getting the destination of the symbolic link on the driver path from /sys/bus/pci - driverInfo, err := os.Readlink(pfDriverPath) - if err != nil { - log.Printf("failed to get driver info: %v", err) - return nil - } - pfDriver := filepath.Base(driverInfo) - switch pfDriver { - case "i40e": - log.Printf("using %v reader for %v", pfDriver, pf) - return statsReader{filepath.Join(*sysClassNet, "/%s/device/sriov/%s/stats/")} - case "ice": - log.Printf("using %v reader for %v", pfDriver, pf) - return statsReader{filepath.Join(*sysClassNet, "/%sv%s/statistics/")} - default: - log.Printf("No stats reader available for Physical Function %v", pf) - return nil +// getStatsReader returns the correct stat reader for the given PF +// Currently only i40e and ice are implemented, but other drivers can be implemented and picked up here. +func getStatsReader(pf string, priority []string) sriovStatReader { + for _, collector := range priority { + switch collector { + case "sysfs": + if _, err := fs.Stat(netfs, filepath.Join(pf, "/device/sriov")); !os.IsNotExist(err) { + log.Printf("%s - using sysfs collector", pf) + return sysfsReader{filepath.Join(*sysClassNet, "%s/device/sriov/%s/stats/")} + } + + log.Printf("%s does not support sysfs collector, directory '%s' does not exist", pf, filepath.Join(pf, "/device/sriov")) + case "netlink": + info, err := drvinfo.GetDriverInfo(pf) + if err != nil { + log.Printf("failed to get driver info error %v", err) + return nil + } + + if supportedDrivers.IsDriverSupported(info) { + log.Printf("%s - using netlink collector", pf) + return netlinkReader{vfstats.VfStats(pf)} + } + + log.Printf("%s does not support netlink collector, %+v driver not supported", pf, info) + default: + log.Printf("%s - '%s' collector not supported", pf, collector) + } } + return nil } -//ReadStats takes in the name of a PF and the VF Id and returns a stats object. +// ReadStats takes in the name of a PF and the VF Id and returns a stats object. func (r netlinkReader) ReadStats(pfName string, vfID string) sriovStats { id, err := strconv.Atoi(vfID) if err != nil { - log.Print("Error reading passed Virtual Function ID") + log.Print("error reading passed virtual function id") return sriovStats{} } + return func() sriovStats { vf := r.data.Vfs[id] return map[string]int64{ @@ -81,31 +91,40 @@ func (r netlinkReader) ReadStats(pfName string, vfID string) sriovStats { }() } -func (r statsReader) ReadStats(pfName string, vfID string) sriovStats { +func (r sysfsReader) ReadStats(pfName string, vfID string) sriovStats { stats := make(sriovStats, 0) - statroot := fmt.Sprintf(r.statsFS, pfName, vfID) - files, err := ioutil.ReadDir(statroot) + + statDir := fmt.Sprintf(sriovVFStatsDir, pfName, vfID) + files, err := fs.ReadDir(netfs, statDir) if err != nil { + log.Printf("error reading stats for %s vf%s\n%v", pfName, vfID, err) return stats } - log.Printf("getting stats for pf %v %v", pfName, vfID) + + log.Printf("getting stats for %s vf%s", pfName, vfID) + for _, f := range files { - path := filepath.Join(statroot, f.Name()) - if isSymLink(path) { - log.Printf("error: cannot read symlink %v", path) + path := filepath.Join(statDir, f.Name()) + if utils.IsSymLink(netfs, path) { + log.Printf("could not stat file '%s'", path) continue } - statRaw, err := ioutil.ReadFile(path) + + statRaw, err := fs.ReadFile(netfs, path) if err != nil { + log.Printf("error reading file, %v", err) continue } + statString := strings.TrimSpace(string(statRaw)) value, err := strconv.ParseInt(statString, 10, 64) if err != nil { - log.Printf("Error reading file %v: %v", f.Name(), err) + log.Printf("%s - error parsing integer from value '%s'\n%v", f.Name(), statString, err) continue } + stats[f.Name()] = value } + return stats } diff --git a/collectors/sriovdev_readers_test.go b/collectors/sriovdev_readers_test.go new file mode 100644 index 0000000..df01da2 --- /dev/null +++ b/collectors/sriovdev_readers_test.go @@ -0,0 +1,115 @@ +package collectors + +import ( + "io/fs" + "sriov-network-metrics-exporter/pkg/drvinfo" + "sriov-network-metrics-exporter/pkg/vfstats" + "testing/fstest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = DescribeTable("test getting stats reader for pf", // getStatsReader + func(pf string, priority []string, fsys fs.FS, driver *drvinfo.DriverInfo, expected sriovStatReader, logs ...string) { + netfs = fsys + + // TODO: replace with ethtool mock + drvinfo.GetDriverInfo = func(name string) (*drvinfo.DriverInfo, error) { + return driver, nil + } + + // TODO: replace with fstest.MapFS entry + supportedDrivers = drvinfo.SupportedDrivers{Drivers: drvinfo.DriversList{Drivers: []drvinfo.DriverInfo{*driver}}, DbFilePath: ""} + + statsReader := getStatsReader(pf, priority) + + if expected != nil { + Expect(statsReader).To(Equal(expected)) + } else { + Expect(statsReader).To(BeNil()) + } + + assertLogs(logs) + }, + Entry("with sysfs support", + "ens785f0", + []string{"sysfs", "netlink"}, + fstest.MapFS{"ens785f0/device/sriov": {Mode: fs.ModeDir}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + sysfsReader{"/sys/class/net/%s/device/sriov/%s/stats"}, + "ens785f0 - using sysfs collector"), + Entry("without sysfs support", + "ens785f0", + []string{"sysfs", "netlink"}, + fstest.MapFS{}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + netlinkReader{vfstats.VfStats("ens785f0")}, + "ens785f0 does not support sysfs collector", + "ens785f0 - using netlink collector"), + Entry("without any collector support", + "ens785f0", + []string{"unsupported_collector"}, + fstest.MapFS{}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + nil, + "ens785f0 - 'unsupported_collector' collector not supported"), +) + +var _ = DescribeTable("test getting reading stats through sriov sysfs interface", // sysfsReader.ReadStats + func(pf string, vfId string, fsys fs.FS, expected sriovStats, logs ...string) { + netfs = fsys + + statsReader := new(sysfsReader) + stats := statsReader.ReadStats(pf, vfId) + Expect(stats).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("with stats files", + "ens785f0", + "0", + fstest.MapFS{ + "ens785f0/device/sriov/0/stats/rx_packets": {Data: []byte("6")}, + "ens785f0/device/sriov/0/stats/rx_bytes": {Data: []byte("24")}, + "ens785f0/device/sriov/0/stats/tx_packets": {Data: []byte("12")}, + "ens785f0/device/sriov/0/stats/tx_bytes": {Data: []byte("48")}}, + map[string]int64{ + "rx_packets": 6, + "rx_bytes": 24, + "tx_packets": 12, + "tx_bytes": 48}, + "getting stats for ens785f0 vf0"), + Entry("without stats files", + "ens785f0", + "0", + fstest.MapFS{}, + map[string]int64{}, + "error reading stats for ens785f0 vf0", + "open ens785f0/device/sriov/0/stats: file does not exist"), + Entry("with stat file as a symlink", + "ens785f0", + "0", + fstest.MapFS{ + "ens785f0/device/sriov/0/stats/rx_packets": {Mode: fs.ModeSymlink}}, + map[string]int64{}, + "getting stats for ens785f0 vf0", + "could not stat file 'ens785f0/device/sriov/0/stats/rx_packets'"), + Entry("with stat file as a directory", + "ens785f0", + "0", + fstest.MapFS{ + "ens785f0/device/sriov/0/stats/rx_packets": {Mode: fs.ModeDir}}, + map[string]int64{}, + "getting stats for ens785f0 vf0", + "error reading file, read ens785f0/device/sriov/0/stats/rx_packets: invalid argument"), + Entry("with invalid stat file", + "ens785f0", + "0", + fstest.MapFS{ + "ens785f0/device/sriov/0/stats/rx_packets": {Data: []byte("NaN")}}, + map[string]int64{}, + "getting stats for ens785f0 vf0", + "rx_packets - error parsing integer from value 'NaN'", + "strconv.ParseInt: parsing \"NaN\": invalid syntax"), +) diff --git a/collectors/sriovdev_test.go b/collectors/sriovdev_test.go new file mode 100644 index 0000000..d2ae693 --- /dev/null +++ b/collectors/sriovdev_test.go @@ -0,0 +1,462 @@ +package collectors + +import ( + "fmt" + "io/fs" + "net" + "sriov-network-metrics-exporter/pkg/drvinfo" + "sriov-network-metrics-exporter/pkg/vfstats" + "testing/fstest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/vishvananda/netlink" +) + +var _ = AfterEach(func() { + vfstats.GetLink = netlink.LinkByName +}) + +var _ = DescribeTable("test vf stats collection", // Collect + func(priority []string, fsys fs.FS, driver *drvinfo.DriverInfo, link netlink.Device, expected []metric, logs ...string) { + devfs = fsys + netfs = fsys + collectorPriority = priority + + // TODO: replace with ethtool mock + drvinfo.GetDriverInfo = func(name string) (*drvinfo.DriverInfo, error) { + return driver, nil + } + + // TODO: replace with fstest.MapFS entry + supportedDrivers = drvinfo.SupportedDrivers{Drivers: drvinfo.DriversList{Drivers: []drvinfo.DriverInfo{*driver}}, DbFilePath: ""} + + vfstats.GetLink = func(name string) (netlink.Link, error) { + return &link, nil + } + + ch := make(chan prometheus.Metric, 1) + go createSriovDevCollector().Collect(ch) + + for i := 0; i < len(expected); i++ { + m := dto.Metric{} + err := (<-ch).Write(&m) + Expect(err).ToNot(HaveOccurred()) + + labels := make(map[string]string, 4) + for _, label := range m.Label { + labels[*label.Name] = *label.Value + } + + metric := metric{labels: labels, counter: *m.Counter.Value} + + Expect(metric).To(BeElementOf(expected)) + } + + assertLogs(logs) + }, + Entry("with only sysfs", + []string{"sysfs"}, + fstest.MapFS{ + "0000:1d:00.0/sriov_totalvfs": {Data: []byte("64")}, + "0000:1d:00.0/net/t_ens785f0": {Mode: fs.ModeDir}, + "0000:1d:00.0/numa_node": {Data: []byte("0")}, + "0000:1d:00.0/class": {Data: []byte("0x020000")}, + "0000:1d:00.0/virtfn0": {Data: []byte("/sys/devices/0000:1d:01.0"), Mode: fs.ModeSymlink}, + "0000:1d:00.0/virtfn1": {Data: []byte("/sys/devices/0000:1d:01.1"), Mode: fs.ModeSymlink}, + "t_ens785f0/device/sriov/0/stats/rx_packets": {Data: []byte("4")}, + "t_ens785f0/device/sriov/0/stats/tx_packets": {Data: []byte("8")}, + "t_ens785f0/device/sriov/1/stats/rx_packets": {Data: []byte("16")}, + "t_ens785f0/device/sriov/1/stats/tx_packets": {Data: []byte("32")}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + nil, + []metric{ + {map[string]string{"numa_node": "0", "pciAddr": "0000:1d:01.0", "pf": "t_ens785f0", "vf": "0"}, 4}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:1d:01.0", "pf": "t_ens785f0", "vf": "0"}, 8}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:1d:01.1", "pf": "t_ens785f0", "vf": "1"}, 16}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:1d:01.1", "pf": "t_ens785f0", "vf": "1"}, 32}}, + "collecting sr-iov device metrics", + "collector priority: \\[sysfs\\]", + "t_ens785f0 - using sysfs collector", + "getting stats for t_ens785f0 vf\\d", + "getting stats for t_ens785f0 vf\\d"), + Entry("with only netlink", + []string{"netlink"}, + fstest.MapFS{ + "0000:2e:00.0/sriov_totalvfs": {Data: []byte("64")}, + "0000:2e:00.0/net/t_ens801f0": {Mode: fs.ModeDir}, + "0000:2e:00.0/numa_node": {Data: []byte("0")}, + "0000:2e:00.0/class": {Data: []byte("0x020000")}, + "0000:2e:00.0/virtfn0": {Data: []byte("/sys/devices/0000:2e:01.0"), Mode: fs.ModeSymlink}, + "0000:2e:00.0/virtfn1": {Data: []byte("/sys/devices/0000:2e:01.1"), Mode: fs.ModeSymlink}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + netlink.Device{LinkAttrs: netlink.LinkAttrs{Vfs: []netlink.VfInfo{ + {0, net.HardwareAddr{}, 0, 0, 0, true, 0, 0, 0, 11, 12, 13, 14, 15, 16, 17, 18, 0, 0}, //nolint:govet + {1, net.HardwareAddr{}, 0, 0, 0, true, 0, 0, 0, 21, 22, 23, 24, 25, 26, 27, 28, 0, 0}}}}, //nolint:govet + []metric{ + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 11}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 12}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 13}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 14}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 15}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 16}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 17}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.0", "pf": "t_ens801f0", "vf": "0"}, 18}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 21}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 22}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 23}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 24}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 25}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 26}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 27}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:2e:01.1", "pf": "t_ens801f0", "vf": "1"}, 28}}, + "collecting sr-iov device metrics", + "collector priority: \\[netlink\\]", + "t_ens801f0 - using netlink collector"), + Entry("with both sysfs and netlink", + []string{"sysfs", "netlink"}, + fstest.MapFS{ + "0000:3f:00.0/sriov_totalvfs": {Data: []byte("64")}, + "0000:3f:00.0/net/t_ens785f0": {Mode: fs.ModeDir}, + "0000:3f:00.0/numa_node": {Data: []byte("0")}, + "0000:3f:00.0/class": {Data: []byte("0x020000")}, + "0000:3f:00.0/virtfn0": {Data: []byte("/sys/devices/0000:3f:01.0"), Mode: fs.ModeSymlink}, + "t_ens785f0/device/sriov/0/stats/rx_packets": {Data: []byte("4")}, + "t_ens785f0/device/sriov/0/stats/tx_packets": {Data: []byte("8")}, + "0000:4g:00.0/sriov_totalvfs": {Data: []byte("128")}, + "0000:4g:00.0/net/t_ens801f0": {Mode: fs.ModeDir}, + "0000:4g:00.0/numa_node": {Data: []byte("0")}, + "0000:4g:00.0/class": {Data: []byte("0x020000")}, + "0000:4g:00.0/virtfn0": {Data: []byte("/sys/devices/0000:4g:01.0"), Mode: fs.ModeSymlink}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + netlink.Device{LinkAttrs: netlink.LinkAttrs{Vfs: []netlink.VfInfo{ + {0, net.HardwareAddr{}, 0, 0, 0, true, 0, 0, 0, 31, 32, 33, 34, 35, 36, 37, 38, 0, 0}}}}, //nolint:govet + []metric{ + {map[string]string{"numa_node": "0", "pciAddr": "0000:3f:01.0", "pf": "t_ens785f0", "vf": "0"}, 4}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:3f:01.0", "pf": "t_ens785f0", "vf": "0"}, 8}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 31}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 32}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 33}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 34}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 35}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 36}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 37}, + {map[string]string{"numa_node": "0", "pciAddr": "0000:4g:01.0", "pf": "t_ens801f0", "vf": "0"}, 38}}, + "collecting sr-iov device metrics", + "collector priority: \\[sysfs netlink\\]", + "t_ens785f0 - using sysfs collector", + "getting stats for t_ens785f0 vf\\d"), + + // These logs are expected, but were causing instability in this test case, removed for now + // "t_ens801f0 does not support sysfs collector, directory 't_ens801f0/device/sriov' does not exist", + // "t_ens801f0 - using netlink collector", +) + +var _ = DescribeTable("test creating sriovDev collector", // createSriovDevCollector + func(fsys fs.FS, expected sriovDevCollector, logs ...string) { + devfs = fsys + + collector := createSriovDevCollector() + Expect(collector).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("only sriov net devices", + fstest.MapFS{ + "0000:1a:00.0/sriov_totalvfs": {Data: []byte("64")}, + "0000:1a:00.0/numa_node": {Data: []byte("1")}, + "0000:1a:00.0/class": {Data: []byte("0x020000")}, + "0000:1a:00.1/sriov_totalvfs": {Data: []byte("64")}, + "0000:1a:00.1/numa_node": {Data: []byte("1")}, + "0000:1a:00.1/class": {Data: []byte("0x020000")}, + "0000:2b:00.0/sriov_totalvfs": {Data: []byte("128")}, + "0000:2b:00.0/numa_node": {Data: []byte("2")}, + "0000:2b:00.0/class": {Data: []byte("0x020000")}, + "0000:2b:00.1/sriov_totalvfs": {Data: []byte("128")}, + "0000:2b:00.1/numa_node": {Data: []byte("2")}, + "0000:2b:00.1/class": {Data: []byte("0x020000")}}, + sriovDevCollector{ + "vfstats", + map[string]string{"0000:1a:00.0": "1", "0000:1a:00.1": "1", "0000:2b:00.0": "2", "0000:2b:00.1": "2"}}), + Entry("mixed devices", + fstest.MapFS{ + "0000:3c:00.0/sriov_totalvfs": {Data: []byte("63")}, + "0000:3c:00.0/numa_node": {Data: []byte("1")}, + "0000:3c:00.0/class": {Data: []byte("0x020000")}, + "0000:3c:00.1/sriov_totalvfs": {Data: []byte("63")}, + "0000:3c:00.1/numa_node": {Data: []byte("1")}, + "0000:3c:00.1/class": {Data: []byte("0x020000")}, + "0000:4d:00.0/sriov_totalvfs": {Data: []byte("64")}, + "0000:4d:00.0/numa_node": {Data: []byte("-1")}, + "0000:4d:00.0/class": {Data: []byte("0x020000")}, + "0000:4d:00.1/sriov_totalvfs": {Data: []byte("64")}, + "0000:4d:00.1/numa_node": {Data: []byte("-1")}, + "0000:4d:00.1/class": {Data: []byte("0x020000")}}, + sriovDevCollector{ + "vfstats", + map[string]string{"0000:3c:00.0": "1", "0000:3c:00.1": "1", "0000:4d:00.0": "", "0000:4d:00.1": ""}}, + "no numa node information for device '0000:4d:00.0'", + "no numa node information for device '0000:4d:00.1'"), + Entry("no sriov net devices", + fstest.MapFS{ + "0000:5e:00.0/": {Mode: fs.ModeDir}, + "0000:5e:00.1/": {Mode: fs.ModeDir}, + "0000:5e:00.2/": {Mode: fs.ModeDir}, + "0000:5e:00.3/": {Mode: fs.ModeDir}}, + sriovDevCollector{ + "vfstats", + map[string]string{}}, + "no sriov net devices found"), +) + +var _ = DescribeTable("test getting sriov devices from filesystem", // getSriovDevAddrs + func(fsys fs.FS, driver *drvinfo.DriverInfo, expected []string, logs ...string) { + devfs = fsys + + // TODO: replace with ethtool mock + drvinfo.GetDriverInfo = func(name string) (*drvinfo.DriverInfo, error) { + return driver, nil + } + + // TODO: replace with fstest.MapFS entry + supportedDrivers = drvinfo.SupportedDrivers{Drivers: drvinfo.DriversList{Drivers: []drvinfo.DriverInfo{*driver}}, DbFilePath: ""} + + devs := getSriovDevAddrs() + Expect(devs).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("only sriov net devices", + fstest.MapFS{ + "0000:6f:00.0/sriov_totalvfs": {Data: []byte("64")}, "0000:6f:00.0/class": {Data: []byte("0x020000")}, + "0000:6f:00.1/sriov_totalvfs": {Data: []byte("64")}, "0000:6f:00.1/class": {Data: []byte("0x020000")}, + "0000:7g:00.0/sriov_totalvfs": {Data: []byte("128")}, "0000:7g:00.0/class": {Data: []byte("0x020000")}, + "0000:7g:00.1/sriov_totalvfs": {Data: []byte("128")}, "0000:7g:00.1/class": {Data: []byte("0x020000")}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + []string{"0000:6f:00.0", "0000:6f:00.1", "0000:7g:00.0", "0000:7g:00.1"}), + Entry("mixed devices", + fstest.MapFS{ + "0000:8h:00.0/": {Mode: fs.ModeDir}, + "0000:8h:00.1/": {Mode: fs.ModeDir}, + "0000:9i:00.0/sriov_totalvfs": {Data: []byte("63")}, "0000:9i:00.0/class": {Data: []byte("0x020000")}, + "0000:9i:00.1/sriov_totalvfs": {Data: []byte("63")}, "0000:9i:00.1/class": {Data: []byte("0x020000")}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + []string{"0000:9i:00.0", "0000:9i:00.1"}), + Entry("no sriov net devices", + fstest.MapFS{ + "0000:1b:00.0/": {Mode: fs.ModeDir}, + "0000:1b:00.1/": {Mode: fs.ModeDir}, + "0000:1b:00.2/": {Mode: fs.ModeDir}, + "0000:1b:00.3/": {Mode: fs.ModeDir}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + []string{}, + "no sriov net devices found"), +) + +var _ = DescribeTable("test getting sriov dev details", // getSriovDev + func(dev string, priority []string, fsys fs.FS, driver *drvinfo.DriverInfo, expected sriovDev, logs ...string) { + devfs = fsys + netfs = fsys + + // TODO: replace with ethtool mock + drvinfo.GetDriverInfo = func(name string) (*drvinfo.DriverInfo, error) { + return driver, nil + } + + // TODO: replace with fstest.MapFS entry + supportedDrivers = drvinfo.SupportedDrivers{Drivers: drvinfo.DriversList{Drivers: []drvinfo.DriverInfo{*driver}}, DbFilePath: ""} + + sriovDev := getSriovDev(dev, priority) + Expect(sriovDev).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("with sysfs support", + "0000:4f:00.0", + []string{"sysfs", "netlink"}, + fstest.MapFS{ + "0000:4f:00.0/net/ens785f0": {Mode: fs.ModeDir}, + "0000:4f:00.0/virtfn0": {Data: []byte("/sys/devices/0000:4f:01.0"), Mode: fs.ModeSymlink}, + "0000:4f:00.0/virtfn1": {Data: []byte("/sys/devices/0000:4f:01.1"), Mode: fs.ModeSymlink}, + "ens785f0/device/sriov": {Mode: fs.ModeDir}, // Added to enable sysfs reader + "0000:5g:00.0/net/ens801f0": {Mode: fs.ModeDir}, + "0000:5g:00.0/virtfn0": {Data: []byte("/sys/devices/0000:5g:01.0"), Mode: fs.ModeSymlink}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + sriovDev{ + "ens785f0", + sysfsReader{"/sys/class/net/%s/device/sriov/%s/stats"}, + map[string]string{"0": "0000:4f:01.0", "1": "0000:4f:01.1"}}, + "ens785f0 - using sysfs collector"), + Entry("without sysfs support", + "0000:6h:00.0", + []string{"sysfs", "netlink"}, + fstest.MapFS{ + "0000:6h:00.0/net/ens785f0": {Mode: fs.ModeDir}, + "0000:6h:00.0/virtfn0": {Data: []byte("/sys/devices/0000:6h:01.0"), Mode: fs.ModeSymlink}, + "0000:6h:00.0/virtfn1": {Data: []byte("/sys/devices/0000:6h:01.1"), Mode: fs.ModeSymlink}, + "0000:7i:00.0/net/ens801f0": {Mode: fs.ModeDir}, + "0000:7i:00.0/virtfn0": {Data: []byte("/sys/devices/0000:7i:01.0"), Mode: fs.ModeSymlink}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + sriovDev{ + "ens785f0", + netlinkReader{vfstats.VfStats("ens785f0")}, + map[string]string{"0": "0000:6h:01.0", "1": "0000:6h:01.1"}}, + "ens785f0 does not support sysfs collector", + "ens785f0 - using netlink collector"), + Entry("without any collector support", + "0000:8j:00.0", + []string{"unsupported_collector"}, + fstest.MapFS{ + "0000:8j:00.0/net/ens785f0": {Mode: fs.ModeDir}, + "0000:8j:00.0/virtfn0": {Data: []byte("/sys/devices/0000:8j:01.0"), Mode: fs.ModeSymlink}, + "0000:8j:00.0/virtfn1": {Data: []byte("/sys/devices/0000:8j:01.1"), Mode: fs.ModeSymlink}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + sriovDev{ + "ens785f0", + nil, + map[string]string{"0": "0000:8j:01.0", "1": "0000:8j:01.1"}}, + "ens785f0 - 'unsupported_collector' collector not supported"), + Entry("without any virtual functions", + "0000:9k:00.0", + []string{"sysfs"}, + fstest.MapFS{ + "0000:9k:00.0/net/ens785f0": {Mode: fs.ModeDir}}, + &drvinfo.DriverInfo{Name: "ice", Version: "1.9.11"}, + sriovDev{ + "ens785f0", + nil, + map[string]string{}}, + "error getting vf address", + "no virtual functions found for pf '0000:9k:00.0'", + "ens785f0 does not support sysfs collector"), +) + +var _ = DescribeTable("test getting numa node information for devices from filesystem", // getNumaNodes // TODO: ensure map order + func(devices []string, fsys fs.FS, expected map[string]string, logs ...string) { + devfs = fsys + + numaNodes := getNumaNodes(devices) + Expect(numaNodes).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("only sriov net devices", + []string{"0000:2c:00.0", "0000:2c:00.1", "0000:3d:00.0", "0000:3d:00.1"}, + fstest.MapFS{ + "0000:2c:00.0/numa_node": {Data: []byte("0")}, + "0000:2c:00.1/numa_node": {Data: []byte("0")}, + "0000:3d:00.0/numa_node": {Data: []byte("1")}, + "0000:3d:00.1/numa_node": {Data: []byte("1")}}, + map[string]string{"0000:2c:00.0": "0", "0000:2c:00.1": "0", "0000:3d:00.0": "1", "0000:3d:00.1": "1"}), + Entry("mixed devices", + []string{"0000:4e:00.0", "0000:4e:00.1", "0000:5f:00.0", "0000:5f:00.1"}, + fstest.MapFS{ + "0000:4e:00.0/": {Mode: fs.ModeDir}, + "0000:4e:00.1/": {Mode: fs.ModeDir}, + "0000:5f:00.0/numa_node": {Data: []byte("-1")}, + "0000:5f:00.1/numa_node": {Data: []byte("-1")}}, + map[string]string{"0000:4e:00.0": "", "0000:4e:00.1": "", "0000:5f:00.0": "", "0000:5f:00.1": ""}, + "could not read numa_node file for device '0000:4e:00.0'", + "open 0000:4e:00.0/numa_node: file does not exist", + "could not read numa_node file for device '0000:4e:00.1'", + "open 0000:4e:00.1/numa_node: file does not exist", + "no numa node information for device '0000:5f:00.0'", + "no numa node information for device '0000:5f:00.1'"), + Entry("no sriov net devices", + []string{"0000:6g:00.0", "0000:6g:00.1", "0000:6g:00.2", "0000:6g:00.3"}, + fstest.MapFS{ + "0000:6g:00.0/": {Mode: fs.ModeDir}, + "0000:6g:00.1/": {Mode: fs.ModeDir}, + "0000:6g:00.2/": {Mode: fs.ModeDir}, + "0000:6g:00.3/": {Mode: fs.ModeDir}}, + map[string]string{"0000:6g:00.0": "", "0000:6g:00.1": "", "0000:6g:00.2": "", "0000:6g:00.3": ""}, + "could not read numa_node file for device '0000:6g:00.0'", + "open 0000:6g:00.0/numa_node: file does not exist", + "could not read numa_node file for device '0000:6g:00.1'", + "open 0000:6g:00.1/numa_node: file does not exist", + "could not read numa_node file for device '0000:6g:00.2'", + "open 0000:6g:00.2/numa_node: file does not exist", + "could not read numa_node file for device '0000:6g:00.3'", + "open 0000:6g:00.3/numa_node: file does not exist"), +) + +var _ = DescribeTable("test getting vf information for devices from filesystem", // vfList + func(dev string, fsys fs.FS, expected vfsPCIAddr, err error, logs ...string) { + devfs = fsys + + vfs, e := vfList(dev) + Expect(vfs).To(Equal(expected)) + + if err != nil { + Expect(e).Should(MatchError(err)) + } + + assertLogs(logs) + }, + Entry("only retrieve vf information for specified sriov net device", + "0000:7h:00.0", + fstest.MapFS{ + "0000:7h:00.0/virtfn0": {Data: []byte("/sys/devices/0000:7h:01.0"), Mode: fs.ModeSymlink}, + "0000:7h:00.0/virtfn1": {Data: []byte("/sys/devices/0000:7h:01.1"), Mode: fs.ModeSymlink}, + "0000:8i:00.0/virtfn0": {Data: []byte("/sys/devices/0000:8i:01.0"), Mode: fs.ModeSymlink}}, + map[string]string{"0": "0000:7h:01.0", "1": "0000:7h:01.1"}, + nil), + Entry("vf file is not a symlink for specified sriov net device", + "0000:9j:00.0", + fstest.MapFS{ + "0000:9j:00.0/virtfn0": {Data: []byte("/sys/devices/0000:9j:01.0"), Mode: fs.ModeDir}}, + map[string]string{}, + fmt.Errorf("no virtual functions found for pf '0000:9j:00.0'"), + "error evaluating symlink '0000:9j:00.0/virtfn0'"), + Entry("vf file does not exist for specified sriov net device", + "0000:1c:00.0", + fstest.MapFS{}, + map[string]string{}, + fmt.Errorf("no virtual functions found for pf '0000:1c:00.0'")), +) + +var _ = DescribeTable("test getting vf data from filesystem", // vfData + func(vfDir string, fsys fs.FS, expectedVfId string, expectedVfPciAddr string, logs ...string) { + devfs = fsys + + vfId, vfPci := vfData(vfDir) + Expect(vfId).To(Equal(expectedVfId)) + Expect(vfPci).To(Equal(expectedVfPciAddr)) + + assertLogs(logs) + }, + Entry("valid symlink", + "0000:7h:00.0/virtfn0", + fstest.MapFS{"0000:7h:00.0/virtfn0": {Data: []byte("/sys/devices/0000:7h:01.0"), Mode: fs.ModeSymlink}}, + "0", + "0000:7h:01.0"), + Entry("invalid symlink", + "0000:8i:00.0/virtfn0", + fstest.MapFS{"0000:8i:00.0/virtfn0": {Mode: fs.ModeDir}}, + "", + "", + "error evaluating symlink '0000:8i:00.0/virtfn0'"), +) + +var _ = DescribeTable("test getting pf name from pci address on filesystem", // getPFName + func(dev string, fsys fs.FS, expected string, logs ...string) { + devfs = fsys + + pfName := getPFName(dev) + Expect(pfName).To(Equal(expected)) + + assertLogs(logs) + }, + Entry("pf exists", + "0000:2d:00.0", + fstest.MapFS{"0000:2d:00.0/net/ens785f0": {Mode: fs.ModeDir}}, + "ens785f0"), + Entry("pf does not exist", + "0000:3e:00.0", + fstest.MapFS{}, + "", + "0000:3e:00.0 - could not get pf interface name in path '0000:3e:00.0/net'", + "open 0000:3e:00.0/net: file does not exist"), +) diff --git a/deployment/daemonset.yaml b/deployment/daemonset.yaml index dfa9499..327e61e 100644 --- a/deployment/daemonset.yaml +++ b/deployment/daemonset.yaml @@ -1,3 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: sriov-network-metrics-exporter + namespace: monitoring +data: + drivers.yaml: |- + drivers: + - name: ice + version: 1.9.11 + - name: mlx5_core + version: 5.15.0-53-generic +--- apiVersion: apps/v1 kind: DaemonSet metadata: @@ -17,14 +30,16 @@ spec: app.kubernetes.io/name: sriov-metrics-exporter app.kubernetes.io/version: v0.0.1 spec: + hostNetwork: true containers: - args: - --path.kubecgroup=/host/kubecgroup - --path.sysbuspci=/host/sys/bus/pci/devices/ - - --path.sysclassnet=/host/sys/class/net + - --path.sysclassnet=/host/sys/class/net/ - --path.cpucheckpoint=/host/cpu_manager_state - - --path.kubeletSocket=/host/kubelet.sock + - --path.kubeletsocket=/host/kubelet.sock - --collector.kubepoddevice=true + - --collector.vfstatspriority=sysfs,netlink image: localhost:5000/sriov-metrics-exporter imagePullPolicy: Always name: sriov-metrics-exporter @@ -59,8 +74,11 @@ spec: - mountPath: /host/cpu_manager_state name: cpucheckpoint readOnly: true + - name: sriov-network-metrics-exporter + mountPath: /etc/sriov-network-metrics-exporter nodeSelector: kubernetes.io/os: linux + feature.node.kubernetes.io/network-sriov.capable: "true" restartPolicy: Always tolerations: - operator: Exists @@ -89,6 +107,9 @@ spec: path: /sys/devices type: "Directory" name: sysdevices + - name: sriov-network-metrics-exporter + configMap: + name: sriov-network-metrics-exporter --- apiVersion: v1 kind: Service diff --git a/go.mod b/go.mod index 94f2e10..20b0587 100644 --- a/go.mod +++ b/go.mod @@ -1,40 +1,35 @@ module sriov-network-metrics-exporter -go 1.15 +go 1.18 require ( - github.com/prometheus/client_golang v1.11.1 + github.com/hashicorp/go-version v1.6.0 + github.com/onsi/ginkgo/v2 v2.5.1 + github.com/onsi/gomega v1.24.0 + github.com/prometheus/client_golang v1.12.1 + github.com/prometheus/client_model v0.2.0 + github.com/safchain/ethtool v0.2.0 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 - golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/genproto v0.0.0-20200420144010-e5e8543f8aeb // indirect - google.golang.org/grpc v1.28.1 // indirect - k8s.io/api v1.17.1 // indirect - k8s.io/apimachinery v0.18.2 // indirect - k8s.io/kubernetes v1.18.2 + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 + google.golang.org/grpc v1.52.0 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/kubelet v0.25.5 ) -replace ( - k8s.io/api => k8s.io/api v0.17.1 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.17.1 - k8s.io/apimachinery => k8s.io/apimachinery v0.17.1 - k8s.io/apiserver => k8s.io/apiserver v0.17.1 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.17.1 - k8s.io/client-go => k8s.io/client-go v0.17.1 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.17.1 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.17.1 - k8s.io/code-generator => k8s.io/code-generator v0.17.1 - k8s.io/component-base => k8s.io/component-base v0.17.1 - k8s.io/cri-api => k8s.io/cri-api v0.17.1 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.17.1 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.17.1 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.17.1 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.17.1 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.17.1 - k8s.io/kubectl => k8s.io/kubectl v0.17.1 - k8s.io/kubelet => k8s.io/kubelet v0.17.1 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.17.1 - k8s.io/metrics => k8s.io/metrics v0.17.1 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.17.1 +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect + google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/go.sum b/go.sum index 4822d6b..cf8f2b3 100644 --- a/go.sum +++ b/go.sum @@ -1,433 +1,179 @@ -bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= -github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +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/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/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= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= -github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/hcsshim v0.0.0-20190417211021-672e52e9209d/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= -github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= 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/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM= -github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/bazelbuild/bazel-gazelle v0.18.2/go.mod h1:D0ehMSbS+vesFsLGiD6JXu3mVEzOlfUl8wNnq+x/9p0= -github.com/bazelbuild/bazel-gazelle v0.19.1-0.20191105222053-70208cbdc798/go.mod h1:rPwzNHUqEzngx1iVBfO/2X2npKaT3tqPqqHW6rVsn/A= -github.com/bazelbuild/buildtools v0.0.0-20190731111112-f720930ceb60/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU= -github.com/bazelbuild/buildtools v0.0.0-20190917191645-69366ca98f89/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU= -github.com/bazelbuild/rules_go v0.0.0-20190719190356-6dae44dc5cab/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/checkpoint-restore/go-criu v0.0.0-20181120144056-17b0214f6c48/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho= -github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/cilium/ebpf v0.0.0-20191025125908-95b36a581eed/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= -github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= -github.com/containerd/console v0.0.0-20170925154832-84eeaae905fa/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/containerd v1.0.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/coredns/corefile-migration v1.0.6/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libnetwork v0.8.0-dev.2.0.20190925143933-c8a5fca4a652/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 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= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= -github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= +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-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= 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-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= 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.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +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= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/go-tools v0.0.0-20190318055746-e32c54105b7c/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.18.0/go.mod h1:kaqo8l0OZKYPtjNmG4z4HrWLgcYNIJ9B9q3LWri9uLg= -github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= -github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= -github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/cadvisor v0.35.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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 v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= -github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +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/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA= -github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= -github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk= -github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao= -github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= -github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mesos/mesos-go v0.0.9/go.mod h1:kPYCMQ9gsOXVAle1OsoY4I1+9kPu8GHkf88aV59fDr4= -github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= -github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= -github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 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-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -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/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= -github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.3.1-0.20190929122143-5215b1806f52/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= +github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/pquerna/ffjson v0.0.0-20180717144149-af8b230fcd20/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 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.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 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= @@ -435,377 +181,329 @@ github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 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 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= -github.com/quobyte/api v0.1.2/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= -github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/safchain/ethtool v0.2.0 h1:dILxMBqDnQfX192cCAPjZr9v2IgVXeElHPy435Z/IdE= +github.com/safchain/ethtool v0.2.0/go.mod h1:WkKB1DnNtvsMlDmQ50sgwowDJV/hGbJSOvJoEXs1AJQ= 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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= -github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -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.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= -github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190927031335-2835ba2e683f/go.mod h1:fYw7AShPAhGMdXqA9gRadk/CcMsvLlClpE5oBwnS3dM= -golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +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= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/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= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 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= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/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-20190321052220-f7bb7a8bee54/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-20190502175342-a43fa875dd82/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-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/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-20191001151750-bb3f8db39f24/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= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190122202912-9c309ee22fab/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 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= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200420144010-e5e8543f8aeb h1:nAFaltAMbNVA0rixtwvdnqgSVLX3HFUUvMkEklmzbYM= -google.golang.org/genproto v0.0.0-20200420144010-e5e8543f8aeb/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= 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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.1 h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k= -google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.17.1 h1:i46MidoDOE9tvQ0TTEYggf3ka/pziP1+tHI/GFVeJao= -k8s.io/api v0.17.1/go.mod h1:zxiAc5y8Ngn4fmhWUtSxuUlkfz1ixT7j9wESokELzOg= -k8s.io/apiextensions-apiserver v0.17.1/go.mod h1:DRIFH5x3jalE4rE7JP0MQKby9zdYk9lUJQuMmp+M/L0= -k8s.io/apimachinery v0.17.1 h1:zUjS3szTxoUjTDYNvdFkYt2uMEXLcthcbp+7uZvWhYM= -k8s.io/apimachinery v0.17.1/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apiserver v0.17.1/go.mod h1:BQEUObJv8H6ZYO7DeKI5vb50tjk6paRJ4ZhSyJsiSco= -k8s.io/cli-runtime v0.17.1/go.mod h1:e5847Iy85W9uWH3rZofXTG/9nOUyGKGTVnObYF7zSik= -k8s.io/client-go v0.17.1/go.mod h1:HZtHJSC/VuSHcETN9QA5QDZky1tXiYrkF/7t7vRpO1A= -k8s.io/cloud-provider v0.17.1/go.mod h1:QM00lVsYDC7gfXmrSCmiVVmRNk6zE8ciiuqskXDsjMM= -k8s.io/cluster-bootstrap v0.17.1/go.mod h1:bp4yDMvUBdGyYJoT2mLAb+WGgkouUanvrEyWEu7mJes= -k8s.io/code-generator v0.17.1/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= -k8s.io/component-base v0.17.1/go.mod h1:LrBPZkXtlvGjBzDJa0+b7E5Ij4VoAAKrOGudRC5z2eY= -k8s.io/cri-api v0.17.1/go.mod h1:BzAkbBHHp81d+aXzbiIcUbilLkbXa40B8mUHOk6EX3s= -k8s.io/csi-translation-lib v0.17.1/go.mod h1:EWeHQJcexqar6avuUocMwEJOYkboteNM9ODXa3qoamc= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-aggregator v0.17.1/go.mod h1:H5LcB3fx+P1gpowuZpzDu5B1XfABdO7JBKyB9J9bt34= -k8s.io/kube-controller-manager v0.17.1/go.mod h1:+jsQDMuaZzr0e2m5TMuSIz7jR0JlYCqfsCOiOr5h3ck= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-proxy v0.17.1/go.mod h1:Vz/TedeV9dMIBDTQ5FsmRLF+swQlKtVSvX394nnnCEg= -k8s.io/kube-scheduler v0.17.1/go.mod h1:vJfxYakLPXeFwnDhiDdNVBqVcICfuGTrDTcXxE81ut4= -k8s.io/kubectl v0.17.1/go.mod h1:ZmbAdEQm+SLA/3s3eWJ3g+liXb5eT6mA85jYj52LMXw= -k8s.io/kubelet v0.17.1/go.mod h1:0gzJqZbPCBik9aHwpu4SE0J2QhUQkdsoxqllG2FEZ4Y= -k8s.io/kubernetes v1.18.2 h1:37sJPq6p+gx5hEHQSwCWXIiXDc9AajzV1A5UrswnDq0= -k8s.io/kubernetes v1.18.2/go.mod h1:z8xjOOO1Ljz+TaHpOxVGC7cxtF32TesIamoQ+BZrVS0= -k8s.io/legacy-cloud-providers v0.17.1/go.mod h1:AWMb5OLBTn+K1jrW1bRTa8aXM6L66OnBG1+4wQEfqOM= -k8s.io/metrics v0.17.1/go.mod h1:dphDhzjA1KR/nQXtXEQzoQyQXk5ViSJO85Ky8QKwBPM= -k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/CEyOJ8= -k8s.io/sample-apiserver v0.17.1/go.mod h1:/aPEYThypAkyvvGXdRUUU69J1Ys5ITY8lgNc7bPKdbI= -k8s.io/system-validators v1.0.4/go.mod h1:HgSgTg4NAGNoYYjKsUyk52gdNi2PVDswQ9Iyn66R7NI= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= -vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= +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/kubelet v0.25.5 h1:2c2qDGQ49cuX4oLF8nEEGHzHovAOxPZOO7iEV868lbc= +k8s.io/kubelet v0.25.5/go.mod h1:8QDPQLg9k5NxkekMV9ANQYLHaSekKdkdBupY6YPT/pk= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/drvinfo/drvinfo.go b/pkg/drvinfo/drvinfo.go new file mode 100644 index 0000000..5ba756b --- /dev/null +++ b/pkg/drvinfo/drvinfo.go @@ -0,0 +1,98 @@ +package drvinfo + +import ( + "log" + "os" + + "github.com/hashicorp/go-version" + "github.com/safchain/ethtool" + "gopkg.in/yaml.v3" +) + +var ( + newEthtool = newEthtoolHandler +) + +type ethtoolInterface interface { + Close() + DriverInfo(name string) (ethtool.DrvInfo, error) +} + +func newEthtoolHandler() (ethtoolInterface, error) { + return ethtool.NewEthtool() +} + +type DriverInfo struct { + Name string + Version string +} + +type DriversList struct { + Drivers []DriverInfo +} + +type SupportedDrivers struct { + Drivers DriversList + DbFilePath string +} + +var NewSupportedDrivers = func(fp string) SupportedDrivers { + retv := SupportedDrivers{} + supportedDrivers, err := readSupportedDrivers(fp) + if err != nil { + log.Printf("fetching supported drivers list from %s failed with error %v", fp, err) + return retv + } + retv.Drivers = *supportedDrivers + retv.DbFilePath = fp + return retv +} + +var GetDriverInfo = func(name string) (*DriverInfo, error) { + ethHandle, err := newEthtool() + if err != nil { + return nil, err + } + defer ethHandle.Close() + drvInfo, err := ethHandle.DriverInfo(name) + if err != nil { + return nil, err + } + return &DriverInfo{ + Name: drvInfo.Driver, + Version: drvInfo.Version, + }, nil +} + +func (dl *SupportedDrivers) IsDriverSupported(drv *DriverInfo) bool { + for _, d := range dl.Drivers.Drivers { + if d.Name != drv.Name { + continue + } + supported, err := version.NewVersion(d.Version) + if err != nil { + continue + } + v, err := version.NewVersion(drv.Version) + if err != nil { + continue + } + if v.GreaterThanOrEqual(supported) { + return true + } + } + return false +} + +func readSupportedDrivers(filePath string) (*DriversList, error) { + data, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + drivers := &DriversList{} + err = yaml.Unmarshal(data, drivers) + if err != nil { + return drivers, err + } + return drivers, nil +} diff --git a/pkg/drvinfo/drvinfo_test.go b/pkg/drvinfo/drvinfo_test.go new file mode 100644 index 0000000..ca70da3 --- /dev/null +++ b/pkg/drvinfo/drvinfo_test.go @@ -0,0 +1,103 @@ +package drvinfo + +import ( + "os" + "path" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/safchain/ethtool" + "gopkg.in/yaml.v3" +) + +var ( + tempDir string +) + +type ethtoolMock struct { + drvInfo ethtool.DrvInfo +} + +func (m *ethtoolMock) Close() { + // do nothing +} + +func (m *ethtoolMock) DriverInfo(name string) (ethtool.DrvInfo, error) { + return m.drvInfo, nil +} + +func TestDrvInfo(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "DrvInfo Test Suite") +} + +var getDbFilePath = func() string { + var err error + tempDir, err = os.MkdirTemp("", "") + Expect(err).ToNot(HaveOccurred()) + fp := path.Join(tempDir, "test.yaml") + createDb(fp, *getTestDrv()) + return fp +} + +var getTestDrv = func() *DriverInfo { + return &DriverInfo{ + Name: "ice", + Version: "1.9.11", + } +} + +func createDb(filePath string, drv ...DriverInfo) { + l := make([]DriverInfo, 0) + l = append(l, drv...) + drivers := &DriversList{ + Drivers: l, + } + data, err := yaml.Marshal(drivers) + Expect(err).ToNot(HaveOccurred()) + + err = os.WriteFile(filePath, data, 0644) + Expect(err).ToNot(HaveOccurred()) +} + +var _ = BeforeSuite(func() { + newEthtool = func() (ethtoolInterface, error) { + return ðtoolMock{ + drvInfo: ethtool.DrvInfo{Driver: "dummy", Version: "1.0.0"}, + }, nil + } +}) + +var _ = Describe("drvinfo", func() { + AfterEach(func() { + os.RemoveAll(tempDir) + }) + + DescribeTable("IsDriverSupported should", func(testDrv *DriverInfo, supported bool, dl SupportedDrivers) { + Expect(dl.IsDriverSupported(testDrv)).To(Equal(supported)) + }, + Entry("return true if driver is supported", getTestDrv(), true, NewSupportedDrivers(getDbFilePath())), + Entry("return false if driver is not supported", &DriverInfo{Name: "ice", Version: "1.8.1"}, false, NewSupportedDrivers(getDbFilePath())), + Entry("return true if driver version is greater than supported", &DriverInfo{Name: "ice", Version: "1.10.1"}, true, NewSupportedDrivers(getDbFilePath())), + ) + + DescribeTable("readSupportedDrivers should", func(testDrv *DriverInfo) { + filePath := getDbFilePath() + db, err := readSupportedDrivers(filePath) + Expect(err).ToNot(HaveOccurred()) + Expect(db.Drivers).To(ContainElement(*testDrv)) + }, + Entry("return list of supported driver without error for valid config", getTestDrv()), + ) + + DescribeTable("GetDriverInfo should", func(testDrv DriverInfo) { + info, err := GetDriverInfo("dummy") + Expect(err).ToNot(HaveOccurred()) + Expect(info).ToNot(BeNil()) + Expect(info.Name).To(Equal(testDrv.Name)) + Expect(info.Version).To(Equal(testDrv.Version)) + }, + Entry("return correct DriverInfo without error", DriverInfo{Name: "dummy", Version: "1.0.0"}), + ) +}) diff --git a/pkg/utils/test/target b/pkg/utils/test/target new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/pkg/utils/test/target @@ -0,0 +1 @@ +hello world diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index d1a8ec0..8e510b3 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,15 +1,70 @@ package utils import ( - "log" + "fmt" + "io/fs" "path/filepath" + "strings" ) -func ResolvePath(path *string) { - cleanPath, err := filepath.EvalSymlinks(filepath.Clean(*path)) +// Comma-separated string flag type +type StringListFlag []string + +func (list *StringListFlag) String() string { + return strings.Join(*list, ",") +} + +func (list *StringListFlag) Set(val string) error { + *list = strings.Split(val, ",") + + for i := range *list { + (*list)[i] = strings.TrimSpace((*list)[i]) + } + + return nil +} + +func ResolveFlag(flag string, path *string) error { + if err := ResolvePath(path); err != nil { + return fmt.Errorf("%s - %v", flag, err) + } + + return nil +} + +func ResolvePath(path *string) error { + if *path == "" { + return fmt.Errorf("unable to resolve an empty path") + } + + cleanPath, err := filepath.Abs(*path) + if err != nil { + *path = "" + return fmt.Errorf("unsafe or invalid path specified '%s'\n%v", *path, err) + } + + evaluatedPath, err := EvalSymlinks(cleanPath) if err != nil { - log.Fatalf("Unsafe or invalid path specified, Error: %v", err) - } else { *path = cleanPath + return fmt.Errorf("unable to evaluate symbolic links on path '%s'\n%v", *path, err) + } + + *path = evaluatedPath + + return nil +} + +// Required to enable testing (filepath.EvalSymlinks does not support the fs.FS interface that fstest implements) +var EvalSymlinks = func(path string) (string, error) { + return filepath.EvalSymlinks(path) +} + +func IsSymLink(fsys fs.FS, path string) bool { + if info, err := fs.Stat(fsys, path); err != nil { + return false + } else if info.Mode() == fs.ModeSymlink { + return true } + + return false } diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go new file mode 100644 index 0000000..4b20bad --- /dev/null +++ b/pkg/utils/utils_test.go @@ -0,0 +1,98 @@ +package utils + +import ( + "fmt" + "io/fs" + "log" + "os" + "path/filepath" + "testing" + "testing/fstest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +const ( + linkPath = "test/link" + targetPath = "test/target" +) + +func TestUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "utils test suite") +} + +var _ = BeforeSuite(func() { + log.SetFlags(0) +}) + +var _ = AfterEach(func() { + os.Remove(linkPath) +}) + +var _ = DescribeTable("test path resolution", // ResolvePath + func(input string, output string, isSymlink bool, expectedErr error) { + if isSymlink { + targetPath, err := filepath.Abs(output) + Expect(err).ToNot(HaveOccurred()) + + err = os.Symlink(targetPath, input) + Expect(err).ToNot(HaveOccurred()) + } + + err := ResolvePath(&input) + Expect(input).To(Equal(output)) + + if expectedErr != nil { + Expect(err).To(Equal(expectedErr)) + } + }, + Entry("path resolved without change", "/var/lib/kubelet/cpu_manager_state", "/var/lib/kubelet/cpu_manager_state", false, nil), + Entry("path resolved with change", "/var/lib/../lib/kubelet/cpu_manager_state", "/var/lib/kubelet/cpu_manager_state", false, nil), + Entry("empty path", "", "", false, fmt.Errorf("unable to resolve an empty path")), + Entry("symbolic link", linkPath, getAbsPath(targetPath), true, nil), +) + +var _ = DescribeTable("test flag resolution", // ResolveFlag + func(flag string, path string, expectedResult string, expectedErr error) { + err := ResolveFlag(flag, &path) + Expect(path).To(Equal(expectedResult)) + + if expectedErr != nil { + Expect(err).To(Equal(expectedErr)) + } + }, + Entry("flag resolved", "test_flag1", "/var/lib/kubelet/cpu_manager_state", "/var/lib/kubelet/cpu_manager_state", nil), + Entry("empty path", "test_flag2", "", "", fmt.Errorf("test_flag2 - unable to resolve an empty path")), +) + +var _ = DescribeTable("test IsSymLink", // IsSymLink + func(fsys fs.FS, path string, expected bool) { + Expect(IsSymLink(fsys, path)).To(Equal(expected)) + }, + Entry("with symlink", fstest.MapFS{"test_file": {Mode: fs.ModeSymlink}}, "test_file", true), + Entry("without symlink", fstest.MapFS{"test_file": {Mode: fs.ModeDir}}, "test_file", false), +) + +var _ = DescribeTable("test StringListFlag type", // StringListFlag + func(input string, expectedSlice StringListFlag, expectedString string) { + var list StringListFlag + err := list.Set(input) + + Expect(err).ToNot(HaveOccurred()) + Expect(list).To(Equal(expectedSlice)) + Expect(list.String()).To(Equal(expectedString)) + }, + Entry("just one value", "sysfs", StringListFlag{"sysfs"}, "sysfs"), + Entry("two values", "sysfs,netlink", StringListFlag{"sysfs", "netlink"}, "sysfs,netlink"), + Entry("odd formatting", " sysfs , netlink ", StringListFlag{"sysfs", "netlink"}, "sysfs,netlink"), +) + +func getAbsPath(fp string) string { + absPath, err := filepath.Abs(fp) + if err != nil { + log.Printf("Failed to get absolute path, %v", err.Error()) + } + return absPath +} diff --git a/pkg/vfstats/netlink.go b/pkg/vfstats/netlink.go index fc79173..ad793f4 100644 --- a/pkg/vfstats/netlink.go +++ b/pkg/vfstats/netlink.go @@ -15,14 +15,18 @@ type PerPF struct { //VfStats returns the stats for all of the SRIOV Virtual Functions attached to the given Physical Function func VfStats(pf string) PerPF { - log.Printf("PerPF called for %v", pf) output := PerPF{pf, make(map[int]netlink.VfInfo)} - lnk, err := netlink.LinkByName(pf) + lnk, err := GetLink(pf) if err != nil { + log.Printf("netlink: error retrieving link for pf '%s'\n%v", pf, err) return output } + for _, vf := range lnk.Attrs().Vfs { output.Vfs[vf.ID] = vf } + return output } + +var GetLink = netlink.LinkByName diff --git a/pkg/vfstats/netlink_test.go b/pkg/vfstats/netlink_test.go new file mode 100644 index 0000000..5344e9c --- /dev/null +++ b/pkg/vfstats/netlink_test.go @@ -0,0 +1,41 @@ +package vfstats + +import ( + "fmt" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/vishvananda/netlink" +) + +func TestNetlink(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "netlink test suite") +} + +var _ = DescribeTable("test vf stats collection", // VfStats + func(devName string, link netlink.Device, err error, expectedPerPF PerPF, logs ...string) { + GetLink = func(name string) (netlink.Link, error) { + return &link, err + } + + Expect(VfStats(devName)).To(Equal(expectedPerPF)) + }, + Entry("Without error", + "ens801f0", + netlink.Device{LinkAttrs: netlink.LinkAttrs{Vfs: []netlink.VfInfo{ + {ID: 0, Mac: nil, Vlan: 0, Qos: 0, TxRate: 0, Spoofchk: true, LinkState: 0, MaxTxRate: 0, MinTxRate: 0, RxPackets: 11, TxPackets: 12, RxBytes: 13, TxBytes: 14, Multicast: 15, Broadcast: 16, RxDropped: 17, TxDropped: 18, RssQuery: 0, Trust: 0}, + {ID: 1, Mac: nil, Vlan: 0, Qos: 0, TxRate: 0, Spoofchk: true, LinkState: 0, MaxTxRate: 0, MinTxRate: 0, RxPackets: 21, TxPackets: 22, RxBytes: 23, TxBytes: 24, Multicast: 25, Broadcast: 26, RxDropped: 27, TxDropped: 28, RssQuery: 0, Trust: 0}}}}, + nil, + PerPF{"ens801f0", map[int]netlink.VfInfo{ + 0: {ID: 0, Mac: nil, Vlan: 0, Qos: 0, TxRate: 0, Spoofchk: true, LinkState: 0, MaxTxRate: 0, MinTxRate: 0, RxPackets: 11, TxPackets: 12, RxBytes: 13, TxBytes: 14, Multicast: 15, Broadcast: 16, RxDropped: 17, TxDropped: 18, RssQuery: 0, Trust: 0}, + 1: {ID: 1, Mac: nil, Vlan: 0, Qos: 0, TxRate: 0, Spoofchk: true, LinkState: 0, MaxTxRate: 0, MinTxRate: 0, RxPackets: 21, TxPackets: 22, RxBytes: 23, TxBytes: 24, Multicast: 25, Broadcast: 26, RxDropped: 27, TxDropped: 28, RssQuery: 0, Trust: 0}}}, + ), + Entry("With error", + "ens801f0", + nil, + fmt.Errorf("Link not found"), + PerPF{"ens801f0", map[int]netlink.VfInfo{}}, + ), +)