Merge pull request #394 from andyzhangx/force-unmount

fix: use force unmount to fix unmount NFS volume stuck issue
This commit is contained in:
Andy Zhang 2023-01-15 16:37:10 +08:00 committed by GitHub
commit f084312ad0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 3867 additions and 774 deletions

13
go.mod
View File

@ -9,16 +9,16 @@ require (
github.com/onsi/ginkgo v1.14.0 github.com/onsi/ginkgo v1.14.0
github.com/onsi/gomega v1.10.1 github.com/onsi/gomega v1.10.1
github.com/pborman/uuid v1.2.0 github.com/pborman/uuid v1.2.0
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.8.0
golang.org/x/net v0.4.0 golang.org/x/net v0.4.0
google.golang.org/grpc v1.40.0 google.golang.org/grpc v1.40.0
k8s.io/api v0.23.3 k8s.io/api v0.23.3
k8s.io/apimachinery v0.23.3 k8s.io/apimachinery v0.23.3
k8s.io/client-go v0.23.3 k8s.io/client-go v0.23.3
k8s.io/klog/v2 v2.30.0 k8s.io/klog/v2 v2.80.1
k8s.io/kubernetes v1.23.3 k8s.io/kubernetes v1.23.3
k8s.io/mount-utils v0.23.3 k8s.io/mount-utils v0.23.3
k8s.io/utils v0.0.0-20211116205334-6203023598ed k8s.io/utils v0.0.0-20221107191617-1a15be271d1d
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
@ -34,7 +34,7 @@ require (
github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-logr/logr v1.2.0 // indirect github.com/go-logr/logr v1.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-cmp v0.5.5 // indirect github.com/google/go-cmp v0.5.5 // indirect
@ -48,6 +48,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/moby/spdystream v0.2.0 // indirect github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxadm/tail v1.4.4 // indirect github.com/nxadm/tail v1.4.4 // indirect
@ -85,7 +86,7 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiserver v0.23.3 // indirect k8s.io/apiserver v0.23.3 // indirect
k8s.io/cloud-provider v0.23.3 // indirect k8s.io/cloud-provider v0.23.3 // indirect
k8s.io/component-base v0.23.3 // indirect k8s.io/component-base v0.23.3 // indirect
@ -122,7 +123,7 @@ replace (
k8s.io/kubelet => k8s.io/kubelet v0.23.3 k8s.io/kubelet => k8s.io/kubelet v0.23.3
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.23.3 k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.23.3
k8s.io/metrics => k8s.io/metrics v0.23.3 k8s.io/metrics => k8s.io/metrics v0.23.3
k8s.io/mount-utils => k8s.io/mount-utils v0.23.3 k8s.io/mount-utils => k8s.io/mount-utils v0.0.0-20230103133730-1df1a57439e2
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.23.3 k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.23.3
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.23.3 k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.23.3
k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.23.3 k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.23.3

24
go.sum
View File

@ -212,8 +212,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 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-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -429,6 +430,8 @@ github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hx
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -544,7 +547,6 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@ -566,13 +568,16 @@ github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwb
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -1097,8 +1102,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -1134,8 +1140,9 @@ k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAE
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=
k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-aggregator v0.23.3/go.mod h1:pt5QJ3QaIdhZzNlUvN5wndbM0LNT4BvhszGkzy2QdFo= k8s.io/kube-aggregator v0.23.3/go.mod h1:pt5QJ3QaIdhZzNlUvN5wndbM0LNT4BvhszGkzy2QdFo=
k8s.io/kube-controller-manager v0.23.3/go.mod h1:e8m5dhjei67DlLZA/QTvenxiGyonG9UhgHtU1LMslJE= k8s.io/kube-controller-manager v0.23.3/go.mod h1:e8m5dhjei67DlLZA/QTvenxiGyonG9UhgHtU1LMslJE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
@ -1151,15 +1158,16 @@ k8s.io/kubernetes v1.23.3 h1:weuFJOkRP7+057uvhNUYbVTVCog/klquhbtKRD+UHUo=
k8s.io/kubernetes v1.23.3/go.mod h1:C0AB/I7M4Nu6d1ELyGdC8qrrHEc6J5l8CHUashza1Io= k8s.io/kubernetes v1.23.3/go.mod h1:C0AB/I7M4Nu6d1ELyGdC8qrrHEc6J5l8CHUashza1Io=
k8s.io/legacy-cloud-providers v0.23.3/go.mod h1:s9vv59dUv4SU+HAm9C/YDdyw2OY9qmFYmcGEwr/ecDc= k8s.io/legacy-cloud-providers v0.23.3/go.mod h1:s9vv59dUv4SU+HAm9C/YDdyw2OY9qmFYmcGEwr/ecDc=
k8s.io/metrics v0.23.3/go.mod h1:Ut8TvkbsO4oMVeUzaTArvPrcw9QRFLs2XNzUlORjdYE= k8s.io/metrics v0.23.3/go.mod h1:Ut8TvkbsO4oMVeUzaTArvPrcw9QRFLs2XNzUlORjdYE=
k8s.io/mount-utils v0.23.3 h1:zPRPjS5rCOeEo4M6H5ysnwddVuYwEgJsiMgo2fgbPH0= k8s.io/mount-utils v0.0.0-20230103133730-1df1a57439e2 h1:kfACKquxtsEA7XXDy+iC92lg/1stK0UtzAhf7R2Y8Fc=
k8s.io/mount-utils v0.23.3/go.mod h1:OTN3LQPiOGMfx/SmVlsnySwsAmh4gYrDYLchlMHtf98= k8s.io/mount-utils v0.0.0-20230103133730-1df1a57439e2/go.mod h1:au99w4FWU5ZWelLb3Yx6kJc8RZ387IyWVM9tN65Yhxo=
k8s.io/pod-security-admission v0.23.3/go.mod h1:vULEGUgsujyrKBz3RRRZnvrJJt115gu0GICArDmgzqo= k8s.io/pod-security-admission v0.23.3/go.mod h1:vULEGUgsujyrKBz3RRRZnvrJJt115gu0GICArDmgzqo=
k8s.io/sample-apiserver v0.23.3/go.mod h1:5yDZRMfFvp7/2BOXBwk0AFNsD00iyuXeEsWZSoLFeGw= k8s.io/sample-apiserver v0.23.3/go.mod h1:5yDZRMfFvp7/2BOXBwk0AFNsD00iyuXeEsWZSoLFeGw=
k8s.io/system-validators v1.6.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/system-validators v1.6.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE=
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= 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/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=

View File

@ -17,6 +17,7 @@ limitations under the License.
package nfs package nfs
import ( import (
"runtime"
"strings" "strings"
"github.com/container-storage-interface/spec/lib/go/csi" "github.com/container-storage-interface/spec/lib/go/csi"
@ -108,7 +109,12 @@ func (n *Driver) Run(testMode bool) {
} }
klog.V(2).Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta) klog.V(2).Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta)
n.ns = NewNodeServer(n, mount.New("")) mounter := mount.New("")
if runtime.GOOS == "linux" {
// MounterForceUnmounter is only implemented on Linux now
mounter = mounter.(mount.MounterForceUnmounter)
}
n.ns = NewNodeServer(n, mounter)
s := NewNonBlockingGRPCServer() s := NewNonBlockingGRPCServer()
s.Start(n.endpoint, s.Start(n.endpoint,
NewDefaultIdentityServer(n), NewDefaultIdentityServer(n),

View File

@ -21,6 +21,7 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/container-storage-interface/spec/lib/go/csi" "github.com/container-storage-interface/spec/lib/go/csi"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -154,7 +155,15 @@ func (ns *NodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu
} }
klog.V(2).Infof("NodeUnpublishVolume: unmounting volume %s on %s", volumeID, targetPath) klog.V(2).Infof("NodeUnpublishVolume: unmounting volume %s on %s", volumeID, targetPath)
err := mount.CleanupMountPoint(targetPath, ns.mounter, true /*extensiveMountPointCheck*/) var err error
extensiveMountPointCheck := true
forceUnmounter, ok := ns.mounter.(mount.MounterForceUnmounter)
if ok {
klog.V(2).Infof("force unmount %s on %s", volumeID, targetPath)
err = mount.CleanupMountWithForce(targetPath, forceUnmounter, extensiveMountPointCheck, 30*time.Second)
} else {
err = mount.CleanupMountPoint(targetPath, ns.mounter, extensiveMountPointCheck)
}
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to unmount target %q: %v", targetPath, err) return nil, status.Errorf(codes.Internal, "failed to unmount target %q: %v", targetPath, err)
} }

View File

@ -19,7 +19,6 @@ package nfs
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"os" "os"
"reflect" "reflect"
"strings" "strings"
@ -217,11 +216,6 @@ func TestNodeUnpublishVolume(t *testing.T) {
req: csi.NodeUnpublishVolumeRequest{VolumeId: "vol_1"}, req: csi.NodeUnpublishVolumeRequest{VolumeId: "vol_1"},
expectedErr: status.Error(codes.InvalidArgument, "Target path missing in request"), expectedErr: status.Error(codes.InvalidArgument, "Target path missing in request"),
}, },
{
desc: "[Error] Unmount error mocked by IsLikelyNotMountPoint",
req: csi.NodeUnpublishVolumeRequest{TargetPath: errorTarget, VolumeId: "vol_1"},
expectedErr: fmt.Errorf("fake IsLikelyNotMountPoint: fake error"),
},
{ {
desc: "[Success] Volume not mounted", desc: "[Success] Volume not mounted",
req: csi.NodeUnpublishVolumeRequest{TargetPath: targetFile, VolumeId: "vol_1"}, req: csi.NodeUnpublishVolumeRequest{TargetPath: targetFile, VolumeId: "vol_1"},

View File

@ -105,14 +105,18 @@ with higher verbosity means more (and less important) logs will be generated.
There are implementations for the following logging libraries: There are implementations for the following logging libraries:
- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr) - **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr)
- **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr)
- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr) - **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr)
- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr) - **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr)
- **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting)
- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr) - **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr)
- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr) - **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr)
- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr) - **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr)
- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend) - **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend)
- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr) - **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr)
- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr) - **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr)
- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0)
- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing)
## FAQ ## FAQ

View File

@ -43,7 +43,9 @@ limitations under the License.
// //
// Info() and Error() are very similar, but they are separate methods so that // Info() and Error() are very similar, but they are separate methods so that
// LogSink implementations can choose to do things like attach additional // LogSink implementations can choose to do things like attach additional
// information (such as stack traces) on calls to Error(). // information (such as stack traces) on calls to Error(). Error() messages are
// always logged, regardless of the current verbosity. If there is no error
// instance available, passing nil is valid.
// //
// Verbosity // Verbosity
// //
@ -53,6 +55,7 @@ limitations under the License.
// Log-lines with V-levels that are not enabled (as per the LogSink) will not // Log-lines with V-levels that are not enabled (as per the LogSink) will not
// be written. Level V(0) is the default, and logger.V(0).Info() has the same // be written. Level V(0) is the default, and logger.V(0).Info() has the same
// meaning as logger.Info(). Negative V-levels have the same meaning as V(0). // meaning as logger.Info(). Negative V-levels have the same meaning as V(0).
// Error messages do not have a verbosity level and are always logged.
// //
// Where we might have written: // Where we might have written:
// if flVerbose >= 2 { // if flVerbose >= 2 {
@ -112,6 +115,15 @@ limitations under the License.
// may be any Go value, but how the value is formatted is determined by the // may be any Go value, but how the value is formatted is determined by the
// LogSink implementation. // LogSink implementation.
// //
// Logger instances are meant to be passed around by value. Code that receives
// such a value can call its methods without having to check whether the
// instance is ready for use.
//
// Calling methods with the null logger (Logger{}) as instance will crash
// because it has no LogSink. Therefore this null logger should never be passed
// around. For cases where passing a logger is optional, a pointer to Logger
// should be used.
//
// Key Naming Conventions // Key Naming Conventions
// //
// Keys are not strictly required to conform to any specification or regex, but // Keys are not strictly required to conform to any specification or regex, but
@ -253,11 +265,13 @@ func (l Logger) Info(msg string, keysAndValues ...interface{}) {
// Error logs an error, with the given message and key/value pairs as context. // Error logs an error, with the given message and key/value pairs as context.
// It functions similarly to Info, but may have unique behavior, and should be // It functions similarly to Info, but may have unique behavior, and should be
// preferred for logging errors (see the package documentations for more // preferred for logging errors (see the package documentations for more
// information). // information). The log message will always be emitted, regardless of
// verbosity level.
// //
// The msg argument should be used to add context to any underlying error, // The msg argument should be used to add context to any underlying error,
// while the err argument should be used to attach the actual error that // while the err argument should be used to attach the actual error that
// triggered this log line, if present. // triggered this log line, if present. The err parameter is optional
// and nil may be passed instead of an error instance.
func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) { func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
withHelper.GetCallStackHelper()() withHelper.GetCallStackHelper()()

202
vendor/github.com/moby/sys/mountinfo/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

44
vendor/github.com/moby/sys/mountinfo/doc.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
// Package mountinfo provides a set of functions to retrieve information about OS mounts.
//
// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD and OpenBSD,
// and a shallow implementation for Windows, but in general this is Linux-only package, so
// the rest of the document only applies to Linux, unless explicitly specified otherwise.
//
// In Linux, information about mounts seen by the current process is available from
// /proc/self/mountinfo. Note that due to mount namespaces, different processes can
// see different mounts. A per-process mountinfo table is available from /proc/<PID>/mountinfo,
// where <PID> is a numerical process identifier.
//
// In general, /proc is not a very efficient interface, and mountinfo is not an exception.
// For example, there is no way to get information about a specific mount point (i.e. it
// is all-or-nothing). This package tries to hide the /proc ineffectiveness by using
// parse filters while reading mountinfo. A filter can skip some entries, or stop
// processing the rest of the file once the needed information is found.
//
// For mountinfo filters that accept path as an argument, the path must be absolute,
// having all symlinks resolved, and being cleaned (i.e. no extra slashes or dots).
// One way to achieve all of the above is to employ filepath.Abs followed by
// filepath.EvalSymlinks (the latter calls filepath.Clean on the result so
// there is no need to explicitly call filepath.Clean).
//
// NOTE that in many cases there is no need to consult mountinfo at all. Here are some
// of the cases where mountinfo should not be parsed:
//
// 1. Before performing a mount. Usually, this is not needed, but if required (say to
// prevent over-mounts), to check whether a directory is mounted, call os.Lstat
// on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev
// fields -- if they differ, then the directory is the mount point. NOTE this does
// not work for bind mounts. Optionally, the filesystem type can also be checked
// by calling unix.Statfs and checking the Type field (i.e. filesystem type).
//
// 2. After performing a mount. If there is no error returned, the mount succeeded;
// checking the mount table for a new mount is redundant and expensive.
//
// 3. Before performing an unmount. It is more efficient to do an unmount and ignore
// a specific error (EINVAL) which tells the directory is not mounted.
//
// 4. After performing an unmount. If there is no error returned, the unmount succeeded.
//
// 5. To find the mount point root of a specific directory. You can perform os.Stat()
// on the directory and traverse up until the Dev field of a parent directory differs.
package mountinfo

101
vendor/github.com/moby/sys/mountinfo/mounted_linux.go generated vendored Normal file
View File

@ -0,0 +1,101 @@
package mountinfo
import (
"os"
"path/filepath"
"golang.org/x/sys/unix"
)
// MountedFast is a method of detecting a mount point without reading
// mountinfo from procfs. A caller can only trust the result if no error
// and sure == true are returned. Otherwise, other methods (e.g. parsing
// /proc/mounts) have to be used. If unsure, use Mounted instead (which
// uses MountedFast, but falls back to parsing mountinfo if needed).
//
// If a non-existent path is specified, an appropriate error is returned.
// In case the caller is not interested in this particular error, it should
// be handled separately using e.g. errors.Is(err, fs.ErrNotExist).
//
// This function is only available on Linux. When available (since kernel
// v5.6), openat2(2) syscall is used to reliably detect all mounts. Otherwise,
// the implementation falls back to using stat(2), which can reliably detect
// normal (but not bind) mounts.
func MountedFast(path string) (mounted, sure bool, err error) {
// Root is always mounted.
if path == string(os.PathSeparator) {
return true, true, nil
}
path, err = normalizePath(path)
if err != nil {
return false, false, err
}
mounted, sure, err = mountedFast(path)
return
}
// mountedByOpenat2 is a method of detecting a mount that works for all kinds
// of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel.
func mountedByOpenat2(path string) (bool, error) {
dir, last := filepath.Split(path)
dirfd, err := unix.Openat2(unix.AT_FDCWD, dir, &unix.OpenHow{
Flags: unix.O_PATH | unix.O_CLOEXEC,
})
if err != nil {
return false, &os.PathError{Op: "openat2", Path: dir, Err: err}
}
fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{
Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW,
Resolve: unix.RESOLVE_NO_XDEV,
})
_ = unix.Close(dirfd)
switch err { //nolint:errorlint // unix errors are bare
case nil: // definitely not a mount
_ = unix.Close(fd)
return false, nil
case unix.EXDEV: // definitely a mount
return true, nil
}
// not sure
return false, &os.PathError{Op: "openat2", Path: path, Err: err}
}
// mountedFast is similar to MountedFast, except it expects a normalized path.
func mountedFast(path string) (mounted, sure bool, err error) {
// Root is always mounted.
if path == string(os.PathSeparator) {
return true, true, nil
}
// Try a fast path, using openat2() with RESOLVE_NO_XDEV.
mounted, err = mountedByOpenat2(path)
if err == nil {
return mounted, true, nil
}
// Another fast path: compare st.st_dev fields.
mounted, err = mountedByStat(path)
// This does not work for bind mounts, so false negative
// is possible, therefore only trust if return is true.
if mounted && err == nil {
return true, true, nil
}
return
}
func mounted(path string) (bool, error) {
path, err := normalizePath(path)
if err != nil {
return false, err
}
mounted, sure, err := mountedFast(path)
if sure && err == nil {
return mounted, nil
}
// Fallback to parsing mountinfo.
return mountedByMountinfo(path)
}

53
vendor/github.com/moby/sys/mountinfo/mounted_unix.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
//go:build linux || freebsd || openbsd || darwin
// +build linux freebsd openbsd darwin
package mountinfo
import (
"os"
"path/filepath"
"golang.org/x/sys/unix"
)
func mountedByStat(path string) (bool, error) {
var st unix.Stat_t
if err := unix.Lstat(path, &st); err != nil {
return false, &os.PathError{Op: "stat", Path: path, Err: err}
}
dev := st.Dev
parent := filepath.Dir(path)
if err := unix.Lstat(parent, &st); err != nil {
return false, &os.PathError{Op: "stat", Path: parent, Err: err}
}
if dev != st.Dev {
// Device differs from that of parent,
// so definitely a mount point.
return true, nil
}
// NB: this does not detect bind mounts on Linux.
return false, nil
}
func normalizePath(path string) (realPath string, err error) {
if realPath, err = filepath.Abs(path); err != nil {
return "", err
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", err
}
if _, err := os.Stat(realPath); err != nil {
return "", err
}
return realPath, nil
}
func mountedByMountinfo(path string) (bool, error) {
entries, err := GetMounts(SingleEntryFilter(path))
if err != nil {
return false, err
}
return len(entries) > 0, nil
}

67
vendor/github.com/moby/sys/mountinfo/mountinfo.go generated vendored Normal file
View File

@ -0,0 +1,67 @@
package mountinfo
import (
"os"
)
// GetMounts retrieves a list of mounts for the current running process,
// with an optional filter applied (use nil for no filter).
func GetMounts(f FilterFunc) ([]*Info, error) {
return parseMountTable(f)
}
// Mounted determines if a specified path is a mount point. In case of any
// error, false (and an error) is returned.
//
// If a non-existent path is specified, an appropriate error is returned.
// In case the caller is not interested in this particular error, it should
// be handled separately using e.g. errors.Is(err, fs.ErrNotExist).
func Mounted(path string) (bool, error) {
// root is always mounted
if path == string(os.PathSeparator) {
return true, nil
}
return mounted(path)
}
// Info reveals information about a particular mounted filesystem. This
// struct is populated from the content in the /proc/<pid>/mountinfo file.
type Info struct {
// ID is a unique identifier of the mount (may be reused after umount).
ID int
// Parent is the ID of the parent mount (or of self for the root
// of this mount namespace's mount tree).
Parent int
// Major and Minor are the major and the minor components of the Dev
// field of unix.Stat_t structure returned by unix.*Stat calls for
// files on this filesystem.
Major, Minor int
// Root is the pathname of the directory in the filesystem which forms
// the root of this mount.
Root string
// Mountpoint is the pathname of the mount point relative to the
// process's root directory.
Mountpoint string
// Options is a comma-separated list of mount options.
Options string
// Optional are zero or more fields of the form "tag[:value]",
// separated by a space. Currently, the possible optional fields are
// "shared", "master", "propagate_from", and "unbindable". For more
// information, see mount_namespaces(7) Linux man page.
Optional string
// FSType is the filesystem type in the form "type[.subtype]".
FSType string
// Source is filesystem-specific information, or "none".
Source string
// VFSOptions is a comma-separated list of superblock options.
VFSOptions string
}

56
vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
//go:build freebsd || openbsd || darwin
// +build freebsd openbsd darwin
package mountinfo
import "golang.org/x/sys/unix"
// parseMountTable returns information about mounted filesystems
func parseMountTable(filter FilterFunc) ([]*Info, error) {
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
if err != nil {
return nil, err
}
entries := make([]unix.Statfs_t, count)
_, err = unix.Getfsstat(entries, unix.MNT_WAIT)
if err != nil {
return nil, err
}
var out []*Info
for _, entry := range entries {
var skip, stop bool
mountinfo := getMountinfo(&entry)
if filter != nil {
// filter out entries we're not interested in
skip, stop = filter(mountinfo)
if skip {
continue
}
}
out = append(out, mountinfo)
if stop {
break
}
}
return out, nil
}
func mounted(path string) (bool, error) {
path, err := normalizePath(path)
if err != nil {
return false, err
}
// Fast path: compare st.st_dev fields.
// This should always work for FreeBSD and OpenBSD.
mounted, err := mountedByStat(path)
if err == nil {
return mounted, nil
}
// Fallback to parsing mountinfo
return mountedByMountinfo(path)
}

View File

@ -0,0 +1,63 @@
package mountinfo
import "strings"
// FilterFunc is a type defining a callback function for GetMount(),
// used to filter out mountinfo entries we're not interested in,
// and/or stop further processing if we found what we wanted.
//
// It takes a pointer to the Info struct (fully populated with all available
// fields on the GOOS platform), and returns two booleans:
//
// skip: true if the entry should be skipped;
//
// stop: true if parsing should be stopped after the entry.
type FilterFunc func(*Info) (skip, stop bool)
// PrefixFilter discards all entries whose mount points do not start with, or
// are equal to the path specified in prefix. The prefix path must be absolute,
// have all symlinks resolved, and cleaned (i.e. no extra slashes or dots).
//
// PrefixFilter treats prefix as a path, not a partial prefix, which means that
// given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns
// "/foo" and "/foo/bar", and discards "/foobar".
func PrefixFilter(prefix string) FilterFunc {
return func(m *Info) (bool, bool) {
skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/")
return skip, false
}
}
// SingleEntryFilter looks for a specific entry.
func SingleEntryFilter(mp string) FilterFunc {
return func(m *Info) (bool, bool) {
if m.Mountpoint == mp {
return false, true // don't skip, stop now
}
return true, false // skip, keep going
}
}
// ParentsFilter returns all entries whose mount points
// can be parents of a path specified, discarding others.
//
// For example, given /var/lib/docker/something, entries
// like /var/lib/docker, /var and / are returned.
func ParentsFilter(path string) FilterFunc {
return func(m *Info) (bool, bool) {
skip := !strings.HasPrefix(path, m.Mountpoint)
return skip, false
}
}
// FSTypeFilter returns all entries that match provided fstype(s).
func FSTypeFilter(fstype ...string) FilterFunc {
return func(m *Info) (bool, bool) {
for _, t := range fstype {
if m.FSType == t {
return false, false // don't skip, keep going
}
}
return true, false // skip, keep going
}
}

View File

@ -0,0 +1,14 @@
//go:build freebsd || darwin
// +build freebsd darwin
package mountinfo
import "golang.org/x/sys/unix"
func getMountinfo(entry *unix.Statfs_t) *Info {
return &Info{
Mountpoint: unix.ByteSliceToString(entry.Mntonname[:]),
FSType: unix.ByteSliceToString(entry.Fstypename[:]),
Source: unix.ByteSliceToString(entry.Mntfromname[:]),
}
}

214
vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go generated vendored Normal file
View File

@ -0,0 +1,214 @@
package mountinfo
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
)
// GetMountsFromReader retrieves a list of mounts from the
// reader provided, with an optional filter applied (use nil
// for no filter). This can be useful in tests or benchmarks
// that provide fake mountinfo data, or when a source other
// than /proc/self/mountinfo needs to be read from.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
s := bufio.NewScanner(r)
out := []*Info{}
for s.Scan() {
var err error
/*
See http://man7.org/linux/man-pages/man5/proc.5.html
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
(1) mount ID: unique identifier of the mount (may be reused after umount)
(2) parent ID: ID of parent (or of self for the top of the mount tree)
(3) major:minor: value of st_dev for files on filesystem
(4) root: root of the mount within the filesystem
(5) mount point: mount point relative to the process's root
(6) mount options: per mount options
(7) optional fields: zero or more fields of the form "tag[:value]"
(8) separator: marks the end of the optional fields
(9) filesystem type: name of filesystem of the form "type[.subtype]"
(10) mount source: filesystem specific information or "none"
(11) super options: per super block options
In other words, we have:
* 6 mandatory fields (1)..(6)
* 0 or more optional fields (7)
* a separator field (8)
* 3 mandatory fields (9)..(11)
*/
text := s.Text()
fields := strings.Split(text, " ")
numFields := len(fields)
if numFields < 10 {
// should be at least 10 fields
return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
}
// separator field
sepIdx := numFields - 4
// In Linux <= 3.9 mounting a cifs with spaces in a share
// name (like "//srv/My Docs") _may_ end up having a space
// in the last field of mountinfo (like "unc=//serv/My Docs").
// Since kernel 3.10-rc1, cifs option "unc=" is ignored,
// so spaces should not appear.
//
// Check for a separator, and work around the spaces bug
for fields[sepIdx] != "-" {
sepIdx--
if sepIdx == 5 {
return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text)
}
}
p := &Info{}
p.Mountpoint, err = unescape(fields[4])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err)
}
p.FSType, err = unescape(fields[sepIdx+1])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
}
p.Source, err = unescape(fields[sepIdx+2])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err)
}
p.VFSOptions = fields[sepIdx+3]
// ignore any numbers parsing errors, as there should not be any
p.ID, _ = strconv.Atoi(fields[0])
p.Parent, _ = strconv.Atoi(fields[1])
mm := strings.SplitN(fields[2], ":", 3)
if len(mm) != 2 {
return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm)
}
p.Major, _ = strconv.Atoi(mm[0])
p.Minor, _ = strconv.Atoi(mm[1])
p.Root, err = unescape(fields[3])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err)
}
p.Options = fields[5]
// zero or more optional fields
p.Optional = strings.Join(fields[6:sepIdx], " ")
// Run the filter after parsing all fields.
var skip, stop bool
if filter != nil {
skip, stop = filter(p)
if skip {
continue
}
}
out = append(out, p)
if stop {
break
}
}
if err := s.Err(); err != nil {
return nil, err
}
return out, nil
}
func parseMountTable(filter FilterFunc) ([]*Info, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
}
defer f.Close()
return GetMountsFromReader(f, filter)
}
// PidMountInfo retrieves the list of mounts from a given process' mount
// namespace. Unless there is a need to get mounts from a mount namespace
// different from that of a calling process, use GetMounts.
//
// This function is Linux-specific.
//
// Deprecated: this will be removed before v1; use GetMountsFromReader with
// opened /proc/<pid>/mountinfo as an argument instead.
func PidMountInfo(pid int) ([]*Info, error) {
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
if err != nil {
return nil, err
}
defer f.Close()
return GetMountsFromReader(f, nil)
}
// A few specific characters in mountinfo path entries (root and mountpoint)
// are escaped using a backslash followed by a character's ascii code in octal.
//
// space -- as \040
// tab (aka \t) -- as \011
// newline (aka \n) -- as \012
// backslash (aka \\) -- as \134
//
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
func unescape(path string) (string, error) {
// try to avoid copying
if strings.IndexByte(path, '\\') == -1 {
return path, nil
}
// The following code is UTF-8 transparent as it only looks for some
// specific characters (backslash and 0..7) with values < utf8.RuneSelf,
// and everything else is passed through as is.
buf := make([]byte, len(path))
bufLen := 0
for i := 0; i < len(path); i++ {
if path[i] != '\\' {
buf[bufLen] = path[i]
bufLen++
continue
}
s := path[i:]
if len(s) < 4 {
// too short
return "", fmt.Errorf("bad escape sequence %q: too short", s)
}
c := s[1]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7':
v := c - '0'
for j := 2; j < 4; j++ { // one digit already; two more
if s[j] < '0' || s[j] > '7' {
return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3])
}
x := s[j] - '0'
v = (v << 3) | x
}
if v > 255 {
return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3])
}
buf[bufLen] = v
bufLen++
i += 3
continue
default:
return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3])
}
}
return string(buf[:bufLen]), nil
}

View File

@ -0,0 +1,11 @@
package mountinfo
import "golang.org/x/sys/unix"
func getMountinfo(entry *unix.Statfs_t) *Info {
return &Info{
Mountpoint: unix.ByteSliceToString(entry.F_mntonname[:]),
FSType: unix.ByteSliceToString(entry.F_fstypename[:]),
Source: unix.ByteSliceToString(entry.F_mntfromname[:]),
}
}

View File

@ -0,0 +1,19 @@
//go:build !windows && !linux && !freebsd && !openbsd && !darwin
// +build !windows,!linux,!freebsd,!openbsd,!darwin
package mountinfo
import (
"fmt"
"runtime"
)
var errNotImplemented = fmt.Errorf("not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
func parseMountTable(_ FilterFunc) ([]*Info, error) {
return nil, errNotImplemented
}
func mounted(path string) (bool, error) {
return false, errNotImplemented
}

View File

@ -0,0 +1,10 @@
package mountinfo
func parseMountTable(_ FilterFunc) ([]*Info, error) {
// Do NOT return an error!
return nil, nil
}
func mounted(_ string) (bool, error) {
return false, nil
}

View File

@ -1,8 +1,10 @@
package assert package assert
import ( import (
"bytes"
"fmt" "fmt"
"reflect" "reflect"
"time"
) )
type CompareType int type CompareType int
@ -30,6 +32,9 @@ var (
float64Type = reflect.TypeOf(float64(1)) float64Type = reflect.TypeOf(float64(1))
stringType = reflect.TypeOf("") stringType = reflect.TypeOf("")
timeType = reflect.TypeOf(time.Time{})
bytesType = reflect.TypeOf([]byte{})
) )
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
@ -299,6 +304,47 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
return compareLess, true return compareLess, true
} }
} }
// Check for known struct types we can check for compare results.
case reflect.Struct:
{
// All structs enter here. We're not interested in most types.
if !canConvert(obj1Value, timeType) {
break
}
// time.Time can compared!
timeObj1, ok := obj1.(time.Time)
if !ok {
timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
}
timeObj2, ok := obj2.(time.Time)
if !ok {
timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time)
}
return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)
}
case reflect.Slice:
{
// We only care about the []byte type.
if !canConvert(obj1Value, bytesType) {
break
}
// []byte can be compared!
bytesObj1, ok := obj1.([]byte)
if !ok {
bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte)
}
bytesObj2, ok := obj2.([]byte)
if !ok {
bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte)
}
return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
}
} }
return compareEqual, false return compareEqual, false
@ -310,7 +356,10 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
// assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, float64(2), float64(1))
// assert.Greater(t, "b", "a") // assert.Greater(t, "b", "a")
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
} }
// GreaterOrEqual asserts that the first element is greater than or equal to the second // GreaterOrEqual asserts that the first element is greater than or equal to the second
@ -320,7 +369,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
// assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "a")
// assert.GreaterOrEqual(t, "b", "b") // assert.GreaterOrEqual(t, "b", "b")
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
} }
// Less asserts that the first element is less than the second // Less asserts that the first element is less than the second
@ -329,7 +381,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
// assert.Less(t, float64(1), float64(2)) // assert.Less(t, float64(1), float64(2))
// assert.Less(t, "a", "b") // assert.Less(t, "a", "b")
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
} }
// LessOrEqual asserts that the first element is less than or equal to the second // LessOrEqual asserts that the first element is less than or equal to the second
@ -339,7 +394,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
// assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "a", "b")
// assert.LessOrEqual(t, "b", "b") // assert.LessOrEqual(t, "b", "b")
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
} }
// Positive asserts that the specified element is positive // Positive asserts that the specified element is positive
@ -347,8 +405,11 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
// assert.Positive(t, 1) // assert.Positive(t, 1)
// assert.Positive(t, 1.23) // assert.Positive(t, 1.23)
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e)) zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
} }
// Negative asserts that the specified element is negative // Negative asserts that the specified element is negative
@ -356,8 +417,11 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
// assert.Negative(t, -1) // assert.Negative(t, -1)
// assert.Negative(t, -1.23) // assert.Negative(t, -1.23)
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e)) zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...)
} }
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {

View File

@ -0,0 +1,16 @@
//go:build go1.17
// +build go1.17
// TODO: once support for Go 1.16 is dropped, this file can be
// merged/removed with assertion_compare_go1.17_test.go and
// assertion_compare_legacy.go
package assert
import "reflect"
// Wrapper around reflect.Value.CanConvert, for compatibility
// reasons.
func canConvert(value reflect.Value, to reflect.Type) bool {
return value.CanConvert(to)
}

View File

@ -0,0 +1,16 @@
//go:build !go1.17
// +build !go1.17
// TODO: once support for Go 1.16 is dropped, this file can be
// merged/removed with assertion_compare_go1.17_test.go and
// assertion_compare_can_convert.go
package assert
import "reflect"
// Older versions of Go does not have the reflect.Value.CanConvert
// method.
func canConvert(value reflect.Value, to reflect.Type) bool {
return false
}

View File

@ -123,6 +123,18 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
} }
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target. // ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is. // This is a wrapper for errors.Is.
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
@ -724,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
} }
// WithinRangef asserts that a time is within a time range (inclusive).
//
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...)
}
// YAMLEqf asserts that two YAML strings are equivalent. // YAMLEqf asserts that two YAML strings are equivalent.
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {

View File

@ -222,6 +222,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ..
return ErrorAsf(a.t, err, target, msg, args...) return ErrorAsf(a.t, err, target, msg, args...)
} }
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// a.ErrorContains(err, expectedErrorSubString)
func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorContains(a.t, theError, contains, msgAndArgs...)
}
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted")
func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorContainsf(a.t, theError, contains, msg, args...)
}
// ErrorIs asserts that at least one of the errors in err's chain matches target. // ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is. // This is a wrapper for errors.Is.
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
@ -1437,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta
return WithinDurationf(a.t, expected, actual, delta, msg, args...) return WithinDurationf(a.t, expected, actual, delta, msg, args...)
} }
// WithinRange asserts that a time is within a time range (inclusive).
//
// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return WithinRange(a.t, actual, start, end, msgAndArgs...)
}
// WithinRangef asserts that a time is within a time range (inclusive).
//
// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return WithinRangef(a.t, actual, start, end, msg, args...)
}
// YAMLEq asserts that two YAML strings are equivalent. // YAMLEq asserts that two YAML strings are equivalent.
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok { if h, ok := a.t.(tHelper); ok {

View File

@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
// assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []float{1, 2})
// assert.IsIncreasing(t, []string{"a", "b"}) // assert.IsIncreasing(t, []string{"a", "b"})
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
} }
// IsNonIncreasing asserts that the collection is not increasing // IsNonIncreasing asserts that the collection is not increasing
@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
// assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []float{2, 1})
// assert.IsNonIncreasing(t, []string{"b", "a"}) // assert.IsNonIncreasing(t, []string{"b", "a"})
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
} }
// IsDecreasing asserts that the collection is decreasing // IsDecreasing asserts that the collection is decreasing
@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{})
// assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []float{2, 1})
// assert.IsDecreasing(t, []string{"b", "a"}) // assert.IsDecreasing(t, []string{"b", "a"})
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
} }
// IsNonDecreasing asserts that the collection is not decreasing // IsNonDecreasing asserts that the collection is not decreasing
@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
// assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []float{1, 2})
// assert.IsNonDecreasing(t, []string{"a", "b"}) // assert.IsNonDecreasing(t, []string{"a", "b"})
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
} }

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"math" "math"
"os" "os"
"path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"runtime" "runtime"
@ -144,7 +145,8 @@ func CallerInfo() []string {
if len(parts) > 1 { if len(parts) > 1 {
dir := parts[len(parts)-2] dir := parts[len(parts)-2]
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
callers = append(callers, fmt.Sprintf("%s:%d", file, line)) path, _ := filepath.Abs(file)
callers = append(callers, fmt.Sprintf("%s:%d", path, line))
} }
} }
@ -563,16 +565,17 @@ func isEmpty(object interface{}) bool {
switch objValue.Kind() { switch objValue.Kind() {
// collection types are empty when they have no element // collection types are empty when they have no element
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: case reflect.Chan, reflect.Map, reflect.Slice:
return objValue.Len() == 0 return objValue.Len() == 0
// pointers are empty if nil or if the value they point to is empty // pointers are empty if nil or if the value they point to is empty
case reflect.Ptr: case reflect.Ptr:
if objValue.IsNil() { if objValue.IsNil() {
return true return true
} }
deref := objValue.Elem().Interface() deref := objValue.Elem().Interface()
return isEmpty(deref) return isEmpty(deref)
// for all other types, compare against the zero value // for all other types, compare against the zero value
// array types are empty when they match their zero-initialized state
default: default:
zero := reflect.Zero(objValue.Type()) zero := reflect.Zero(objValue.Type())
return reflect.DeepEqual(object, zero.Interface()) return reflect.DeepEqual(object, zero.Interface())
@ -718,10 +721,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
// return (false, false) if impossible. // return (false, false) if impossible.
// return (true, false) if element was not found. // return (true, false) if element was not found.
// return (true, true) if element was found. // return (true, true) if element was found.
func includeElement(list interface{}, element interface{}) (ok, found bool) { func containsElement(list interface{}, element interface{}) (ok, found bool) {
listValue := reflect.ValueOf(list) listValue := reflect.ValueOf(list)
listKind := reflect.TypeOf(list).Kind() listType := reflect.TypeOf(list)
if listType == nil {
return false, false
}
listKind := listType.Kind()
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
ok = false ok = false
@ -764,7 +771,7 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
h.Helper() h.Helper()
} }
ok, found := includeElement(s, contains) ok, found := containsElement(s, contains)
if !ok { if !ok {
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
} }
@ -787,7 +794,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
h.Helper() h.Helper()
} }
ok, found := includeElement(s, contains) ok, found := containsElement(s, contains)
if !ok { if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
} }
@ -811,7 +818,6 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
return true // we consider nil to be equal to the nil set return true // we consider nil to be equal to the nil set
} }
subsetValue := reflect.ValueOf(subset)
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
ok = false ok = false
@ -821,17 +827,35 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
listKind := reflect.TypeOf(list).Kind() listKind := reflect.TypeOf(list).Kind()
subsetKind := reflect.TypeOf(subset).Kind() subsetKind := reflect.TypeOf(subset).Kind()
if listKind != reflect.Array && listKind != reflect.Slice { if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
} }
if subsetKind != reflect.Array && subsetKind != reflect.Slice { if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
} }
subsetValue := reflect.ValueOf(subset)
if subsetKind == reflect.Map && listKind == reflect.Map {
listValue := reflect.ValueOf(list)
subsetKeys := subsetValue.MapKeys()
for i := 0; i < len(subsetKeys); i++ {
subsetKey := subsetKeys[i]
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
listElement := listValue.MapIndex(subsetKey).Interface()
if !ObjectsAreEqual(subsetElement, listElement) {
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
}
}
return true
}
for i := 0; i < subsetValue.Len(); i++ { for i := 0; i < subsetValue.Len(); i++ {
element := subsetValue.Index(i).Interface() element := subsetValue.Index(i).Interface()
ok, found := includeElement(list, element) ok, found := containsElement(list, element)
if !ok { if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
} }
@ -852,10 +876,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
h.Helper() h.Helper()
} }
if subset == nil { if subset == nil {
return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
} }
subsetValue := reflect.ValueOf(subset)
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
ok = false ok = false
@ -865,17 +888,35 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
listKind := reflect.TypeOf(list).Kind() listKind := reflect.TypeOf(list).Kind()
subsetKind := reflect.TypeOf(subset).Kind() subsetKind := reflect.TypeOf(subset).Kind()
if listKind != reflect.Array && listKind != reflect.Slice { if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
} }
if subsetKind != reflect.Array && subsetKind != reflect.Slice { if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
} }
subsetValue := reflect.ValueOf(subset)
if subsetKind == reflect.Map && listKind == reflect.Map {
listValue := reflect.ValueOf(list)
subsetKeys := subsetValue.MapKeys()
for i := 0; i < len(subsetKeys); i++ {
subsetKey := subsetKeys[i]
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
listElement := listValue.MapIndex(subsetKey).Interface()
if !ObjectsAreEqual(subsetElement, listElement) {
return true
}
}
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
}
for i := 0; i < subsetValue.Len(); i++ { for i := 0; i < subsetValue.Len(); i++ {
element := subsetValue.Index(i).Interface() element := subsetValue.Index(i).Interface()
ok, found := includeElement(list, element) ok, found := containsElement(list, element)
if !ok { if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
} }
@ -1000,27 +1041,21 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
type PanicTestFunc func() type PanicTestFunc func()
// didPanic returns true if the function passed to it panics. Otherwise, it returns false. // didPanic returns true if the function passed to it panics. Otherwise, it returns false.
func didPanic(f PanicTestFunc) (bool, interface{}, string) { func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) {
didPanic = true
didPanic := false
var message interface{}
var stack string
func() {
defer func() {
if message = recover(); message != nil {
didPanic = true
stack = string(debug.Stack())
}
}()
// call the target function
f()
defer func() {
message = recover()
if didPanic {
stack = string(debug.Stack())
}
}() }()
return didPanic, message, stack // call the target function
f()
didPanic = false
return
} }
// Panics asserts that the code inside the specified PanicTestFunc panics. // Panics asserts that the code inside the specified PanicTestFunc panics.
@ -1111,6 +1146,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration,
return true return true
} }
// WithinRange asserts that a time is within a time range (inclusive).
//
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if end.Before(start) {
return Fail(t, "Start should be before end", msgAndArgs...)
}
if actual.Before(start) {
return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...)
} else if actual.After(end) {
return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...)
}
return true
}
func toFloat(x interface{}) (float64, bool) { func toFloat(x interface{}) (float64, bool) {
var xf float64 var xf float64
xok := true xok := true
@ -1161,11 +1217,15 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
bf, bok := toFloat(actual) bf, bok := toFloat(actual)
if !aok || !bok { if !aok || !bok {
return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) return Fail(t, "Parameters must be numerical", msgAndArgs...)
}
if math.IsNaN(af) && math.IsNaN(bf) {
return true
} }
if math.IsNaN(af) { if math.IsNaN(af) {
return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...) return Fail(t, "Expected must not be NaN", msgAndArgs...)
} }
if math.IsNaN(bf) { if math.IsNaN(bf) {
@ -1188,7 +1248,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
if expected == nil || actual == nil || if expected == nil || actual == nil ||
reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(actual).Kind() != reflect.Slice ||
reflect.TypeOf(expected).Kind() != reflect.Slice { reflect.TypeOf(expected).Kind() != reflect.Slice {
return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) return Fail(t, "Parameters must be slice", msgAndArgs...)
} }
actualSlice := reflect.ValueOf(actual) actualSlice := reflect.ValueOf(actual)
@ -1250,8 +1310,12 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m
func calcRelativeError(expected, actual interface{}) (float64, error) { func calcRelativeError(expected, actual interface{}) (float64, error) {
af, aok := toFloat(expected) af, aok := toFloat(expected)
if !aok { bf, bok := toFloat(actual)
return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) if !aok || !bok {
return 0, fmt.Errorf("Parameters must be numerical")
}
if math.IsNaN(af) && math.IsNaN(bf) {
return 0, nil
} }
if math.IsNaN(af) { if math.IsNaN(af) {
return 0, errors.New("expected value must not be NaN") return 0, errors.New("expected value must not be NaN")
@ -1259,10 +1323,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
if af == 0 { if af == 0 {
return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
} }
bf, bok := toFloat(actual)
if !bok {
return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
}
if math.IsNaN(bf) { if math.IsNaN(bf) {
return 0, errors.New("actual value must not be NaN") return 0, errors.New("actual value must not be NaN")
} }
@ -1298,7 +1358,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
if expected == nil || actual == nil || if expected == nil || actual == nil ||
reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(actual).Kind() != reflect.Slice ||
reflect.TypeOf(expected).Kind() != reflect.Slice { reflect.TypeOf(expected).Kind() != reflect.Slice {
return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) return Fail(t, "Parameters must be slice", msgAndArgs...)
} }
actualSlice := reflect.ValueOf(actual) actualSlice := reflect.ValueOf(actual)
@ -1375,6 +1435,27 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
return true return true
} }
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
// and that the error contains the specified substring.
//
// actualObj, err := SomeFunction()
// assert.ErrorContains(t, err, expectedErrorSubString)
func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !Error(t, theError, msgAndArgs...) {
return false
}
actual := theError.Error()
if !strings.Contains(actual, contains) {
return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...)
}
return true
}
// matchRegexp return true if a specified regexp matches a string. // matchRegexp return true if a specified regexp matches a string.
func matchRegexp(rx interface{}, str interface{}) bool { func matchRegexp(rx interface{}, str interface{}) bool {
@ -1588,12 +1669,17 @@ func diff(expected interface{}, actual interface{}) string {
} }
var e, a string var e, a string
if et != reflect.TypeOf("") {
e = spewConfig.Sdump(expected) switch et {
a = spewConfig.Sdump(actual) case reflect.TypeOf(""):
} else {
e = reflect.ValueOf(expected).String() e = reflect.ValueOf(expected).String()
a = reflect.ValueOf(actual).String() a = reflect.ValueOf(actual).String()
case reflect.TypeOf(time.Time{}):
e = spewConfigStringerEnabled.Sdump(expected)
a = spewConfigStringerEnabled.Sdump(actual)
default:
e = spewConfig.Sdump(expected)
a = spewConfig.Sdump(actual)
} }
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
@ -1625,6 +1711,14 @@ var spewConfig = spew.ConfigState{
MaxDepth: 10, MaxDepth: 10,
} }
var spewConfigStringerEnabled = spew.ConfigState{
Indent: " ",
DisablePointerAddresses: true,
DisableCapacities: true,
SortKeys: true,
MaxDepth: 10,
}
type tHelper interface { type tHelper interface {
Helper() Helper()
} }

78
vendor/gopkg.in/yaml.v3/decode.go generated vendored
View File

@ -100,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t {
if p.event.typ != yaml_NO_EVENT { if p.event.typ != yaml_NO_EVENT {
return p.event.typ return p.event.typ
} }
if !yaml_parser_parse(&p.parser, &p.event) { // It's curious choice from the underlying API to generally return a
// positive result on success, but on this case return true in an error
// scenario. This was the source of bugs in the past (issue #666).
if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR {
p.fail() p.fail()
} }
return p.event.typ return p.event.typ
@ -320,6 +323,8 @@ type decoder struct {
decodeCount int decodeCount int
aliasCount int aliasCount int
aliasDepth int aliasDepth int
mergedFields map[interface{}]bool
} }
var ( var (
@ -808,6 +813,11 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
} }
} }
mergedFields := d.mergedFields
d.mergedFields = nil
var mergeNode *Node
mapIsNew := false mapIsNew := false
if out.IsNil() { if out.IsNil() {
out.Set(reflect.MakeMap(outt)) out.Set(reflect.MakeMap(outt))
@ -815,11 +825,18 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
} }
for i := 0; i < l; i += 2 { for i := 0; i < l; i += 2 {
if isMerge(n.Content[i]) { if isMerge(n.Content[i]) {
d.merge(n.Content[i+1], out) mergeNode = n.Content[i+1]
continue continue
} }
k := reflect.New(kt).Elem() k := reflect.New(kt).Elem()
if d.unmarshal(n.Content[i], k) { if d.unmarshal(n.Content[i], k) {
if mergedFields != nil {
ki := k.Interface()
if mergedFields[ki] {
continue
}
mergedFields[ki] = true
}
kkind := k.Kind() kkind := k.Kind()
if kkind == reflect.Interface { if kkind == reflect.Interface {
kkind = k.Elem().Kind() kkind = k.Elem().Kind()
@ -833,6 +850,12 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
} }
} }
} }
d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
}
d.stringMapType = stringMapType d.stringMapType = stringMapType
d.generalMapType = generalMapType d.generalMapType = generalMapType
return true return true
@ -844,7 +867,8 @@ func isStringMap(n *Node) bool {
} }
l := len(n.Content) l := len(n.Content)
for i := 0; i < l; i += 2 { for i := 0; i < l; i += 2 {
if n.Content[i].ShortTag() != strTag { shortTag := n.Content[i].ShortTag()
if shortTag != strTag && shortTag != mergeTag {
return false return false
} }
} }
@ -861,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
var elemType reflect.Type var elemType reflect.Type
if sinfo.InlineMap != -1 { if sinfo.InlineMap != -1 {
inlineMap = out.Field(sinfo.InlineMap) inlineMap = out.Field(sinfo.InlineMap)
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
elemType = inlineMap.Type().Elem() elemType = inlineMap.Type().Elem()
} }
@ -870,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
d.prepare(n, field) d.prepare(n, field)
} }
mergedFields := d.mergedFields
d.mergedFields = nil
var mergeNode *Node
var doneFields []bool var doneFields []bool
if d.uniqueKeys { if d.uniqueKeys {
doneFields = make([]bool, len(sinfo.FieldsList)) doneFields = make([]bool, len(sinfo.FieldsList))
@ -879,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
for i := 0; i < l; i += 2 { for i := 0; i < l; i += 2 {
ni := n.Content[i] ni := n.Content[i]
if isMerge(ni) { if isMerge(ni) {
d.merge(n.Content[i+1], out) mergeNode = n.Content[i+1]
continue continue
} }
if !d.unmarshal(ni, name) { if !d.unmarshal(ni, name) {
continue continue
} }
if info, ok := sinfo.FieldsMap[name.String()]; ok { sname := name.String()
if mergedFields != nil {
if mergedFields[sname] {
continue
}
mergedFields[sname] = true
}
if info, ok := sinfo.FieldsMap[sname]; ok {
if d.uniqueKeys { if d.uniqueKeys {
if doneFields[info.Id] { if doneFields[info.Id] {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
@ -911,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
} }
} }
d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
}
return true return true
} }
@ -918,19 +956,29 @@ func failWantMap() {
failf("map merge requires map or sequence of maps as the value") failf("map merge requires map or sequence of maps as the value")
} }
func (d *decoder) merge(n *Node, out reflect.Value) { func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
switch n.Kind { mergedFields := d.mergedFields
if mergedFields == nil {
d.mergedFields = make(map[interface{}]bool)
for i := 0; i < len(parent.Content); i += 2 {
k := reflect.New(ifaceType).Elem()
if d.unmarshal(parent.Content[i], k) {
d.mergedFields[k.Interface()] = true
}
}
}
switch merge.Kind {
case MappingNode: case MappingNode:
d.unmarshal(n, out) d.unmarshal(merge, out)
case AliasNode: case AliasNode:
if n.Alias != nil && n.Alias.Kind != MappingNode { if merge.Alias != nil && merge.Alias.Kind != MappingNode {
failWantMap() failWantMap()
} }
d.unmarshal(n, out) d.unmarshal(merge, out)
case SequenceNode: case SequenceNode:
// Step backwards as earlier nodes take precedence. for i := 0; i < len(merge.Content); i++ {
for i := len(n.Content) - 1; i >= 0; i-- { ni := merge.Content[i]
ni := n.Content[i]
if ni.Kind == AliasNode { if ni.Kind == AliasNode {
if ni.Alias != nil && ni.Alias.Kind != MappingNode { if ni.Alias != nil && ni.Alias.Kind != MappingNode {
failWantMap() failWantMap()
@ -943,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) {
default: default:
failWantMap() failWantMap()
} }
d.mergedFields = mergedFields
} }
func isMerge(n *Node) bool { func isMerge(n *Node) bool {

11
vendor/gopkg.in/yaml.v3/parserc.go generated vendored
View File

@ -687,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
if token == nil {
return false
}
parser.marks = append(parser.marks, token.start_mark) parser.marks = append(parser.marks, token.start_mark)
skip_token(parser) skip_token(parser)
} }
@ -786,7 +789,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
} }
token := peek_token(parser) token := peek_token(parser)
if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
return return
} }
@ -813,6 +816,9 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
if token == nil {
return false
}
parser.marks = append(parser.marks, token.start_mark) parser.marks = append(parser.marks, token.start_mark)
skip_token(parser) skip_token(parser)
} }
@ -922,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
if token == nil {
return false
}
parser.marks = append(parser.marks, token.start_mark) parser.marks = append(parser.marks, token.start_mark)
skip_token(parser) skip_token(parser)
} }

19
vendor/k8s.io/klog/v2/OWNERS generated vendored
View File

@ -1,19 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners # See the OWNERS docs at https://go.k8s.io/owners
reviewers: reviewers:
- jayunit100 - harshanarayana
- hoegaarden
- andyxning
- neolit123
- pohly - pohly
- yagonobre
- vincepri
- detiber
approvers: approvers:
- dims - dims
- thockin - thockin
- justinsb
- tallclair
- piosz
- brancz
- lavalamp
- serathius - serathius
emeritus_approvers:
- brancz
- justinsb
- lavalamp
- piosz
- tallclair

17
vendor/k8s.io/klog/v2/README.md generated vendored
View File

@ -23,6 +23,20 @@ Historical context is available here:
* https://groups.google.com/forum/#!msg/kubernetes-sig-architecture/wCWiWf3Juzs/hXRVBH90CgAJ * https://groups.google.com/forum/#!msg/kubernetes-sig-architecture/wCWiWf3Juzs/hXRVBH90CgAJ
* https://groups.google.com/forum/#!msg/kubernetes-dev/7vnijOMhLS0/1oRiNtigBgAJ * https://groups.google.com/forum/#!msg/kubernetes-dev/7vnijOMhLS0/1oRiNtigBgAJ
## Release versioning
Semantic versioning is used in this repository. It contains several Go modules
with different levels of stability:
- `k8s.io/klog/v2` - stable API, `vX.Y.Z` tags
- `examples` - no stable API, no tags, no intention to ever stabilize
Exempt from the API stability guarantee are items (packages, functions, etc.)
which are marked explicitly as `EXPERIMENTAL` in their docs comment. Those
may still change in incompatible ways or get removed entirely. This can only
be used for code that is used in tests to avoid situations where non-test
code from two different Kubernetes dependencies depends on incompatible
releases of klog because an experimental API was changed.
---- ----
How to use klog How to use klog
@ -32,6 +46,7 @@ How to use klog
- You can now use `log_file` instead of `log_dir` for logging to a single file (See `examples/log_file/usage_log_file.go`) - You can now use `log_file` instead of `log_dir` for logging to a single file (See `examples/log_file/usage_log_file.go`)
- If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`) - If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`)
- For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md)) - For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md))
- See our documentation on [pkg.go.dev/k8s.io](https://pkg.go.dev/k8s.io/klog).
**NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater. **NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater.
@ -85,7 +100,7 @@ The comment from glog.go introduces the ideas:
glog.Fatalf("Initialization failed: %s", err) glog.Fatalf("Initialization failed: %s", err)
See the documentation for the V function for an explanation See the documentation of the V function for an explanation
of these examples: of these examples:
if glog.V(2) { if glog.V(2) {

186
vendor/k8s.io/klog/v2/contextual.go generated vendored Normal file
View File

@ -0,0 +1,186 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package klog
import (
"context"
"github.com/go-logr/logr"
)
// This file provides the implementation of
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/1602-structured-logging
//
// SetLogger and ClearLogger were originally added to klog.go and got moved
// here. Contextual logging adds a way to retrieve a Logger for direct logging
// without the logging calls in klog.go.
//
// The global variables are expected to be modified only during sequential
// parts of a program (init, serial tests) and therefore are not protected by
// mutex locking.
var (
// klogLogger is used as fallback for logging through the normal klog code
// when no Logger is set.
klogLogger logr.Logger = logr.New(&klogger{})
)
// SetLogger sets a Logger implementation that will be used as backing
// implementation of the traditional klog log calls. klog will do its own
// verbosity checks before calling logger.V().Info. logger.Error is always
// called, regardless of the klog verbosity settings.
//
// If set, all log lines will be suppressed from the regular output, and
// redirected to the logr implementation.
// Use as:
//
// ...
// klog.SetLogger(zapr.NewLogger(zapLog))
//
// To remove a backing logr implemention, use ClearLogger. Setting an
// empty logger with SetLogger(logr.Logger{}) does not work.
//
// Modifying the logger is not thread-safe and should be done while no other
// goroutines invoke log calls, usually during program initialization.
func SetLogger(logger logr.Logger) {
SetLoggerWithOptions(logger)
}
// SetLoggerWithOptions is a more flexible version of SetLogger. Without
// additional options, it behaves exactly like SetLogger. By passing
// ContextualLogger(true) as option, it can be used to set a logger that then
// will also get called directly by applications which retrieve it via
// FromContext, Background, or TODO.
//
// Supporting direct calls is recommended because it avoids the overhead of
// routing log entries through klogr into klog and then into the actual Logger
// backend.
func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
logging.logger = &logger
logging.loggerOptions = loggerOptions{}
for _, opt := range opts {
opt(&logging.loggerOptions)
}
}
// ContextualLogger determines whether the logger passed to
// SetLoggerWithOptions may also get called directly. Such a logger cannot rely
// on verbosity checking in klog.
func ContextualLogger(enabled bool) LoggerOption {
return func(o *loggerOptions) {
o.contextualLogger = enabled
}
}
// FlushLogger provides a callback for flushing data buffered by the logger.
func FlushLogger(flush func()) LoggerOption {
return func(o *loggerOptions) {
o.flush = flush
}
}
// LoggerOption implements the functional parameter paradigm for
// SetLoggerWithOptions.
type LoggerOption func(o *loggerOptions)
type loggerOptions struct {
contextualLogger bool
flush func()
}
// ClearLogger removes a backing Logger implementation if one was set earlier
// with SetLogger.
//
// Modifying the logger is not thread-safe and should be done while no other
// goroutines invoke log calls, usually during program initialization.
func ClearLogger() {
logging.logger = nil
logging.loggerOptions = loggerOptions{}
}
// EnableContextualLogging controls whether contextual logging is enabled.
// By default it is enabled. When disabled, FromContext avoids looking up
// the logger in the context and always returns the global logger.
// LoggerWithValues, LoggerWithName, and NewContext become no-ops
// and return their input logger respectively context. This may be useful
// to avoid the additional overhead for contextual logging.
//
// This must be called during initialization before goroutines are started.
func EnableContextualLogging(enabled bool) {
logging.contextualLoggingEnabled = enabled
}
// FromContext retrieves a logger set by the caller or, if not set,
// falls back to the program's global logger (a Logger instance or klog
// itself).
func FromContext(ctx context.Context) Logger {
if logging.contextualLoggingEnabled {
if logger, err := logr.FromContext(ctx); err == nil {
return logger
}
}
return Background()
}
// TODO can be used as a last resort by code that has no means of
// receiving a logger from its caller. FromContext or an explicit logger
// parameter should be used instead.
func TODO() Logger {
return Background()
}
// Background retrieves the fallback logger. It should not be called before
// that logger was initialized by the program and not by code that should
// better receive a logger via its parameters. TODO can be used as a temporary
// solution for such code.
func Background() Logger {
if logging.loggerOptions.contextualLogger {
// Is non-nil because logging.loggerOptions.contextualLogger is
// only true if a logger was set.
return *logging.logger
}
return klogLogger
}
// LoggerWithValues returns logger.WithValues(...kv) when
// contextual logging is enabled, otherwise the logger.
func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
if logging.contextualLoggingEnabled {
return logger.WithValues(kv...)
}
return logger
}
// LoggerWithName returns logger.WithName(name) when contextual logging is
// enabled, otherwise the logger.
func LoggerWithName(logger Logger, name string) Logger {
if logging.contextualLoggingEnabled {
return logger.WithName(name)
}
return logger
}
// NewContext returns logr.NewContext(ctx, logger) when
// contextual logging is enabled, otherwise ctx.
func NewContext(ctx context.Context, logger Logger) context.Context {
if logging.contextualLoggingEnabled {
return logr.NewContext(ctx, logger)
}
return ctx
}

69
vendor/k8s.io/klog/v2/exit.go generated vendored Normal file
View File

@ -0,0 +1,69 @@
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
//
// Copyright 2013 Google Inc. All Rights Reserved.
// Copyright 2022 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package klog
import (
"fmt"
"os"
"time"
)
var (
// ExitFlushTimeout is the timeout that klog has traditionally used during
// calls like Fatal or Exit when flushing log data right before exiting.
// Applications that replace those calls and do not have some specific
// requirements like "exit immediately" can use this value as parameter
// for FlushAndExit.
//
// Can be set for testing purpose or to change the application's
// default.
ExitFlushTimeout = 10 * time.Second
// OsExit is the function called by FlushAndExit to terminate the program.
//
// Can be set for testing purpose or to change the application's
// default behavior. Note that the function should not simply return
// because callers of functions like Fatal will not expect that.
OsExit = os.Exit
)
// FlushAndExit flushes log data for a certain amount of time and then calls
// os.Exit. Combined with some logging call it provides a replacement for
// traditional calls like Fatal or Exit.
func FlushAndExit(flushTimeout time.Duration, exitCode int) {
timeoutFlush(flushTimeout)
OsExit(exitCode)
}
// timeoutFlush calls Flush and returns when it completes or after timeout
// elapses, whichever happens first. This is needed because the hooks invoked
// by Flush may deadlock when klog.Fatal is called from a hook that holds
// a lock. Flushing also might take too long.
func timeoutFlush(timeout time.Duration) {
done := make(chan bool, 1)
go func() {
Flush() // calls logging.lockAndFlushAll()
done <- true
}()
select {
case <-done:
case <-time.After(timeout):
fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout)
}
}

38
vendor/k8s.io/klog/v2/imports.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package klog
import (
"github.com/go-logr/logr"
)
// The reason for providing these aliases is to allow code to work with logr
// without directly importing it.
// Logger in this package is exactly the same as logr.Logger.
type Logger = logr.Logger
// LogSink in this package is exactly the same as logr.LogSink.
type LogSink = logr.LogSink
// Runtimeinfo in this package is exactly the same as logr.RuntimeInfo.
type RuntimeInfo = logr.RuntimeInfo
var (
// New is an alias for logr.New.
New = logr.New
)

159
vendor/k8s.io/klog/v2/internal/buffer/buffer.go generated vendored Normal file
View File

@ -0,0 +1,159 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// Copyright 2022 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package buffer provides a cache for byte.Buffer instances that can be reused
// to avoid frequent allocation and deallocation. It also has utility code
// for log header formatting that use these buffers.
package buffer
import (
"bytes"
"os"
"sync"
"time"
"k8s.io/klog/v2/internal/severity"
)
var (
// Pid is inserted into log headers. Can be overridden for tests.
Pid = os.Getpid()
)
// Buffer holds a single byte.Buffer for reuse. The zero value is ready for
// use. It also provides some helper methods for output formatting.
type Buffer struct {
bytes.Buffer
Tmp [64]byte // temporary byte array for creating headers.
next *Buffer
}
// Buffers manages the reuse of individual buffer instances. It is thread-safe.
type Buffers struct {
// mu protects the free list. It is separate from the main mutex
// so buffers can be grabbed and printed to without holding the main lock,
// for better parallelization.
mu sync.Mutex
// freeList is a list of byte buffers, maintained under mu.
freeList *Buffer
}
// GetBuffer returns a new, ready-to-use buffer.
func (bl *Buffers) GetBuffer() *Buffer {
bl.mu.Lock()
b := bl.freeList
if b != nil {
bl.freeList = b.next
}
bl.mu.Unlock()
if b == nil {
b = new(Buffer)
} else {
b.next = nil
b.Reset()
}
return b
}
// PutBuffer returns a buffer to the free list.
func (bl *Buffers) PutBuffer(b *Buffer) {
if b.Len() >= 256 {
// Let big buffers die a natural death.
return
}
bl.mu.Lock()
b.next = bl.freeList
bl.freeList = b
bl.mu.Unlock()
}
// Some custom tiny helper functions to print the log header efficiently.
const digits = "0123456789"
// twoDigits formats a zero-prefixed two-digit integer at buf.Tmp[i].
func (buf *Buffer) twoDigits(i, d int) {
buf.Tmp[i+1] = digits[d%10]
d /= 10
buf.Tmp[i] = digits[d%10]
}
// nDigits formats an n-digit integer at buf.Tmp[i],
// padding with pad on the left.
// It assumes d >= 0.
func (buf *Buffer) nDigits(n, i, d int, pad byte) {
j := n - 1
for ; j >= 0 && d > 0; j-- {
buf.Tmp[i+j] = digits[d%10]
d /= 10
}
for ; j >= 0; j-- {
buf.Tmp[i+j] = pad
}
}
// someDigits formats a zero-prefixed variable-width integer at buf.Tmp[i].
func (buf *Buffer) someDigits(i, d int) int {
// Print into the top, then copy down. We know there's space for at least
// a 10-digit number.
j := len(buf.Tmp)
for {
j--
buf.Tmp[j] = digits[d%10]
d /= 10
if d == 0 {
break
}
}
return copy(buf.Tmp[i:], buf.Tmp[j:])
}
// FormatHeader formats a log header using the provided file name and line number.
func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
if line < 0 {
line = 0 // not a real line number, but acceptable to someDigits
}
if s > severity.FatalLog {
s = severity.InfoLog // for safety.
}
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
// It's worth about 3X. Fprintf is hard.
_, month, day := now.Date()
hour, minute, second := now.Clock()
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
buf.Tmp[0] = severity.Char[s]
buf.twoDigits(1, int(month))
buf.twoDigits(3, day)
buf.Tmp[5] = ' '
buf.twoDigits(6, hour)
buf.Tmp[8] = ':'
buf.twoDigits(9, minute)
buf.Tmp[11] = ':'
buf.twoDigits(12, second)
buf.Tmp[14] = '.'
buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
buf.Tmp[21] = ' '
buf.nDigits(7, 22, Pid, ' ') // TODO: should be TID
buf.Tmp[29] = ' '
buf.Write(buf.Tmp[:30])
buf.WriteString(file)
buf.Tmp[0] = ':'
n := buf.someDigits(1, line)
buf.Tmp[n+1] = ']'
buf.Tmp[n+2] = ' '
buf.Write(buf.Tmp[:n+3])
}

7
vendor/k8s.io/klog/v2/internal/clock/README.md generated vendored Normal file
View File

@ -0,0 +1,7 @@
# Clock
This package provides an interface for time-based operations. It allows
mocking time for testing.
This is a copy of k8s.io/utils/clock. We have to copy it to avoid a circular
dependency (k8s.io/klog -> k8s.io/utils -> k8s.io/klog).

178
vendor/k8s.io/klog/v2/internal/clock/clock.go generated vendored Normal file
View File

@ -0,0 +1,178 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package clock
import "time"
// PassiveClock allows for injecting fake or real clocks into code
// that needs to read the current time but does not support scheduling
// activity in the future.
type PassiveClock interface {
Now() time.Time
Since(time.Time) time.Duration
}
// Clock allows for injecting fake or real clocks into code that
// needs to do arbitrary things based on time.
type Clock interface {
PassiveClock
// After returns the channel of a new Timer.
// This method does not allow to free/GC the backing timer before it fires. Use
// NewTimer instead.
After(d time.Duration) <-chan time.Time
// NewTimer returns a new Timer.
NewTimer(d time.Duration) Timer
// Sleep sleeps for the provided duration d.
// Consider making the sleep interruptible by using 'select' on a context channel and a timer channel.
Sleep(d time.Duration)
// Tick returns the channel of a new Ticker.
// This method does not allow to free/GC the backing ticker. Use
// NewTicker from WithTicker instead.
Tick(d time.Duration) <-chan time.Time
}
// WithTicker allows for injecting fake or real clocks into code that
// needs to do arbitrary things based on time.
type WithTicker interface {
Clock
// NewTicker returns a new Ticker.
NewTicker(time.Duration) Ticker
}
// WithDelayedExecution allows for injecting fake or real clocks into
// code that needs to make use of AfterFunc functionality.
type WithDelayedExecution interface {
Clock
// AfterFunc executes f in its own goroutine after waiting
// for d duration and returns a Timer whose channel can be
// closed by calling Stop() on the Timer.
AfterFunc(d time.Duration, f func()) Timer
}
// WithTickerAndDelayedExecution allows for injecting fake or real clocks
// into code that needs Ticker and AfterFunc functionality
type WithTickerAndDelayedExecution interface {
WithTicker
// AfterFunc executes f in its own goroutine after waiting
// for d duration and returns a Timer whose channel can be
// closed by calling Stop() on the Timer.
AfterFunc(d time.Duration, f func()) Timer
}
// Ticker defines the Ticker interface.
type Ticker interface {
C() <-chan time.Time
Stop()
}
var _ = WithTicker(RealClock{})
// RealClock really calls time.Now()
type RealClock struct{}
// Now returns the current time.
func (RealClock) Now() time.Time {
return time.Now()
}
// Since returns time since the specified timestamp.
func (RealClock) Since(ts time.Time) time.Duration {
return time.Since(ts)
}
// After is the same as time.After(d).
// This method does not allow to free/GC the backing timer before it fires. Use
// NewTimer instead.
func (RealClock) After(d time.Duration) <-chan time.Time {
return time.After(d)
}
// NewTimer is the same as time.NewTimer(d)
func (RealClock) NewTimer(d time.Duration) Timer {
return &realTimer{
timer: time.NewTimer(d),
}
}
// AfterFunc is the same as time.AfterFunc(d, f).
func (RealClock) AfterFunc(d time.Duration, f func()) Timer {
return &realTimer{
timer: time.AfterFunc(d, f),
}
}
// Tick is the same as time.Tick(d)
// This method does not allow to free/GC the backing ticker. Use
// NewTicker instead.
func (RealClock) Tick(d time.Duration) <-chan time.Time {
return time.Tick(d)
}
// NewTicker returns a new Ticker.
func (RealClock) NewTicker(d time.Duration) Ticker {
return &realTicker{
ticker: time.NewTicker(d),
}
}
// Sleep is the same as time.Sleep(d)
// Consider making the sleep interruptible by using 'select' on a context channel and a timer channel.
func (RealClock) Sleep(d time.Duration) {
time.Sleep(d)
}
// Timer allows for injecting fake or real timers into code that
// needs to do arbitrary things based on time.
type Timer interface {
C() <-chan time.Time
Stop() bool
Reset(d time.Duration) bool
}
var _ = Timer(&realTimer{})
// realTimer is backed by an actual time.Timer.
type realTimer struct {
timer *time.Timer
}
// C returns the underlying timer's channel.
func (r *realTimer) C() <-chan time.Time {
return r.timer.C
}
// Stop calls Stop() on the underlying timer.
func (r *realTimer) Stop() bool {
return r.timer.Stop()
}
// Reset calls Reset() on the underlying timer.
func (r *realTimer) Reset(d time.Duration) bool {
return r.timer.Reset(d)
}
type realTicker struct {
ticker *time.Ticker
}
func (r *realTicker) C() <-chan time.Time {
return r.ticker.C
}
func (r *realTicker) Stop() {
r.ticker.Stop()
}

42
vendor/k8s.io/klog/v2/internal/dbg/dbg.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
//
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package dbg provides some helper code for call traces.
package dbg
import (
"runtime"
)
// Stacks is a wrapper for runtime.Stack that attempts to recover the data for
// all goroutines or the calling one.
func Stacks(all bool) []byte {
// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
n := 10000
if all {
n = 100000
}
var trace []byte
for i := 0; i < 5; i++ {
trace = make([]byte, n)
nbytes := runtime.Stack(trace, all)
if nbytes < len(trace) {
return trace[:nbytes]
}
n *= 2
}
return trace
}

253
vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go generated vendored Normal file
View File

@ -0,0 +1,253 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package serialize
import (
"bytes"
"fmt"
"strconv"
"github.com/go-logr/logr"
)
// WithValues implements LogSink.WithValues. The old key/value pairs are
// assumed to be well-formed, the new ones are checked and padded if
// necessary. It returns a new slice.
func WithValues(oldKV, newKV []interface{}) []interface{} {
if len(newKV) == 0 {
return oldKV
}
newLen := len(oldKV) + len(newKV)
hasMissingValue := newLen%2 != 0
if hasMissingValue {
newLen++
}
// The new LogSink must have its own slice.
kv := make([]interface{}, 0, newLen)
kv = append(kv, oldKV...)
kv = append(kv, newKV...)
if hasMissingValue {
kv = append(kv, missingValue)
}
return kv
}
// MergeKVs deduplicates elements provided in two key/value slices.
//
// Keys in each slice are expected to be unique, so duplicates can only occur
// when the first and second slice contain the same key. When that happens, the
// key/value pair from the second slice is used. The first slice must be well-formed
// (= even key/value pairs). The second one may have a missing value, in which
// case the special "missing value" is added to the result.
func MergeKVs(first, second []interface{}) []interface{} {
maxLength := len(first) + (len(second)+1)/2*2
if maxLength == 0 {
// Nothing to do at all.
return nil
}
if len(first) == 0 && len(second)%2 == 0 {
// Nothing to be overridden, second slice is well-formed
// and can be used directly.
return second
}
// Determine which keys are in the second slice so that we can skip
// them when iterating over the first one. The code intentionally
// favors performance over completeness: we assume that keys are string
// constants and thus compare equal when the string values are equal. A
// string constant being overridden by, for example, a fmt.Stringer is
// not handled.
overrides := map[interface{}]bool{}
for i := 0; i < len(second); i += 2 {
overrides[second[i]] = true
}
merged := make([]interface{}, 0, maxLength)
for i := 0; i+1 < len(first); i += 2 {
key := first[i]
if overrides[key] {
continue
}
merged = append(merged, key, first[i+1])
}
merged = append(merged, second...)
if len(merged)%2 != 0 {
merged = append(merged, missingValue)
}
return merged
}
const missingValue = "(MISSING)"
// KVListFormat serializes all key/value pairs into the provided buffer.
// A space gets inserted before the first pair and between each pair.
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
for i := 0; i < len(keysAndValues); i += 2 {
var v interface{}
k := keysAndValues[i]
if i+1 < len(keysAndValues) {
v = keysAndValues[i+1]
} else {
v = missingValue
}
b.WriteByte(' ')
// Keys are assumed to be well-formed according to
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
// for the sake of performance. Keys with spaces,
// special characters, etc. will break parsing.
if sK, ok := k.(string); ok {
// Avoid one allocation when the key is a string, which
// normally it should be.
b.WriteString(sK)
} else {
b.WriteString(fmt.Sprintf("%s", k))
}
// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case fmt.Stringer:
writeStringValue(b, true, StringerToString(v))
case string:
writeStringValue(b, true, v)
case error:
writeStringValue(b, true, ErrorToString(v))
case logr.Marshaler:
value := MarshalerToValue(v)
// A marshaler that returns a string is useful for
// delayed formatting of complex values. We treat this
// case like a normal string. This is useful for
// multi-line support.
//
// We could do this by recursively formatting a value,
// but that comes with the risk of infinite recursion
// if a marshaler returns itself. Instead we call it
// only once and rely on it returning the intended
// value directly.
switch value := value.(type) {
case string:
writeStringValue(b, true, value)
default:
writeStringValue(b, false, fmt.Sprintf("%+v", value))
}
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
writeStringValue(b, false, fmt.Sprintf("%+v", v))
}
}
}
// StringerToString converts a Stringer to a string,
// handling panics if they occur.
func StringerToString(s fmt.Stringer) (ret string) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = s.String()
return
}
// MarshalerToValue invokes a marshaler and catches
// panics.
func MarshalerToValue(m logr.Marshaler) (ret interface{}) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = m.MarshalLog()
return
}
// ErrorToString converts an error to a string,
// handling panics if they occur.
func ErrorToString(err error) (ret string) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = err.Error()
return
}
func writeStringValue(b *bytes.Buffer, quote bool, v string) {
data := []byte(v)
index := bytes.IndexByte(data, '\n')
if index == -1 {
b.WriteByte('=')
if quote {
// Simple string, quote quotation marks and non-printable characters.
b.WriteString(strconv.Quote(v))
return
}
// Non-string with no line breaks.
b.WriteString(v)
return
}
// Complex multi-line string, show as-is with indention like this:
// I... "hello world" key=<
// <tab>line 1
// <tab>line 2
// >
//
// Tabs indent the lines of the value while the end of string delimiter
// is indented with a space. That has two purposes:
// - visual difference between the two for a human reader because indention
// will be different
// - no ambiguity when some value line starts with the end delimiter
//
// One downside is that the output cannot distinguish between strings that
// end with a line break and those that don't because the end delimiter
// will always be on the next line.
b.WriteString("=<\n")
for index != -1 {
b.WriteByte('\t')
b.Write(data[0 : index+1])
data = data[index+1:]
index = bytes.IndexByte(data, '\n')
}
if len(data) == 0 {
// String ended with line break, don't add another.
b.WriteString(" >")
} else {
// No line break at end of last line, write rest of string and
// add one.
b.WriteByte('\t')
b.Write(data)
b.WriteString("\n >")
}
}

58
vendor/k8s.io/klog/v2/internal/severity/severity.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// Copyright 2022 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package severity provides definitions for klog severity (info, warning, ...)
package severity
import (
"strings"
)
// severity identifies the sort of log: info, warning etc. The binding to flag.Value
// is handled in klog.go
type Severity int32 // sync/atomic int32
// These constants identify the log levels in order of increasing severity.
// A message written to a high-severity log file is also written to each
// lower-severity log file.
const (
InfoLog Severity = iota
WarningLog
ErrorLog
FatalLog
NumSeverity = 4
)
// Char contains one shortcut letter per severity level.
const Char = "IWEF"
// Name contains one name per severity level.
var Name = []string{
InfoLog: "INFO",
WarningLog: "WARNING",
ErrorLog: "ERROR",
FatalLog: "FATAL",
}
// ByName looks up a severity level by name.
func ByName(s string) (Severity, bool) {
s = strings.ToUpper(s)
for i, name := range Name {
if name == s {
return Severity(i), true
}
}
return 0, false
}

158
vendor/k8s.io/klog/v2/k8s_references.go generated vendored Normal file
View File

@ -0,0 +1,158 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package klog
import (
"fmt"
"reflect"
"github.com/go-logr/logr"
)
// ObjectRef references a kubernetes object
type ObjectRef struct {
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
}
func (ref ObjectRef) String() string {
if ref.Namespace != "" {
return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name)
}
return ref.Name
}
// MarshalLog ensures that loggers with support for structured output will log
// as a struct by removing the String method via a custom type.
func (ref ObjectRef) MarshalLog() interface{} {
type or ObjectRef
return or(ref)
}
var _ logr.Marshaler = ObjectRef{}
// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
// this interface may expand in the future, but will always be a subset of the
// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
type KMetadata interface {
GetName() string
GetNamespace() string
}
// KObj returns ObjectRef from ObjectMeta
func KObj(obj KMetadata) ObjectRef {
if obj == nil {
return ObjectRef{}
}
if val := reflect.ValueOf(obj); val.Kind() == reflect.Ptr && val.IsNil() {
return ObjectRef{}
}
return ObjectRef{
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
}
}
// KRef returns ObjectRef from name and namespace
func KRef(namespace, name string) ObjectRef {
return ObjectRef{
Name: name,
Namespace: namespace,
}
}
// KObjs returns slice of ObjectRef from an slice of ObjectMeta
//
// DEPRECATED: Use KObjSlice instead, it has better performance.
func KObjs(arg interface{}) []ObjectRef {
s := reflect.ValueOf(arg)
if s.Kind() != reflect.Slice {
return nil
}
objectRefs := make([]ObjectRef, 0, s.Len())
for i := 0; i < s.Len(); i++ {
if v, ok := s.Index(i).Interface().(KMetadata); ok {
objectRefs = append(objectRefs, KObj(v))
} else {
return nil
}
}
return objectRefs
}
// KObjSlice takes a slice of objects that implement the KMetadata interface
// and returns an object that gets logged as a slice of ObjectRef values or a
// string containing those values, depending on whether the logger prefers text
// output or structured output.
//
// An error string is logged when KObjSlice is not passed a suitable slice.
//
// Processing of the argument is delayed until the value actually gets logged,
// in contrast to KObjs where that overhead is incurred regardless of whether
// the result is needed.
func KObjSlice(arg interface{}) interface{} {
return kobjSlice{arg: arg}
}
type kobjSlice struct {
arg interface{}
}
var _ fmt.Stringer = kobjSlice{}
var _ logr.Marshaler = kobjSlice{}
func (ks kobjSlice) String() string {
objectRefs, err := ks.process()
if err != nil {
return err.Error()
}
return fmt.Sprintf("%v", objectRefs)
}
func (ks kobjSlice) MarshalLog() interface{} {
objectRefs, err := ks.process()
if err != nil {
return err.Error()
}
return objectRefs
}
func (ks kobjSlice) process() ([]interface{}, error) {
s := reflect.ValueOf(ks.arg)
switch s.Kind() {
case reflect.Invalid:
// nil parameter, print as nil.
return nil, nil
case reflect.Slice:
// Okay, handle below.
default:
return nil, fmt.Errorf("<KObjSlice needs a slice, got type %T>", ks.arg)
}
objectRefs := make([]interface{}, 0, s.Len())
for i := 0; i < s.Len(); i++ {
item := s.Index(i).Interface()
if item == nil {
objectRefs = append(objectRefs, nil)
} else if v, ok := item.(KMetadata); ok {
objectRefs = append(objectRefs, KObj(v))
} else {
return nil, fmt.Errorf("<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
}
}
return objectRefs, nil
}

1066
vendor/k8s.io/klog/v2/klog.go generated vendored

File diff suppressed because it is too large Load Diff

34
vendor/k8s.io/klog/v2/klog_file.go generated vendored
View File

@ -22,9 +22,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -57,38 +55,6 @@ func init() {
} }
} }
func getUserName() string {
userNameOnce.Do(func() {
// On Windows, the Go 'user' package requires netapi32.dll.
// This affects Windows Nano Server:
// https://github.com/golang/go/issues/21867
// Fallback to using environment variables.
if runtime.GOOS == "windows" {
u := os.Getenv("USERNAME")
if len(u) == 0 {
return
}
// Sanitize the USERNAME since it may contain filepath separators.
u = strings.Replace(u, `\`, "_", -1)
// user.Current().Username normally produces something like 'USERDOMAIN\USERNAME'
d := os.Getenv("USERDOMAIN")
if len(d) != 0 {
userName = d + "_" + u
} else {
userName = u
}
} else {
current, err := user.Current()
if err == nil {
userName = current.Username
}
}
})
return userName
}
// shortHostname returns its argument, truncating at the first period. // shortHostname returns its argument, truncating at the first period.
// For instance, given "www.google.com" it returns "www". // For instance, given "www.google.com" it returns "www".
func shortHostname(hostname string) string { func shortHostname(hostname string) string {

19
vendor/k8s.io/klog/v2/klog_file_others.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
//go:build !windows
// +build !windows
package klog
import (
"os/user"
)
func getUserName() string {
userNameOnce.Do(func() {
current, err := user.Current()
if err == nil {
userName = current.Username
}
})
return userName
}

34
vendor/k8s.io/klog/v2/klog_file_windows.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
//go:build windows
// +build windows
package klog
import (
"os"
"strings"
)
func getUserName() string {
userNameOnce.Do(func() {
// On Windows, the Go 'user' package requires netapi32.dll.
// This affects Windows Nano Server:
// https://github.com/golang/go/issues/21867
// Fallback to using environment variables.
u := os.Getenv("USERNAME")
if len(u) == 0 {
return
}
// Sanitize the USERNAME since it may contain filepath separators.
u = strings.Replace(u, `\`, "_", -1)
// user.Current().Username normally produces something like 'USERDOMAIN\USERNAME'
d := os.Getenv("USERDOMAIN")
if len(d) != 0 {
userName = d + "_" + u
} else {
userName = u
}
})
return userName
}

87
vendor/k8s.io/klog/v2/klogr.go generated vendored Normal file
View File

@ -0,0 +1,87 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package klog
import (
"github.com/go-logr/logr"
"k8s.io/klog/v2/internal/serialize"
)
// NewKlogr returns a logger that is functionally identical to
// klogr.NewWithOptions(klogr.FormatKlog), i.e. it passes through to klog. The
// difference is that it uses a simpler implementation.
func NewKlogr() Logger {
return New(&klogger{})
}
// klogger is a subset of klogr/klogr.go. It had to be copied to break an
// import cycle (klogr wants to use klog, and klog wants to use klogr).
type klogger struct {
level int
callDepth int
prefix string
values []interface{}
}
func (l *klogger) Init(info logr.RuntimeInfo) {
l.callDepth += info.CallDepth
}
func (l klogger) Info(level int, msg string, kvList ...interface{}) {
merged := serialize.MergeKVs(l.values, kvList)
if l.prefix != "" {
msg = l.prefix + ": " + msg
}
V(Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
}
func (l klogger) Enabled(level int) bool {
return V(Level(level)).Enabled()
}
func (l klogger) Error(err error, msg string, kvList ...interface{}) {
merged := serialize.MergeKVs(l.values, kvList)
if l.prefix != "" {
msg = l.prefix + ": " + msg
}
ErrorSDepth(l.callDepth+1, err, msg, merged...)
}
// WithName returns a new logr.Logger with the specified name appended. klogr
// uses '/' characters to separate name elements. Callers should not pass '/'
// in the provided name string, but this library does not actually enforce that.
func (l klogger) WithName(name string) logr.LogSink {
if len(l.prefix) > 0 {
l.prefix = l.prefix + "/"
}
l.prefix += name
return &l
}
func (l klogger) WithValues(kvList ...interface{}) logr.LogSink {
l.values = serialize.WithValues(l.values, kvList)
return &l
}
func (l klogger) WithCallDepth(depth int) logr.LogSink {
l.callDepth += depth
return &l
}
var _ logr.LogSink = &klogger{}
var _ logr.CallDepthLogSink = &klogger{}

1
vendor/k8s.io/mount-utils/OWNERS generated vendored
View File

@ -11,6 +11,5 @@ approvers:
- jingxu97 - jingxu97
- saad-ali - saad-ali
- jsafrane - jsafrane
labels: labels:
- sig/storage - sig/storage

View File

@ -28,3 +28,4 @@ Code of Conduct](code-of-conduct.md).
### Contibution Guidelines ### Contibution Guidelines
See [CONTRIBUTING.md](CONTRIBUTING.md) for more information. See [CONTRIBUTING.md](CONTRIBUTING.md) for more information.

View File

@ -32,8 +32,9 @@ type FakeMounter struct {
MountCheckErrors map[string]error MountCheckErrors map[string]error
// Some tests run things in parallel, make sure the mounter does not produce // Some tests run things in parallel, make sure the mounter does not produce
// any golang's DATA RACE warnings. // any golang's DATA RACE warnings.
mutex sync.Mutex mutex sync.Mutex
UnmountFunc UnmountFunc UnmountFunc UnmountFunc
skipMountPointCheck bool
} }
// UnmountFunc is a function callback to be executed during the Unmount() call. // UnmountFunc is a function callback to be executed during the Unmount() call.
@ -64,6 +65,11 @@ func NewFakeMounter(mps []MountPoint) *FakeMounter {
} }
} }
func (f *FakeMounter) WithSkipMountPointCheck() *FakeMounter {
f.skipMountPointCheck = true
return f
}
// ResetLog clears all the log entries in FakeMounter // ResetLog clears all the log entries in FakeMounter
func (f *FakeMounter) ResetLog() { func (f *FakeMounter) ResetLog() {
f.mutex.Lock() f.mutex.Lock()
@ -212,6 +218,18 @@ func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil return true, nil
} }
func (f *FakeMounter) CanSafelySkipMountPointCheck() bool {
return f.skipMountPointCheck
}
func (f *FakeMounter) IsMountPoint(file string) (bool, error) {
notMnt, err := f.IsLikelyNotMountPoint(file)
if err != nil {
return false, err
}
return !notMnt, nil
}
// GetMountRefs finds all mount references to the path, returns a // GetMountRefs finds all mount references to the path, returns a
// list of paths. // list of paths.
func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) { func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {

90
vendor/k8s.io/mount-utils/mount.go generated vendored
View File

@ -21,7 +21,6 @@ package mount
import ( import (
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@ -66,6 +65,18 @@ type Interface interface {
// care about such situations, this is a faster alternative to calling List() // care about such situations, this is a faster alternative to calling List()
// and scanning that output. // and scanning that output.
IsLikelyNotMountPoint(file string) (bool, error) IsLikelyNotMountPoint(file string) (bool, error)
// CanSafelySkipMountPointCheck indicates whether this mounter returns errors on
// operations for targets that are not mount points. If this returns true, no such
// errors will be returned.
CanSafelySkipMountPointCheck() bool
// IsMountPoint determines if a directory is a mountpoint.
// It should return ErrNotExist when the directory does not exist.
// IsMountPoint is more expensive than IsLikelyNotMountPoint.
// IsMountPoint detects bind mounts in linux.
// IsMountPoint may enumerate all the mountpoints using List() and
// the list of mountpoints may be large, then it uses
// isMountPointMatch to evaluate whether the directory is a mountpoint.
IsMountPoint(file string) (bool, error)
// GetMountRefs finds all mount references to pathname, returning a slice of // GetMountRefs finds all mount references to pathname, returning a slice of
// paths. Pathname can be a mountpoint path or a normal directory // paths. Pathname can be a mountpoint path or a normal directory
// (for bind mount). On Linux, pathname is excluded from the slice. // (for bind mount). On Linux, pathname is excluded from the slice.
@ -154,7 +165,15 @@ func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string,
// be used by callers that pass sensitive material (like passwords) as mount // be used by callers that pass sensitive material (like passwords) as mount
// options. // options.
func (mounter *SafeFormatAndMount) FormatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error { func (mounter *SafeFormatAndMount) FormatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
return mounter.formatAndMountSensitive(source, target, fstype, options, sensitiveOptions) return mounter.FormatAndMountSensitiveWithFormatOptions(source, target, fstype, options, sensitiveOptions, nil /* formatOptions */)
}
// FormatAndMountSensitiveWithFormatOptions behaves exactly the same as
// FormatAndMountSensitive, but allows for options to be passed when the disk
// is formatted. These options are NOT validated in any way and should never
// come directly from untrusted user input as that would be an injection risk.
func (mounter *SafeFormatAndMount) FormatAndMountSensitiveWithFormatOptions(source string, target string, fstype string, options []string, sensitiveOptions []string, formatOptions []string) error {
return mounter.formatAndMountSensitive(source, target, fstype, options, sensitiveOptions, formatOptions)
} }
// getMountRefsByDev finds all references to the device provided // getMountRefsByDev finds all references to the device provided
@ -187,6 +206,24 @@ func getMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
return refs, nil return refs, nil
} }
// IsNotMountPoint determines if a directory is a mountpoint.
// It should return ErrNotExist when the directory does not exist.
// IsNotMountPoint is more expensive than IsLikelyNotMountPoint
// and depends on IsMountPoint.
//
// If an error occurs, it returns true (assuming it is not a mountpoint)
// when ErrNotExist is returned for callers similar to IsLikelyNotMountPoint.
//
// Deprecated: This function is kept to keep changes backward compatible with
// previous library version. Callers should prefer mounter.IsMountPoint.
func IsNotMountPoint(mounter Interface, file string) (bool, error) {
isMnt, err := mounter.IsMountPoint(file)
if err != nil {
return true, err
}
return !isMnt, nil
}
// GetDeviceNameFromMount given a mnt point, find the device from /proc/mounts // GetDeviceNameFromMount given a mnt point, find the device from /proc/mounts
// returns the device name, reference count, and error code. // returns the device name, reference count, and error code.
func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) { func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) {
@ -220,56 +257,11 @@ func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, e
return device, refCount, nil return device, refCount, nil
} }
// IsNotMountPoint determines if a directory is a mountpoint.
// It should return ErrNotExist when the directory does not exist.
// IsNotMountPoint is more expensive than IsLikelyNotMountPoint.
// IsNotMountPoint detects bind mounts in linux.
// IsNotMountPoint enumerates all the mountpoints using List() and
// the list of mountpoints may be large, then it uses
// isMountPointMatch to evaluate whether the directory is a mountpoint.
func IsNotMountPoint(mounter Interface, file string) (bool, error) {
// IsLikelyNotMountPoint provides a quick check
// to determine whether file IS A mountpoint.
notMnt, notMntErr := mounter.IsLikelyNotMountPoint(file)
if notMntErr != nil && os.IsPermission(notMntErr) {
// We were not allowed to do the simple stat() check, e.g. on NFS with
// root_squash. Fall back to /proc/mounts check below.
notMnt = true
notMntErr = nil
}
if notMntErr != nil {
return notMnt, notMntErr
}
// identified as mountpoint, so return this fact.
if notMnt == false {
return notMnt, nil
}
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts.
resolvedFile, err := filepath.EvalSymlinks(file)
if err != nil {
return true, err
}
// check all mountpoints since IsLikelyNotMountPoint
// is not reliable for some mountpoint types.
mountPoints, mountPointsErr := mounter.List()
if mountPointsErr != nil {
return notMnt, mountPointsErr
}
for _, mp := range mountPoints {
if isMountPointMatch(mp, resolvedFile) {
notMnt = false
break
}
}
return notMnt, nil
}
// MakeBindOpts detects whether a bind mount is being requested and makes the remount options to // MakeBindOpts detects whether a bind mount is being requested and makes the remount options to
// use in case of bind mount, due to the fact that bind mount doesn't respect mount options. // use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
// The list equals: // The list equals:
// options - 'bind' + 'remount' (no duplicate) //
// options - 'bind' + 'remount' (no duplicate)
func MakeBindOpts(options []string) (bool, []string, []string) { func MakeBindOpts(options []string) (bool, []string, []string) {
bind, bindOpts, bindRemountOpts, _ := MakeBindOptsSensitive(options, nil /* sensitiveOptions */) bind, bindOpts, bindRemountOpts, _ := MakeBindOptsSensitive(options, nil /* sensitiveOptions */)
return bind, bindOpts, bindRemountOpts return bind, bindOpts, bindRemountOpts

View File

@ -31,7 +31,7 @@ import (
func CleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool) error { func CleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool) error {
pathExists, pathErr := PathExists(mountPath) pathExists, pathErr := PathExists(mountPath)
if !pathExists && pathErr == nil { if !pathExists && pathErr == nil {
klog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath) klog.Warningf("Warning: mount cleanup skipped because path does not exist: %v", mountPath)
return nil return nil
} }
corruptedMnt := IsCorruptedMnt(pathErr) corruptedMnt := IsCorruptedMnt(pathErr)
@ -44,36 +44,41 @@ func CleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointC
func CleanupMountWithForce(mountPath string, mounter MounterForceUnmounter, extensiveMountPointCheck bool, umountTimeout time.Duration) error { func CleanupMountWithForce(mountPath string, mounter MounterForceUnmounter, extensiveMountPointCheck bool, umountTimeout time.Duration) error {
pathExists, pathErr := PathExists(mountPath) pathExists, pathErr := PathExists(mountPath)
if !pathExists && pathErr == nil { if !pathExists && pathErr == nil {
klog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath) klog.Warningf("Warning: mount cleanup skipped because path does not exist: %v", mountPath)
return nil return nil
} }
corruptedMnt := IsCorruptedMnt(pathErr) corruptedMnt := IsCorruptedMnt(pathErr)
if pathErr != nil && !corruptedMnt { if pathErr != nil && !corruptedMnt {
return fmt.Errorf("Error checking path: %v", pathErr) return fmt.Errorf("Error checking path: %v", pathErr)
} }
var notMnt bool
var err error if corruptedMnt || mounter.CanSafelySkipMountPointCheck() {
if !corruptedMnt { klog.V(4).Infof("unmounting %q (corruptedMount: %t, mounterCanSkipMountPointChecks: %t)",
notMnt, err = removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck) mountPath, corruptedMnt, mounter.CanSafelySkipMountPointCheck())
// if mountPath was not a mount point - we would have attempted to remove mountPath if err := mounter.UnmountWithForce(mountPath, umountTimeout); err != nil {
// and hence return errors if any.
if err != nil || notMnt {
return err return err
} }
return removePath(mountPath)
}
notMnt, err := removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck)
// if mountPath is not a mount point, it's just been removed or there was an error
if err != nil || notMnt {
return err
} }
// Unmount the mount path
klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath) klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath)
if err := mounter.UnmountWithForce(mountPath, umountTimeout); err != nil { if err := mounter.UnmountWithForce(mountPath, umountTimeout); err != nil {
return err return err
} }
notMnt, err = removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck) notMnt, err = removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck)
// mountPath is not a mount point we should return whatever error we saw // if mountPath is not a mount point, it's either just been removed or there was an error
if notMnt { if notMnt {
return err return err
} }
return fmt.Errorf("Failed to unmount path %v", mountPath) // mountPath is still a mount point
return fmt.Errorf("failed to cleanup mount point %v", mountPath)
} }
// doCleanupMountPoint unmounts the given path and // doCleanupMountPoint unmounts the given path and
@ -82,31 +87,35 @@ func CleanupMountWithForce(mountPath string, mounter MounterForceUnmounter, exte
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint. // IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
// IsNotMountPoint is more expensive but properly handles bind mounts within the same fs. // IsNotMountPoint is more expensive but properly handles bind mounts within the same fs.
// if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, and the mount point check // if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, and the mount point check
// will be skipped // will be skipped. The mount point check will also be skipped if the mounter supports it.
func doCleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool, corruptedMnt bool) error { func doCleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool, corruptedMnt bool) error {
var notMnt bool if corruptedMnt || mounter.CanSafelySkipMountPointCheck() {
var err error klog.V(4).Infof("unmounting %q (corruptedMount: %t, mounterCanSkipMountPointChecks: %t)",
if !corruptedMnt { mountPath, corruptedMnt, mounter.CanSafelySkipMountPointCheck())
notMnt, err = removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck) if err := mounter.Unmount(mountPath); err != nil {
// if mountPath was not a mount point - we would have attempted to remove mountPath
// and hence return errors if any.
if err != nil || notMnt {
return err return err
} }
return removePath(mountPath)
}
notMnt, err := removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck)
// if mountPath is not a mount point, it's just been removed or there was an error
if err != nil || notMnt {
return err
} }
// Unmount the mount path
klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath) klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath)
if err := mounter.Unmount(mountPath); err != nil { if err := mounter.Unmount(mountPath); err != nil {
return err return err
} }
notMnt, err = removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck) notMnt, err = removePathIfNotMountPoint(mountPath, mounter, extensiveMountPointCheck)
// mountPath is not a mount point we should return whatever error we saw // if mountPath is not a mount point, it's either just been removed or there was an error
if notMnt { if notMnt {
return err return err
} }
return fmt.Errorf("Failed to unmount path %v", mountPath) // mountPath is still a mount point
return fmt.Errorf("failed to cleanup mount point %v", mountPath)
} }
// removePathIfNotMountPoint verifies if given mountPath is a mount point if not it attempts // removePathIfNotMountPoint verifies if given mountPath is a mount point if not it attempts
@ -135,3 +144,14 @@ func removePathIfNotMountPoint(mountPath string, mounter Interface, extensiveMou
} }
return notMnt, nil return notMnt, nil
} }
// removePath attempts to remove the directory. Returns nil if the directory was removed or does not exist.
func removePath(mountPath string) error {
klog.V(4).Infof("Warning: deleting path %q", mountPath)
err := os.Remove(mountPath)
if os.IsNotExist(err) {
klog.V(4).Infof("%q does not exist", mountPath)
return nil
}
return err
}

View File

@ -136,7 +136,7 @@ func ParseMountInfo(filename string) ([]MountInfo, error) {
Minor: minor, Minor: minor,
Root: fields[3], Root: fields[3],
MountPoint: fields[4], MountPoint: fields[4],
MountOptions: strings.Split(fields[5], ","), MountOptions: splitMountOptions(fields[5]),
} }
// All fields until "-" are "optional fields". // All fields until "-" are "optional fields".
i := 6 i := 6
@ -150,12 +150,26 @@ func ParseMountInfo(filename string) ([]MountInfo, error) {
} }
info.FsType = fields[i] info.FsType = fields[i]
info.Source = fields[i+1] info.Source = fields[i+1]
info.SuperOptions = strings.Split(fields[i+2], ",") info.SuperOptions = splitMountOptions(fields[i+2])
infos = append(infos, info) infos = append(infos, info)
} }
return infos, nil return infos, nil
} }
// splitMountOptions parses comma-separated list of mount options into an array.
// It respects double quotes - commas in them are not considered as the option separator.
func splitMountOptions(s string) []string {
inQuotes := false
list := strings.FieldsFunc(s, func(r rune) bool {
if r == '"' {
inQuotes = !inQuotes
}
// Report a new field only when outside of double quotes.
return r == ',' && !inQuotes
})
return list
}
// isMountPointMatch returns true if the path in mp is the same as dir. // isMountPointMatch returns true if the path in mp is the same as dir.
// Handles case where mountpoint dir has been renamed due to stale NFS mount. // Handles case where mountpoint dir has been renamed due to stale NFS mount.
func isMountPointMatch(mp MountPoint, dir string) bool { func isMountPointMatch(mp MountPoint, dir string) bool {

View File

@ -21,7 +21,10 @@ package mount
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io/fs"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -30,6 +33,8 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/moby/sys/mountinfo"
"k8s.io/klog/v2" "k8s.io/klog/v2"
utilexec "k8s.io/utils/exec" utilexec "k8s.io/utils/exec"
utilio "k8s.io/utils/io" utilio "k8s.io/utils/io"
@ -48,14 +53,18 @@ const (
fsckErrorsUncorrected = 4 fsckErrorsUncorrected = 4
// Error thrown by exec cmd.Run() when process spawned by cmd.Start() completes before cmd.Wait() is called (see - k/k issue #103753) // Error thrown by exec cmd.Run() when process spawned by cmd.Start() completes before cmd.Wait() is called (see - k/k issue #103753)
errNoChildProcesses = "wait: no child processes" errNoChildProcesses = "wait: no child processes"
// Error returned by some `umount` implementations when the specified path is not a mount point
errNotMounted = "not mounted"
) )
// Mounter provides the default implementation of mount.Interface // Mounter provides the default implementation of mount.Interface
// for the linux platform. This implementation assumes that the // for the linux platform. This implementation assumes that the
// kubelet is running in the host's root mount namespace. // kubelet is running in the host's root mount namespace.
type Mounter struct { type Mounter struct {
mounterPath string mounterPath string
withSystemd bool withSystemd *bool
trySystemd bool
withSafeNotMountedBehavior bool
} }
var _ MounterForceUnmounter = &Mounter{} var _ MounterForceUnmounter = &Mounter{}
@ -65,11 +74,39 @@ var _ MounterForceUnmounter = &Mounter{}
// mounterPath allows using an alternative to `/bin/mount` for mounting. // mounterPath allows using an alternative to `/bin/mount` for mounting.
func New(mounterPath string) Interface { func New(mounterPath string) Interface {
return &Mounter{ return &Mounter{
mounterPath: mounterPath, mounterPath: mounterPath,
withSystemd: detectSystemd(), trySystemd: true,
withSafeNotMountedBehavior: detectSafeNotMountedBehavior(),
} }
} }
// NewWithoutSystemd returns a Linux specific mount.Interface for the current
// system. It provides options to override the default mounter behavior.
// mounterPath allows using an alternative to `/bin/mount` for mounting. Any
// detection for systemd functionality is disabled with this Mounter.
func NewWithoutSystemd(mounterPath string) Interface {
return &Mounter{
mounterPath: mounterPath,
trySystemd: false,
withSafeNotMountedBehavior: detectSafeNotMountedBehavior(),
}
}
// hasSystemd validates that the withSystemd bool is set, if it is not,
// detectSystemd will be called once for this Mounter instance.
func (mounter *Mounter) hasSystemd() bool {
if !mounter.trySystemd {
mounter.withSystemd = &mounter.trySystemd
}
if mounter.withSystemd == nil {
withSystemd := detectSystemd()
mounter.withSystemd = &withSystemd
}
return *mounter.withSystemd
}
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must // Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
// be an empty string in case it's not required, e.g. for remount, or for auto filesystem // be an empty string in case it's not required, e.g. for remount, or for auto filesystem
// type, where kernel handles fstype for you. The mount 'options' is a list of options, // type, where kernel handles fstype for you. The mount 'options' is a list of options,
@ -90,11 +127,11 @@ func (mounter *Mounter) MountSensitive(source string, target string, fstype stri
mounterPath := "" mounterPath := ""
bind, bindOpts, bindRemountOpts, bindRemountOptsSensitive := MakeBindOptsSensitive(options, sensitiveOptions) bind, bindOpts, bindRemountOpts, bindRemountOptsSensitive := MakeBindOptsSensitive(options, sensitiveOptions)
if bind { if bind {
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts, bindRemountOptsSensitive, nil /* mountFlags */, true) err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts, bindRemountOptsSensitive, nil /* mountFlags */, mounter.trySystemd)
if err != nil { if err != nil {
return err return err
} }
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts, bindRemountOptsSensitive, nil /* mountFlags */, true) return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts, bindRemountOptsSensitive, nil /* mountFlags */, mounter.trySystemd)
} }
// The list of filesystems that require containerized mounter on GCI image cluster // The list of filesystems that require containerized mounter on GCI image cluster
fsTypesNeedMounter := map[string]struct{}{ fsTypesNeedMounter := map[string]struct{}{
@ -106,7 +143,7 @@ func (mounter *Mounter) MountSensitive(source string, target string, fstype stri
if _, ok := fsTypesNeedMounter[fstype]; ok { if _, ok := fsTypesNeedMounter[fstype]; ok {
mounterPath = mounter.mounterPath mounterPath = mounter.mounterPath
} }
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions, nil /* mountFlags */, true) return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions, nil /* mountFlags */, mounter.trySystemd)
} }
// MountSensitiveWithoutSystemd is the same as MountSensitive() but disable using systemd mount. // MountSensitiveWithoutSystemd is the same as MountSensitive() but disable using systemd mount.
@ -149,7 +186,7 @@ func (mounter *Mounter) doMount(mounterPath string, mountCmd string, source stri
mountCmd = mounterPath mountCmd = mounterPath
} }
if mounter.withSystemd && systemdMountRequired { if systemdMountRequired && mounter.hasSystemd() {
// Try to run mount via systemd-run --scope. This will escape the // Try to run mount via systemd-run --scope. This will escape the
// service where kubelet runs and any fuse daemons will be started in a // service where kubelet runs and any fuse daemons will be started in a
// specific scope. kubelet service than can be restarted without killing // specific scope. kubelet service than can be restarted without killing
@ -223,6 +260,36 @@ func detectSystemd() bool {
return true return true
} }
// detectSafeNotMountedBehavior returns true if the umount implementation replies "not mounted"
// when the specified path is not mounted. When not sure (permission errors, ...), it returns false.
// When possible, we will trust umount's message and avoid doing our own mount point checks.
// More info: https://github.com/util-linux/util-linux/blob/v2.2/mount/umount.c#L179
func detectSafeNotMountedBehavior() bool {
return detectSafeNotMountedBehaviorWithExec(utilexec.New())
}
// detectSafeNotMountedBehaviorWithExec is for testing with FakeExec.
func detectSafeNotMountedBehaviorWithExec(exec utilexec.Interface) bool {
// create a temp dir and try to umount it
path, err := ioutil.TempDir("", "kubelet-detect-safe-umount")
if err != nil {
klog.V(4).Infof("Cannot create temp dir to detect safe 'not mounted' behavior: %v", err)
return false
}
defer os.RemoveAll(path)
cmd := exec.Command("umount", path)
output, err := cmd.CombinedOutput()
if err != nil {
if strings.Contains(string(output), errNotMounted) {
klog.V(4).Infof("Detected umount with safe 'not mounted' behavior")
return true
}
klog.V(4).Infof("'umount %s' failed with: %v, output: %s", path, err, string(output))
}
klog.V(4).Infof("Detected umount with unsafe 'not mounted' behavior")
return false
}
// MakeMountArgs makes the arguments to the mount(8) command. // MakeMountArgs makes the arguments to the mount(8) command.
// options MUST not contain sensitive material (like passwords). // options MUST not contain sensitive material (like passwords).
func MakeMountArgs(source, target, fstype string, options []string) (mountArgs []string) { func MakeMountArgs(source, target, fstype string, options []string) (mountArgs []string) {
@ -290,6 +357,7 @@ func AddSystemdScopeSensitive(systemdRunPath, mountName, command string, args []
} }
// Unmount unmounts the target. // Unmount unmounts the target.
// If the mounter has safe "not mounted" behavior, no error will be returned when the target is not a mount point.
func (mounter *Mounter) Unmount(target string) error { func (mounter *Mounter) Unmount(target string) error {
klog.V(4).Infof("Unmounting %s", target) klog.V(4).Infof("Unmounting %s", target)
command := exec.Command("umount", target) command := exec.Command("umount", target)
@ -303,6 +371,10 @@ func (mounter *Mounter) Unmount(target string) error {
// Rewrite err with the actual exit error of the process. // Rewrite err with the actual exit error of the process.
err = &exec.ExitError{ProcessState: command.ProcessState} err = &exec.ExitError{ProcessState: command.ProcessState}
} }
if mounter.withSafeNotMountedBehavior && strings.Contains(string(output), errNotMounted) {
klog.V(4).Infof("ignoring 'not mounted' error for %s", target)
return nil
}
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", err, target, string(output)) return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", err, target, string(output))
} }
return nil return nil
@ -311,7 +383,7 @@ func (mounter *Mounter) Unmount(target string) error {
// UnmountWithForce unmounts given target but will retry unmounting with force option // UnmountWithForce unmounts given target but will retry unmounting with force option
// after given timeout. // after given timeout.
func (mounter *Mounter) UnmountWithForce(target string, umountTimeout time.Duration) error { func (mounter *Mounter) UnmountWithForce(target string, umountTimeout time.Duration) error {
err := tryUnmount(target, umountTimeout) err := tryUnmount(mounter, target, umountTimeout)
if err != nil { if err != nil {
if err == context.DeadlineExceeded { if err == context.DeadlineExceeded {
klog.V(2).Infof("Timed out waiting for unmount of %s, trying with -f", target) klog.V(2).Infof("Timed out waiting for unmount of %s, trying with -f", target)
@ -351,6 +423,11 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil return true, nil
} }
// CanSafelySkipMountPointCheck relies on the detected behavior of umount when given a target that is not a mount point.
func (mounter *Mounter) CanSafelySkipMountPointCheck() bool {
return mounter.withSafeNotMountedBehavior
}
// GetMountRefs finds all mount references to pathname, returns a // GetMountRefs finds all mount references to pathname, returns a
// list of paths. Path could be a mountpoint or a normal // list of paths. Path could be a mountpoint or a normal
// directory (for bind mount). // directory (for bind mount).
@ -387,13 +464,15 @@ func (mounter *SafeFormatAndMount) checkAndRepairFilesystem(source string) error
return NewMountError(HasFilesystemErrors, "'fsck' found errors on device %s but could not correct them: %s", source, string(out)) return NewMountError(HasFilesystemErrors, "'fsck' found errors on device %s but could not correct them: %s", source, string(out))
case isExitError && ee.ExitStatus() > fsckErrorsUncorrected: case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
klog.Infof("`fsck` error %s", string(out)) klog.Infof("`fsck` error %s", string(out))
default:
klog.Warningf("fsck on device %s failed with error %v, output: %v", source, err, string(out))
} }
} }
return nil return nil
} }
// formatAndMount uses unix utils to format and mount the given disk // formatAndMount uses unix utils to format and mount the given disk
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error { func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string, formatOptions []string) error {
readOnly := false readOnly := false
for _, option := range options { for _, option := range options {
if option == "ro" { if option == "ro" {
@ -445,6 +524,7 @@ func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target
source, source,
} }
} }
args = append(formatOptions, args...)
klog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args) klog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
output, err := mounter.Exec.Command("mkfs."+fstype, args...).CombinedOutput() output, err := mounter.Exec.Command("mkfs."+fstype, args...).CombinedOutput()
@ -637,8 +717,65 @@ func SearchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
return refs, nil return refs, nil
} }
// IsMountPoint determines if a file is a mountpoint.
// It first detects bind & any other mountpoints using
// MountedFast function. If the MountedFast function returns
// sure as true and err as nil, then a mountpoint is detected
// successfully. When an error is returned by MountedFast, the
// following is true:
// 1. All errors are returned with IsMountPoint as false
// except os.IsPermission.
// 2. When os.IsPermission is returned by MountedFast, List()
// is called to confirm if the given file is a mountpoint are not.
//
// os.ErrNotExist should always be returned if a file does not exist
// as callers have in past relied on this error and not fallback.
//
// When MountedFast returns sure as false and err as nil (eg: in
// case of bindmounts on kernel version 5.10- ); mounter.List()
// endpoint is called to enumerate all the mountpoints and check if
// it is mountpoint match or not.
func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
isMnt, sure, isMntErr := mountinfo.MountedFast(file)
if sure && isMntErr == nil {
return isMnt, nil
}
if isMntErr != nil {
if errors.Is(isMntErr, fs.ErrNotExist) {
return false, fs.ErrNotExist
}
// We were not allowed to do the simple stat() check, e.g. on NFS with
// root_squash. Fall back to /proc/mounts check below when
// fs.ErrPermission is returned.
if !errors.Is(isMntErr, fs.ErrPermission) {
return false, isMntErr
}
}
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts.
resolvedFile, err := filepath.EvalSymlinks(file)
if err != nil {
if errors.Is(isMntErr, fs.ErrNotExist) {
return false, fs.ErrNotExist
}
return false, err
}
// check all mountpoints since MountedFast is not sure.
// is not reliable for some mountpoint types.
mountPoints, mountPointsErr := mounter.List()
if mountPointsErr != nil {
return false, mountPointsErr
}
for _, mp := range mountPoints {
if isMountPointMatch(mp, resolvedFile) {
return true, nil
}
}
return false, nil
}
// tryUnmount calls plain "umount" and waits for unmountTimeout for it to finish. // tryUnmount calls plain "umount" and waits for unmountTimeout for it to finish.
func tryUnmount(path string, unmountTimeout time.Duration) error { func tryUnmount(mounter *Mounter, path string, unmountTimeout time.Duration) error {
klog.V(4).Infof("Unmounting %s", path) klog.V(4).Infof("Unmounting %s", path)
ctx, cancel := context.WithTimeout(context.Background(), unmountTimeout) ctx, cancel := context.WithTimeout(context.Background(), unmountTimeout)
defer cancel() defer cancel()
@ -653,6 +790,10 @@ func tryUnmount(path string, unmountTimeout time.Duration) error {
} }
if cmderr != nil { if cmderr != nil {
if mounter.withSafeNotMountedBehavior && strings.Contains(string(out), errNotMounted) {
klog.V(4).Infof("ignoring 'not mounted' error for %s", path)
return nil
}
return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", cmderr, path, string(out)) return fmt.Errorf("unmount failed: %v\nUnmounting arguments: %s\nOutput: %s", cmderr, path, string(out))
} }
return nil return nil

View File

@ -74,15 +74,32 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, errUnsupported return true, errUnsupported
} }
// CanSafelySkipMountPointCheck always returns false on unsupported platforms
func (mounter *Mounter) CanSafelySkipMountPointCheck() bool {
return false
}
// IsMountPoint determines if a directory is a mountpoint.
// It always returns an error on unsupported platforms.
func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
return false, errUnsupported
}
// GetMountRefs always returns an error on unsupported platforms // GetMountRefs always returns an error on unsupported platforms
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
return nil, errUnsupported return nil, errUnsupported
} }
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error { func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string, formatOptions []string) error {
return mounter.Interface.Mount(source, target, fstype, options) return mounter.Interface.Mount(source, target, fstype, options)
} }
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) { func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
return true, errUnsupported return true, errUnsupported
} }
// IsMountPoint determines if a directory is a mountpoint.
// It always returns an error on unsupported platforms.
func (mounter *SafeFormatAndMount) IsMountPoint(file string) (bool, error) {
return false, errUnsupported
}

View File

@ -244,6 +244,20 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
return true, nil return true, nil
} }
// CanSafelySkipMountPointCheck always returns false on Windows
func (mounter *Mounter) CanSafelySkipMountPointCheck() bool {
return false
}
// IsMountPoint: determines if a directory is a mountpoint.
func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
isNotMnt, err := mounter.IsLikelyNotMountPoint(file)
if err != nil {
return false, err
}
return !isNotMnt, nil
}
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows // GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
windowsPath := NormalizeWindowsPath(pathname) windowsPath := NormalizeWindowsPath(pathname)
@ -259,7 +273,7 @@ func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
return []string{pathname}, nil return []string{pathname}, nil
} }
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error { func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string, formatOptions []string) error {
// Try to mount the disk // Try to mount the disk
klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target) klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
@ -274,8 +288,12 @@ func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target
} }
// format disk if it is unformatted(raw) // format disk if it is unformatted(raw)
formatOptionsUnwrapped := ""
if len(formatOptions) > 0 {
formatOptionsUnwrapped = " " + strings.Join(formatOptions, " ")
}
cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle GPT -PassThru"+ cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle GPT -PassThru"+
" | New-Partition -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype) " | New-Partition -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false%s", source, fstype, formatOptionsUnwrapped)
if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil { if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil {
return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output)) return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
} }

View File

@ -28,6 +28,10 @@ import (
utilexec "k8s.io/utils/exec" utilexec "k8s.io/utils/exec"
) )
const (
blockDev = "blockdev"
)
// ResizeFs Provides support for resizing file systems // ResizeFs Provides support for resizing file systems
type ResizeFs struct { type ResizeFs struct {
exec utilexec.Interface exec utilexec.Interface
@ -104,6 +108,17 @@ func (resizefs *ResizeFs) btrfsResize(deviceMountPath string) (bool, error) {
} }
func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string) (bool, error) { func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string) (bool, error) {
// Do nothing if device is mounted as readonly
readonly, err := resizefs.getDeviceRO(devicePath)
if err != nil {
return false, err
}
if readonly {
klog.V(3).Infof("ResizeFs.needResize - no resize possible since filesystem %s is readonly", devicePath)
return false, nil
}
deviceSize, err := resizefs.getDeviceSize(devicePath) deviceSize, err := resizefs.getDeviceSize(devicePath)
if err != nil { if err != nil {
return false, err return false, err
@ -129,6 +144,9 @@ func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string)
case "xfs": case "xfs":
blockSize, fsSize, err = resizefs.getXFSSize(deviceMountPath) blockSize, fsSize, err = resizefs.getXFSSize(deviceMountPath)
klog.V(5).Infof("Xfs size: filesystem size=%d, block size=%d, err=%v", fsSize, blockSize, err) klog.V(5).Infof("Xfs size: filesystem size=%d, block size=%d, err=%v", fsSize, blockSize, err)
case "btrfs":
blockSize, fsSize, err = resizefs.getBtrfsSize(devicePath)
klog.V(5).Infof("Btrfs size: filesystem size=%d, block size=%d, err=%v", fsSize, blockSize, err)
default: default:
klog.Errorf("Not able to parse given filesystem info. fsType: %s, will not resize", format) klog.Errorf("Not able to parse given filesystem info. fsType: %s, will not resize", format)
return false, fmt.Errorf("Could not parse fs info on given filesystem format: %s. Supported fs types are: xfs, ext3, ext4", format) return false, fmt.Errorf("Could not parse fs info on given filesystem format: %s. Supported fs types are: xfs, ext3, ext4", format)
@ -144,7 +162,7 @@ func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string)
return true, nil return true, nil
} }
func (resizefs *ResizeFs) getDeviceSize(devicePath string) (uint64, error) { func (resizefs *ResizeFs) getDeviceSize(devicePath string) (uint64, error) {
output, err := resizefs.exec.Command("blockdev", "--getsize64", devicePath).CombinedOutput() output, err := resizefs.exec.Command(blockDev, "--getsize64", devicePath).CombinedOutput()
outStr := strings.TrimSpace(string(output)) outStr := strings.TrimSpace(string(output))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to read size of device %s: %s: %s", devicePath, err, outStr) return 0, fmt.Errorf("failed to read size of device %s: %s: %s", devicePath, err, outStr)
@ -156,6 +174,22 @@ func (resizefs *ResizeFs) getDeviceSize(devicePath string) (uint64, error) {
return size, nil return size, nil
} }
func (resizefs *ResizeFs) getDeviceRO(devicePath string) (bool, error) {
output, err := resizefs.exec.Command(blockDev, "--getro", devicePath).CombinedOutput()
outStr := strings.TrimSpace(string(output))
if err != nil {
return false, fmt.Errorf("failed to get readonly bit from device %s: %s: %s", devicePath, err, outStr)
}
switch outStr {
case "0":
return false, nil
case "1":
return true, nil
default:
return false, fmt.Errorf("Failed readonly device check. Expected 1 or 0, got '%s'", outStr)
}
}
func (resizefs *ResizeFs) getExtSize(devicePath string) (uint64, uint64, error) { func (resizefs *ResizeFs) getExtSize(devicePath string) (uint64, uint64, error) {
output, err := resizefs.exec.Command("dumpe2fs", "-h", devicePath).CombinedOutput() output, err := resizefs.exec.Command("dumpe2fs", "-h", devicePath).CombinedOutput()
if err != nil { if err != nil {
@ -190,6 +224,51 @@ func (resizefs *ResizeFs) getXFSSize(devicePath string) (uint64, uint64, error)
return blockSize, blockSize * blockCount, nil return blockSize, blockSize * blockCount, nil
} }
func (resizefs *ResizeFs) getBtrfsSize(devicePath string) (uint64, uint64, error) {
output, err := resizefs.exec.Command("btrfs", "inspect-internal", "dump-super", "-f", devicePath).CombinedOutput()
if err != nil {
return 0, 0, fmt.Errorf("failed to read size of filesystem on %s: %s: %s", devicePath, err, string(output))
}
blockSize, totalBytes, _ := resizefs.parseBtrfsInfoOutput(string(output), "sectorsize", "total_bytes")
if blockSize == 0 {
return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath)
}
if totalBytes == 0 {
return 0, 0, fmt.Errorf("could not find total size of device %s", devicePath)
}
return blockSize, totalBytes, nil
}
func (resizefs *ResizeFs) parseBtrfsInfoOutput(cmdOutput string, blockSizeKey string, totalBytesKey string) (uint64, uint64, error) {
lines := strings.Split(cmdOutput, "\n")
var blockSize, blockCount uint64
var err error
for _, line := range lines {
tokens := strings.Fields(line)
if len(tokens) != 2 {
continue
}
key, value := strings.ToLower(strings.TrimSpace(tokens[0])), strings.ToLower(strings.TrimSpace(tokens[1]))
if key == blockSizeKey {
blockSize, err = strconv.ParseUint(value, 10, 64)
if err != nil {
return 0, 0, fmt.Errorf("failed to parse block size %s: %s", value, err)
}
}
if key == totalBytesKey {
blockCount, err = strconv.ParseUint(value, 10, 64)
if err != nil {
return 0, 0, fmt.Errorf("failed to parse total size %s: %s", value, err)
}
}
}
return blockSize, blockCount, err
}
func (resizefs *ResizeFs) parseFsInfoOutput(cmdOutput string, spliter string, blockSizeKey string, blockCountKey string) (uint64, uint64, error) { func (resizefs *ResizeFs) parseFsInfoOutput(cmdOutput string, spliter string, blockSizeKey string, blockCountKey string) (uint64, uint64, error) {
lines := strings.Split(cmdOutput, "\n") lines := strings.Split(cmdOutput, "\n")
var blockSize, blockCount uint64 var blockSize, blockCount uint64

View File

@ -39,3 +39,8 @@ func NewResizeFs(exec utilexec.Interface) *ResizeFs {
func (resizefs *ResizeFs) Resize(devicePath string, deviceMountPath string) (bool, error) { func (resizefs *ResizeFs) Resize(devicePath string, deviceMountPath string) (bool, error) {
return false, fmt.Errorf("Resize is not supported for this build") return false, fmt.Errorf("Resize is not supported for this build")
} }
// NeedResize check whether mounted volume needs resize
func (resizefs *ResizeFs) NeedResize(devicePath string, deviceMountPath string) (bool, error) {
return false, fmt.Errorf("NeedResize is not supported for this build")
}

10
vendor/k8s.io/utils/clock/clock.go generated vendored
View File

@ -63,6 +63,16 @@ type WithDelayedExecution interface {
AfterFunc(d time.Duration, f func()) Timer AfterFunc(d time.Duration, f func()) Timer
} }
// WithTickerAndDelayedExecution allows for injecting fake or real clocks
// into code that needs Ticker and AfterFunc functionality
type WithTickerAndDelayedExecution interface {
WithTicker
// AfterFunc executes f in its own goroutine after waiting
// for d duration and returns a Timer whose channel can be
// closed by calling Stop() on the Timer.
AfterFunc(d time.Duration, f func()) Timer
}
// Ticker defines the Ticker interface. // Ticker defines the Ticker interface.
type Ticker interface { type Ticker interface {
C() <-chan time.Time C() <-chan time.Time

View File

@ -239,7 +239,8 @@ func (f *FakeClock) Sleep(d time.Duration) {
// IntervalClock implements clock.PassiveClock, but each invocation of Now steps the clock forward the specified duration. // IntervalClock implements clock.PassiveClock, but each invocation of Now steps the clock forward the specified duration.
// IntervalClock technically implements the other methods of clock.Clock, but each implementation is just a panic. // IntervalClock technically implements the other methods of clock.Clock, but each implementation is just a panic.
// See SimpleIntervalClock for an alternative that only has the methods of PassiveClock. //
// Deprecated: See SimpleIntervalClock for an alternative that only has the methods of PassiveClock.
type IntervalClock struct { type IntervalClock struct {
Time time.Time Time time.Time
Duration time.Duration Duration time.Duration
@ -282,9 +283,9 @@ func (*IntervalClock) Tick(d time.Duration) <-chan time.Time {
// NewTicker has no implementation yet and is omitted. // NewTicker has no implementation yet and is omitted.
// TODO: make interval clock use FakeClock so this can be implemented. // TODO: make interval clock use FakeClock so this can be implemented.
//func (*IntervalClock) NewTicker(d time.Duration) clock.Ticker { func (*IntervalClock) NewTicker(d time.Duration) clock.Ticker {
// panic("IntervalClock doesn't implement NewTicker") panic("IntervalClock doesn't implement NewTicker")
//} }
// Sleep is unimplemented, will panic. // Sleep is unimplemented, will panic.
func (*IntervalClock) Sleep(d time.Duration) { func (*IntervalClock) Sleep(d time.Duration) {

10
vendor/k8s.io/utils/exec/exec.go generated vendored
View File

@ -19,6 +19,7 @@ package exec
import ( import (
"context" "context"
"io" "io"
"io/fs"
osexec "os/exec" osexec "os/exec"
"syscall" "syscall"
"time" "time"
@ -98,17 +99,18 @@ func New() Interface {
// Command is part of the Interface interface. // Command is part of the Interface interface.
func (executor *executor) Command(cmd string, args ...string) Cmd { func (executor *executor) Command(cmd string, args ...string) Cmd {
return (*cmdWrapper)(osexec.Command(cmd, args...)) return (*cmdWrapper)(maskErrDotCmd(osexec.Command(cmd, args...)))
} }
// CommandContext is part of the Interface interface. // CommandContext is part of the Interface interface.
func (executor *executor) CommandContext(ctx context.Context, cmd string, args ...string) Cmd { func (executor *executor) CommandContext(ctx context.Context, cmd string, args ...string) Cmd {
return (*cmdWrapper)(osexec.CommandContext(ctx, cmd, args...)) return (*cmdWrapper)(maskErrDotCmd(osexec.CommandContext(ctx, cmd, args...)))
} }
// LookPath is part of the Interface interface // LookPath is part of the Interface interface
func (executor *executor) LookPath(file string) (string, error) { func (executor *executor) LookPath(file string) (string, error) {
return osexec.LookPath(file) path, err := osexec.LookPath(file)
return path, handleError(maskErrDot(err))
} }
// Wraps exec.Cmd so we can capture errors. // Wraps exec.Cmd so we can capture errors.
@ -198,6 +200,8 @@ func handleError(err error) error {
switch e := err.(type) { switch e := err.(type) {
case *osexec.ExitError: case *osexec.ExitError:
return &ExitErrorWrapper{e} return &ExitErrorWrapper{e}
case *fs.PathError:
return ErrExecutableNotFound
case *osexec.Error: case *osexec.Error:
if e.Err == osexec.ErrNotFound { if e.Err == osexec.ErrNotFound {
return ErrExecutableNotFound return ErrExecutableNotFound

32
vendor/k8s.io/utils/exec/fixup_go118.go generated vendored Normal file
View File

@ -0,0 +1,32 @@
//go:build !go1.19
// +build !go1.19
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec
import (
osexec "os/exec"
)
func maskErrDotCmd(cmd *osexec.Cmd) *osexec.Cmd {
return cmd
}
func maskErrDot(err error) error {
return err
}

40
vendor/k8s.io/utils/exec/fixup_go119.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
//go:build go1.19
// +build go1.19
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec
import (
"errors"
osexec "os/exec"
)
// maskErrDotCmd reverts the behavior of osexec.Cmd to what it was before go1.19
// specifically set the Err field to nil (LookPath returns a new error when the file
// is resolved to the current directory.
func maskErrDotCmd(cmd *osexec.Cmd) *osexec.Cmd {
cmd.Err = maskErrDot(cmd.Err)
return cmd
}
func maskErrDot(err error) error {
if err != nil && errors.Is(err, osexec.ErrDot) {
return nil
}
return err
}

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
/* /*

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
/* /*

View File

@ -19,6 +19,7 @@ package pointer
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"time"
) )
// AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when, // AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when,
@ -51,6 +52,9 @@ func Int(i int) *int {
return &i return &i
} }
// IntPtr is a function variable referring to Int.
//
// Deprecated: Use Int instead.
var IntPtr = Int // for back-compat var IntPtr = Int // for back-compat
// IntDeref dereferences the int ptr and returns it if not nil, or else // IntDeref dereferences the int ptr and returns it if not nil, or else
@ -62,6 +66,9 @@ func IntDeref(ptr *int, def int) int {
return def return def
} }
// IntPtrDerefOr is a function variable referring to IntDeref.
//
// Deprecated: Use IntDeref instead.
var IntPtrDerefOr = IntDeref // for back-compat var IntPtrDerefOr = IntDeref // for back-compat
// Int32 returns a pointer to an int32. // Int32 returns a pointer to an int32.
@ -69,6 +76,9 @@ func Int32(i int32) *int32 {
return &i return &i
} }
// Int32Ptr is a function variable referring to Int32.
//
// Deprecated: Use Int32 instead.
var Int32Ptr = Int32 // for back-compat var Int32Ptr = Int32 // for back-compat
// Int32Deref dereferences the int32 ptr and returns it if not nil, or else // Int32Deref dereferences the int32 ptr and returns it if not nil, or else
@ -80,6 +90,9 @@ func Int32Deref(ptr *int32, def int32) int32 {
return def return def
} }
// Int32PtrDerefOr is a function variable referring to Int32Deref.
//
// Deprecated: Use Int32Deref instead.
var Int32PtrDerefOr = Int32Deref // for back-compat var Int32PtrDerefOr = Int32Deref // for back-compat
// Int32Equal returns true if both arguments are nil or both arguments // Int32Equal returns true if both arguments are nil or both arguments
@ -94,11 +107,74 @@ func Int32Equal(a, b *int32) bool {
return *a == *b return *a == *b
} }
// Uint returns a pointer to an uint
func Uint(i uint) *uint {
return &i
}
// UintPtr is a function variable referring to Uint.
//
// Deprecated: Use Uint instead.
var UintPtr = Uint // for back-compat
// UintDeref dereferences the uint ptr and returns it if not nil, or else
// returns def.
func UintDeref(ptr *uint, def uint) uint {
if ptr != nil {
return *ptr
}
return def
}
// UintPtrDerefOr is a function variable referring to UintDeref.
//
// Deprecated: Use UintDeref instead.
var UintPtrDerefOr = UintDeref // for back-compat
// Uint32 returns a pointer to an uint32.
func Uint32(i uint32) *uint32 {
return &i
}
// Uint32Ptr is a function variable referring to Uint32.
//
// Deprecated: Use Uint32 instead.
var Uint32Ptr = Uint32 // for back-compat
// Uint32Deref dereferences the uint32 ptr and returns it if not nil, or else
// returns def.
func Uint32Deref(ptr *uint32, def uint32) uint32 {
if ptr != nil {
return *ptr
}
return def
}
// Uint32PtrDerefOr is a function variable referring to Uint32Deref.
//
// Deprecated: Use Uint32Deref instead.
var Uint32PtrDerefOr = Uint32Deref // for back-compat
// Uint32Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Uint32Equal(a, b *uint32) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// Int64 returns a pointer to an int64. // Int64 returns a pointer to an int64.
func Int64(i int64) *int64 { func Int64(i int64) *int64 {
return &i return &i
} }
// Int64Ptr is a function variable referring to Int64.
//
// Deprecated: Use Int64 instead.
var Int64Ptr = Int64 // for back-compat var Int64Ptr = Int64 // for back-compat
// Int64Deref dereferences the int64 ptr and returns it if not nil, or else // Int64Deref dereferences the int64 ptr and returns it if not nil, or else
@ -110,6 +186,9 @@ func Int64Deref(ptr *int64, def int64) int64 {
return def return def
} }
// Int64PtrDerefOr is a function variable referring to Int64Deref.
//
// Deprecated: Use Int64Deref instead.
var Int64PtrDerefOr = Int64Deref // for back-compat var Int64PtrDerefOr = Int64Deref // for back-compat
// Int64Equal returns true if both arguments are nil or both arguments // Int64Equal returns true if both arguments are nil or both arguments
@ -124,11 +203,50 @@ func Int64Equal(a, b *int64) bool {
return *a == *b return *a == *b
} }
// Uint64 returns a pointer to an uint64.
func Uint64(i uint64) *uint64 {
return &i
}
// Uint64Ptr is a function variable referring to Uint64.
//
// Deprecated: Use Uint64 instead.
var Uint64Ptr = Uint64 // for back-compat
// Uint64Deref dereferences the uint64 ptr and returns it if not nil, or else
// returns def.
func Uint64Deref(ptr *uint64, def uint64) uint64 {
if ptr != nil {
return *ptr
}
return def
}
// Uint64PtrDerefOr is a function variable referring to Uint64Deref.
//
// Deprecated: Use Uint64Deref instead.
var Uint64PtrDerefOr = Uint64Deref // for back-compat
// Uint64Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Uint64Equal(a, b *uint64) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// Bool returns a pointer to a bool. // Bool returns a pointer to a bool.
func Bool(b bool) *bool { func Bool(b bool) *bool {
return &b return &b
} }
// BoolPtr is a function variable referring to Bool.
//
// Deprecated: Use Bool instead.
var BoolPtr = Bool // for back-compat var BoolPtr = Bool // for back-compat
// BoolDeref dereferences the bool ptr and returns it if not nil, or else // BoolDeref dereferences the bool ptr and returns it if not nil, or else
@ -140,6 +258,9 @@ func BoolDeref(ptr *bool, def bool) bool {
return def return def
} }
// BoolPtrDerefOr is a function variable referring to BoolDeref.
//
// Deprecated: Use BoolDeref instead.
var BoolPtrDerefOr = BoolDeref // for back-compat var BoolPtrDerefOr = BoolDeref // for back-compat
// BoolEqual returns true if both arguments are nil or both arguments // BoolEqual returns true if both arguments are nil or both arguments
@ -159,6 +280,9 @@ func String(s string) *string {
return &s return &s
} }
// StringPtr is a function variable referring to String.
//
// Deprecated: Use String instead.
var StringPtr = String // for back-compat var StringPtr = String // for back-compat
// StringDeref dereferences the string ptr and returns it if not nil, or else // StringDeref dereferences the string ptr and returns it if not nil, or else
@ -170,6 +294,9 @@ func StringDeref(ptr *string, def string) string {
return def return def
} }
// StringPtrDerefOr is a function variable referring to StringDeref.
//
// Deprecated: Use StringDeref instead.
var StringPtrDerefOr = StringDeref // for back-compat var StringPtrDerefOr = StringDeref // for back-compat
// StringEqual returns true if both arguments are nil or both arguments // StringEqual returns true if both arguments are nil or both arguments
@ -184,11 +311,14 @@ func StringEqual(a, b *string) bool {
return *a == *b return *a == *b
} }
// Float32 returns a pointer to the a float32. // Float32 returns a pointer to a float32.
func Float32(i float32) *float32 { func Float32(i float32) *float32 {
return &i return &i
} }
// Float32Ptr is a function variable referring to Float32.
//
// Deprecated: Use Float32 instead.
var Float32Ptr = Float32 var Float32Ptr = Float32
// Float32Deref dereferences the float32 ptr and returns it if not nil, or else // Float32Deref dereferences the float32 ptr and returns it if not nil, or else
@ -200,6 +330,9 @@ func Float32Deref(ptr *float32, def float32) float32 {
return def return def
} }
// Float32PtrDerefOr is a function variable referring to Float32Deref.
//
// Deprecated: Use Float32Deref instead.
var Float32PtrDerefOr = Float32Deref // for back-compat var Float32PtrDerefOr = Float32Deref // for back-compat
// Float32Equal returns true if both arguments are nil or both arguments // Float32Equal returns true if both arguments are nil or both arguments
@ -214,11 +347,14 @@ func Float32Equal(a, b *float32) bool {
return *a == *b return *a == *b
} }
// Float64 returns a pointer to the a float64. // Float64 returns a pointer to a float64.
func Float64(i float64) *float64 { func Float64(i float64) *float64 {
return &i return &i
} }
// Float64Ptr is a function variable referring to Float64.
//
// Deprecated: Use Float64 instead.
var Float64Ptr = Float64 var Float64Ptr = Float64
// Float64Deref dereferences the float64 ptr and returns it if not nil, or else // Float64Deref dereferences the float64 ptr and returns it if not nil, or else
@ -230,6 +366,9 @@ func Float64Deref(ptr *float64, def float64) float64 {
return def return def
} }
// Float64PtrDerefOr is a function variable referring to Float64Deref.
//
// Deprecated: Use Float64Deref instead.
var Float64PtrDerefOr = Float64Deref // for back-compat var Float64PtrDerefOr = Float64Deref // for back-compat
// Float64Equal returns true if both arguments are nil or both arguments // Float64Equal returns true if both arguments are nil or both arguments
@ -243,3 +382,29 @@ func Float64Equal(a, b *float64) bool {
} }
return *a == *b return *a == *b
} }
// Duration returns a pointer to a time.Duration.
func Duration(d time.Duration) *time.Duration {
return &d
}
// DurationDeref dereferences the time.Duration ptr and returns it if not nil, or else
// returns def.
func DurationDeref(ptr *time.Duration, def time.Duration) time.Duration {
if ptr != nil {
return *ptr
}
return def
}
// DurationEqual returns true if both arguments are nil or both arguments
// dereference to the same value.
func DurationEqual(a, b *time.Duration) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}

30
vendor/k8s.io/utils/trace/trace.go generated vendored
View File

@ -21,6 +21,7 @@ import (
"context" "context"
"fmt" "fmt"
"math/rand" "math/rand"
"sync"
"time" "time"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -93,13 +94,16 @@ func (s traceStep) writeItem(b *bytes.Buffer, formatter string, startTime time.T
// Trace keeps track of a set of "steps" and allows us to log a specific // Trace keeps track of a set of "steps" and allows us to log a specific
// step if it took longer than its share of the total allowed time // step if it took longer than its share of the total allowed time
type Trace struct { type Trace struct {
// constant fields
name string name string
fields []Field fields []Field
threshold *time.Duration
startTime time.Time startTime time.Time
endTime *time.Time
traceItems []traceItem
parentTrace *Trace parentTrace *Trace
// fields guarded by a lock
lock sync.RWMutex
threshold *time.Duration
endTime *time.Time
traceItems []traceItem
} }
func (t *Trace) time() time.Time { func (t *Trace) time() time.Time {
@ -138,6 +142,8 @@ func New(name string, fields ...Field) *Trace {
// how long it took. The Fields add key value pairs to provide additional details about the trace // how long it took. The Fields add key value pairs to provide additional details about the trace
// step. // step.
func (t *Trace) Step(msg string, fields ...Field) { func (t *Trace) Step(msg string, fields ...Field) {
t.lock.Lock()
defer t.lock.Unlock()
if t.traceItems == nil { if t.traceItems == nil {
// traces almost always have less than 6 steps, do this to avoid more than a single allocation // traces almost always have less than 6 steps, do this to avoid more than a single allocation
t.traceItems = make([]traceItem, 0, 6) t.traceItems = make([]traceItem, 0, 6)
@ -153,7 +159,9 @@ func (t *Trace) Nest(msg string, fields ...Field) *Trace {
newTrace := New(msg, fields...) newTrace := New(msg, fields...)
if t != nil { if t != nil {
newTrace.parentTrace = t newTrace.parentTrace = t
t.lock.Lock()
t.traceItems = append(t.traceItems, newTrace) t.traceItems = append(t.traceItems, newTrace)
t.lock.Unlock()
} }
return newTrace return newTrace
} }
@ -163,7 +171,9 @@ func (t *Trace) Nest(msg string, fields ...Field) *Trace {
// is logged. // is logged.
func (t *Trace) Log() { func (t *Trace) Log() {
endTime := time.Now() endTime := time.Now()
t.lock.Lock()
t.endTime = &endTime t.endTime = &endTime
t.lock.Unlock()
// an explicit logging request should dump all the steps out at the higher level // an explicit logging request should dump all the steps out at the higher level
if t.parentTrace == nil { // We don't start logging until Log or LogIfLong is called on the root trace if t.parentTrace == nil { // We don't start logging until Log or LogIfLong is called on the root trace
t.logTrace() t.logTrace()
@ -178,13 +188,17 @@ func (t *Trace) Log() {
// If the Trace is nested it is not immediately logged. Instead, it is logged when the trace it // If the Trace is nested it is not immediately logged. Instead, it is logged when the trace it
// is nested within is logged. // is nested within is logged.
func (t *Trace) LogIfLong(threshold time.Duration) { func (t *Trace) LogIfLong(threshold time.Duration) {
t.lock.Lock()
t.threshold = &threshold t.threshold = &threshold
t.lock.Unlock()
t.Log() t.Log()
} }
// logTopLevelTraces finds all traces in a hierarchy of nested traces that should be logged but do not have any // logTopLevelTraces finds all traces in a hierarchy of nested traces that should be logged but do not have any
// parents that will be logged, due to threshold limits, and logs them as top level traces. // parents that will be logged, due to threshold limits, and logs them as top level traces.
func (t *Trace) logTrace() { func (t *Trace) logTrace() {
t.lock.RLock()
defer t.lock.RUnlock()
if t.durationIsWithinThreshold() { if t.durationIsWithinThreshold() {
var buffer bytes.Buffer var buffer bytes.Buffer
traceNum := rand.Int31() traceNum := rand.Int31()
@ -244,9 +258,13 @@ func (t *Trace) calculateStepThreshold() *time.Duration {
traceThreshold := *t.threshold traceThreshold := *t.threshold
for _, s := range t.traceItems { for _, s := range t.traceItems {
nestedTrace, ok := s.(*Trace) nestedTrace, ok := s.(*Trace)
if ok && nestedTrace.threshold != nil { if ok {
traceThreshold = traceThreshold - *nestedTrace.threshold nestedTrace.lock.RLock()
lenTrace-- if nestedTrace.threshold != nil {
traceThreshold = traceThreshold - *nestedTrace.threshold
lenTrace--
}
nestedTrace.lock.RUnlock()
} }
} }

26
vendor/modules.txt vendored
View File

@ -57,7 +57,7 @@ github.com/felixge/httpsnoop
# github.com/fsnotify/fsnotify v1.4.9 # github.com/fsnotify/fsnotify v1.4.9
## explicit; go 1.13 ## explicit; go 1.13
github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify
# github.com/go-logr/logr v1.2.0 # github.com/go-logr/logr v1.2.3
## explicit; go 1.16 ## explicit; go 1.16
github.com/go-logr/logr github.com/go-logr/logr
# github.com/gogo/protobuf v1.3.2 # github.com/gogo/protobuf v1.3.2
@ -124,6 +124,9 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
## explicit; go 1.13 ## explicit; go 1.13
github.com/moby/spdystream github.com/moby/spdystream
github.com/moby/spdystream/spdy github.com/moby/spdystream/spdy
# github.com/moby/sys/mountinfo v0.6.2
## explicit; go 1.16
github.com/moby/sys/mountinfo
# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
## explicit ## explicit
github.com/modern-go/concurrent github.com/modern-go/concurrent
@ -218,7 +221,7 @@ github.com/spf13/cobra
# github.com/spf13/pflag v1.0.5 # github.com/spf13/pflag v1.0.5
## explicit; go 1.12 ## explicit; go 1.12
github.com/spf13/pflag github.com/spf13/pflag
# github.com/stretchr/testify v1.7.0 # github.com/stretchr/testify v1.8.0
## explicit; go 1.13 ## explicit; go 1.13
github.com/stretchr/testify/assert github.com/stretchr/testify/assert
# go.opentelemetry.io/contrib v0.20.0 # go.opentelemetry.io/contrib v0.20.0
@ -459,7 +462,7 @@ gopkg.in/tomb.v1
# gopkg.in/yaml.v2 v2.4.0 # gopkg.in/yaml.v2 v2.4.0
## explicit; go 1.15 ## explicit; go 1.15
gopkg.in/yaml.v2 gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b # gopkg.in/yaml.v3 v3.0.1
## explicit ## explicit
gopkg.in/yaml.v3 gopkg.in/yaml.v3
# k8s.io/api v0.23.3 => k8s.io/api v0.23.3 # k8s.io/api v0.23.3 => k8s.io/api v0.23.3
@ -866,9 +869,14 @@ k8s.io/component-base/version
k8s.io/component-helpers/node/util/sysctl k8s.io/component-helpers/node/util/sysctl
k8s.io/component-helpers/scheduling/corev1 k8s.io/component-helpers/scheduling/corev1
k8s.io/component-helpers/scheduling/corev1/nodeaffinity k8s.io/component-helpers/scheduling/corev1/nodeaffinity
# k8s.io/klog/v2 v2.30.0 # k8s.io/klog/v2 v2.80.1
## explicit; go 1.13 ## explicit; go 1.13
k8s.io/klog/v2 k8s.io/klog/v2
k8s.io/klog/v2/internal/buffer
k8s.io/klog/v2/internal/clock
k8s.io/klog/v2/internal/dbg
k8s.io/klog/v2/internal/serialize
k8s.io/klog/v2/internal/severity
# k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 # k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65
## explicit; go 1.16 ## explicit; go 1.16
k8s.io/kube-openapi/pkg/schemaconv k8s.io/kube-openapi/pkg/schemaconv
@ -943,11 +951,11 @@ k8s.io/kubernetes/test/e2e/storage/podlogs
k8s.io/kubernetes/test/e2e/storage/utils k8s.io/kubernetes/test/e2e/storage/utils
k8s.io/kubernetes/test/utils k8s.io/kubernetes/test/utils
k8s.io/kubernetes/test/utils/image k8s.io/kubernetes/test/utils/image
# k8s.io/mount-utils v0.23.3 => k8s.io/mount-utils v0.23.3 # k8s.io/mount-utils v0.23.3 => k8s.io/mount-utils v0.0.0-20230103133730-1df1a57439e2
## explicit; go 1.16 ## explicit; go 1.19
k8s.io/mount-utils k8s.io/mount-utils
# k8s.io/utils v0.0.0-20211116205334-6203023598ed # k8s.io/utils v0.0.0-20221107191617-1a15be271d1d
## explicit; go 1.12 ## explicit; go 1.18
k8s.io/utils/buffer k8s.io/utils/buffer
k8s.io/utils/clock k8s.io/utils/clock
k8s.io/utils/clock/testing k8s.io/utils/clock/testing
@ -1003,7 +1011,7 @@ sigs.k8s.io/yaml
# k8s.io/kubelet => k8s.io/kubelet v0.23.3 # k8s.io/kubelet => k8s.io/kubelet v0.23.3
# k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.23.3 # k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.23.3
# k8s.io/metrics => k8s.io/metrics v0.23.3 # k8s.io/metrics => k8s.io/metrics v0.23.3
# k8s.io/mount-utils => k8s.io/mount-utils v0.23.3 # k8s.io/mount-utils => k8s.io/mount-utils v0.0.0-20230103133730-1df1a57439e2
# k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.23.3 # k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.23.3
# k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.23.3 # k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.23.3
# k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.23.3 # k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.23.3