Unverified Commit 7f737097 authored by Chris Kim's avatar Chris Kim Committed by GitHub

[release-1.21] Bump Kubernetes to v1.21.7-k3s1 (#4531)

* Update to v1.21.7 Signed-off-by: 's avatarChris Kim <oats87g@gmail.com>
parent 1847a711
...@@ -36,34 +36,34 @@ replace ( ...@@ -36,34 +36,34 @@ replace (
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884
google.golang.org/grpc => google.golang.org/grpc v1.27.1 google.golang.org/grpc => google.golang.org/grpc v1.27.1
gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.21.6-k3s1 k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.21.7-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.21.6-k3s1 k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.21.7-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.21.6-k3s1 k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.21.7-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.21.6-k3s1 k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.21.7-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.21.6-k3s1 k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.21.7-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.21.6-k3s1 k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.21.7-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.21.6-k3s1 k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.21.7-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.21.6-k3s1 k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.21.7-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.21.6-k3s1 k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.21.7-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.21.6-k3s1 k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.21.7-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.21.6-k3s1 k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.21.7-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.21.6-k3s1 k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.21.7-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.21.6-k3s1 k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.21.7-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.21.6-k3s1 k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.21.7-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.21.6-k3s1 k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.21.7-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.21.6-k3s1 k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.21.7-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.21.6-k3s1 k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.21.7-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.21.6-k3s1 k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.21.7-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.21.6-k3s1 k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.21.7-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.21.6-k3s1 k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.21.7-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.21.6-k3s1 k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.21.7-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.21.6-k3s1 k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.21.7-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.21.6-k3s1 k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.21.7-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.21.6-k3s1 k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.21.7-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.21.6-k3s1 k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.21.7-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.21.6-k3s1 k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.21.7-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.21.6-k3s1 k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.21.7-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.21.6-k3s1 k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.21.7-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7 mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7
) )
...@@ -119,18 +119,18 @@ require ( ...@@ -119,18 +119,18 @@ require (
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 golang.org/x/sys v0.0.0-20210426230700-d19ff857e887
google.golang.org/grpc v1.37.0 google.golang.org/grpc v1.37.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.21.6 k8s.io/api v0.21.7
k8s.io/apimachinery v0.21.6 k8s.io/apimachinery v0.21.7
k8s.io/apiserver v0.21.6 k8s.io/apiserver v0.21.7
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
k8s.io/cloud-provider v0.21.6 k8s.io/cloud-provider v0.21.7
k8s.io/component-base v0.21.6 k8s.io/component-base v0.21.7
k8s.io/controller-manager v0.21.6 // indirect k8s.io/controller-manager v0.21.7 // indirect
k8s.io/cri-api v0.21.6 k8s.io/cri-api v0.21.7
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.9.0 k8s.io/klog/v2 v2.9.0
k8s.io/kubectl v0.21.6 k8s.io/kubectl v0.21.7
k8s.io/kubernetes v1.21.6 k8s.io/kubernetes v1.21.7
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 k8s.io/utils v0.0.0-20210521133846-da695404a2bc
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"net" "net"
"net/http" "net/http"
"runtime" "runtime"
"sync"
"time" "time"
"k8s.io/klog/v2" "k8s.io/klog/v2"
...@@ -51,13 +52,17 @@ type respLogger struct { ...@@ -51,13 +52,17 @@ type respLogger struct {
statusRecorded bool statusRecorded bool
status int status int
statusStack string statusStack string
addedInfo string // mutex is used when accessing addedInfo
startTime time.Time // It can be modified by other goroutine when logging happens (in case of request timeout)
mutex sync.Mutex
addedInfo string
startTime time.Time
captureErrorOutput bool captureErrorOutput bool
req *http.Request req *http.Request
w http.ResponseWriter userAgent string
w http.ResponseWriter
logStacktracePred StacktracePred logStacktracePred StacktracePred
} }
...@@ -107,6 +112,7 @@ func newLogged(req *http.Request, w http.ResponseWriter) *respLogger { ...@@ -107,6 +112,7 @@ func newLogged(req *http.Request, w http.ResponseWriter) *respLogger {
return &respLogger{ return &respLogger{
startTime: time.Now(), startTime: time.Now(),
req: req, req: req,
userAgent: req.UserAgent(),
w: w, w: w,
logStacktracePred: DefaultStacktracePred, logStacktracePred: DefaultStacktracePred,
} }
...@@ -152,6 +158,8 @@ func StatusIsNot(statuses ...int) StacktracePred { ...@@ -152,6 +158,8 @@ func StatusIsNot(statuses ...int) StacktracePred {
// Addf adds additional data to be logged with this request. // Addf adds additional data to be logged with this request.
func (rl *respLogger) Addf(format string, data ...interface{}) { func (rl *respLogger) Addf(format string, data ...interface{}) {
rl.mutex.Lock()
defer rl.mutex.Unlock()
rl.addedInfo += "\n" + fmt.Sprintf(format, data...) rl.addedInfo += "\n" + fmt.Sprintf(format, data...)
} }
...@@ -162,7 +170,7 @@ func (rl *respLogger) LogArgs() []interface{} { ...@@ -162,7 +170,7 @@ func (rl *respLogger) LogArgs() []interface{} {
"verb", rl.req.Method, "verb", rl.req.Method,
"URI", rl.req.RequestURI, "URI", rl.req.RequestURI,
"latency", latency, "latency", latency,
"userAgent", rl.req.UserAgent(), "userAgent", rl.userAgent,
"srcIP", rl.req.RemoteAddr, "srcIP", rl.req.RemoteAddr,
"hijacked", true, "hijacked", true,
} }
...@@ -171,10 +179,19 @@ func (rl *respLogger) LogArgs() []interface{} { ...@@ -171,10 +179,19 @@ func (rl *respLogger) LogArgs() []interface{} {
"verb", rl.req.Method, "verb", rl.req.Method,
"URI", rl.req.RequestURI, "URI", rl.req.RequestURI,
"latency", latency, "latency", latency,
"userAgent", rl.req.UserAgent(), // We can't get UserAgent from rl.req.UserAgent() here as it accesses headers map,
// which can be modified in another goroutine when apiserver request times out.
// For example authentication filter modifies request's headers,
// This can cause apiserver to crash with unrecoverable fatal error.
// More info about concurrent read and write for maps: https://golang.org/doc/go1.6#runtime
"userAgent", rl.userAgent,
"srcIP", rl.req.RemoteAddr, "srcIP", rl.req.RemoteAddr,
"resp", rl.status, "resp", rl.status,
} }
rl.mutex.Lock()
defer rl.mutex.Unlock()
if len(rl.statusStack) > 0 { if len(rl.statusStack) > 0 {
args = append(args, "statusStack", rl.statusStack) args = append(args, "statusStack", rl.statusStack)
} }
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "21" gitMinor = "21"
gitVersion = "v1.21.6-k3s1" gitVersion = "v1.21.7-k3s1"
gitCommit = "2500169b04d85bb5c5541a3c99cb1f0adc77da55" gitCommit = "e88164b6f545ced9e4ae580c788b1835738b1f6c"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-10-28T15:54:24Z" buildDate = "2021-11-17T22:40:17Z"
) )
...@@ -55,6 +55,9 @@ const ( ...@@ -55,6 +55,9 @@ const (
// should be changed appropriately. // should be changed appropriately.
minRetryDelay = 5 * time.Second minRetryDelay = 5 * time.Second
maxRetryDelay = 300 * time.Second maxRetryDelay = 300 * time.Second
// ToBeDeletedTaint is a taint used by the CLuster Autoscaler before marking a node for deletion. Defined in
// https://github.com/kubernetes/autoscaler/blob/e80ab518340f88f364fe3ef063f8303755125971/cluster-autoscaler/utils/deletetaint/delete.go#L36
ToBeDeletedTaint = "ToBeDeletedByClusterAutoscaler"
) )
type cachedService struct { type cachedService struct {
...@@ -671,6 +674,14 @@ func (s *Controller) getNodeConditionPredicate() NodeConditionPredicate { ...@@ -671,6 +674,14 @@ func (s *Controller) getNodeConditionPredicate() NodeConditionPredicate {
return false return false
} }
// Remove nodes that are about to be deleted by the cluster autoscaler.
for _, taint := range node.Spec.Taints {
if taint.Key == ToBeDeletedTaint {
klog.V(4).Infof("Ignoring node %v with autoscaler taint %+v", node.Name, taint)
return false
}
}
// If we have no info, don't accept // If we have no info, don't accept
if len(node.Status.Conditions) == 0 { if len(node.Status.Conditions) == 0 {
return false return false
......
...@@ -16,7 +16,7 @@ require ( ...@@ -16,7 +16,7 @@ require (
k8s.io/component-base v0.0.0 k8s.io/component-base v0.0.0
k8s.io/controller-manager v0.0.0 k8s.io/controller-manager v0.0.0
k8s.io/klog/v2 v2.9.0 k8s.io/klog/v2 v2.9.0
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 k8s.io/utils v0.0.0-20210521133846-da695404a2bc
) )
replace ( replace (
......
...@@ -672,10 +672,10 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 ...@@ -672,10 +672,10 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
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.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= k8s.io/kube-openapi v0.0.0-20211110012726-3cc51fd1e909 h1:s77MRc/+/eQjsF89MB12JssAlsoi9mnNoaacRqibeAU=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20211110012726-3cc51fd1e909/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20210521133846-da695404a2bc h1:dx6VGe+PnOW/kD/2UV4aUSsRfJGd7+lcqgJ6Xg0HwUs=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210521133846-da695404a2bc/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
......
...@@ -58,8 +58,8 @@ func (o *Options) Validate() []error { ...@@ -58,8 +58,8 @@ func (o *Options) Validate() []error {
// AddFlags adds flags for exposing component metrics. // AddFlags adds flags for exposing component metrics.
func (o *Options) AddFlags(fs *pflag.FlagSet) { func (o *Options) AddFlags(fs *pflag.FlagSet) {
if o != nil { if o == nil {
o = NewOptions() return
} }
fs.StringVar(&o.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.ShowHiddenMetricsForVersion, fs.StringVar(&o.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.ShowHiddenMetricsForVersion,
"The previous version for which you want to show hidden metrics. "+ "The previous version for which you want to show hidden metrics. "+
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "21" gitMinor = "21"
gitVersion = "v1.21.6-k3s1" gitVersion = "v1.21.7-k3s1"
gitCommit = "2500169b04d85bb5c5541a3c99cb1f0adc77da55" gitCommit = "e88164b6f545ced9e4ae580c788b1835738b1f6c"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-10-28T15:54:24Z" buildDate = "2021-11-17T22:40:17Z"
) )
...@@ -151,7 +151,7 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 ...@@ -151,7 +151,7 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
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.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20211110012726-3cc51fd1e909/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
......
...@@ -35,7 +35,7 @@ import ( ...@@ -35,7 +35,7 @@ import (
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/munnerz/goautoneg" "github.com/munnerz/goautoneg"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
klog "k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/builder" "k8s.io/kube-openapi/pkg/builder"
"k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/common"
) )
...@@ -57,13 +57,40 @@ type OpenAPIService struct { ...@@ -57,13 +57,40 @@ type OpenAPIService struct {
lastModified time.Time lastModified time.Time
specBytes []byte jsonCache cache
specPb []byte protoCache cache
specPbGz []byte }
type cache struct {
BuildCache func() ([]byte, error)
once sync.Once
bytes []byte
etag string
err error
}
func (c *cache) Get() ([]byte, string, error) {
c.once.Do(func() {
bytes, err := c.BuildCache()
// if there is an error updating the cache, there can be situations where
// c.bytes contains a valid value (carried over from the previous update)
// but c.err is also not nil; the cache user is expected to check for this
c.err = err
if c.err == nil {
// don't override previous spec if we had an error
c.bytes = bytes
c.etag = computeETag(c.bytes)
}
})
return c.bytes, c.etag, c.err
}
specBytesETag string func (c *cache) New(cacheBuilder func() ([]byte, error)) cache {
specPbETag string return cache{
specPbGzETag string bytes: c.bytes,
etag: c.etag,
BuildCache: cacheBuilder,
}
} }
func init() { func init() {
...@@ -73,6 +100,9 @@ func init() { ...@@ -73,6 +100,9 @@ func init() {
} }
func computeETag(data []byte) string { func computeETag(data []byte) string {
if data == nil {
return ""
}
return fmt.Sprintf("\"%X\"", sha512.Sum512(data)) return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
} }
...@@ -85,55 +115,44 @@ func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) { ...@@ -85,55 +115,44 @@ func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
return o, nil return o, nil
} }
func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) { func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time, error) {
o.rwMutex.RLock() o.rwMutex.RLock()
defer o.rwMutex.RUnlock() defer o.rwMutex.RUnlock()
return o.specBytes, o.specBytesETag, o.lastModified specBytes, etag, err := o.jsonCache.Get()
} if err != nil {
return nil, "", time.Time{}, err
func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) { }
o.rwMutex.RLock() return specBytes, etag, o.lastModified, nil
defer o.rwMutex.RUnlock()
return o.specPb, o.specPbETag, o.lastModified
} }
func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) { func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time, error) {
o.rwMutex.RLock() o.rwMutex.RLock()
defer o.rwMutex.RUnlock() defer o.rwMutex.RUnlock()
return o.specPbGz, o.specPbGzETag, o.lastModified specPb, etag, err := o.protoCache.Get()
}
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
specBytes, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(openapiSpec)
if err != nil { if err != nil {
return err return nil, "", time.Time{}, err
}
var json map[string]interface{}
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(specBytes, &json); err != nil {
return err
} }
specPb, err := ToProtoBinary(json) return specPb, etag, o.lastModified, nil
if err != nil { }
return err
}
specPbGz := toGzip(specPb)
specBytesETag := computeETag(specBytes)
specPbETag := computeETag(specPb)
specPbGzETag := computeETag(specPbGz)
lastModified := time.Now()
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
o.rwMutex.Lock() o.rwMutex.Lock()
defer o.rwMutex.Unlock() defer o.rwMutex.Unlock()
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
o.specBytes = specBytes return jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(openapiSpec)
o.specPb = specPb })
o.specPbGz = specPbGz o.protoCache = o.protoCache.New(func() ([]byte, error) {
o.specBytesETag = specBytesETag json, _, err := o.jsonCache.Get()
o.specPbETag = specPbETag if err != nil {
o.specPbGzETag = specPbGzETag return nil, err
o.lastModified = lastModified }
var jMap map[string]interface{}
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(json, &jMap); err != nil {
return nil, err
}
return ToProtoBinary(jMap)
})
o.lastModified = time.Now()
return nil return nil
} }
...@@ -212,7 +231,7 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl ...@@ -212,7 +231,7 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
accepted := []struct { accepted := []struct {
Type string Type string
SubType string SubType string
GetDataAndETag func() ([]byte, string, time.Time) GetDataAndETag func() ([]byte, string, time.Time, error)
}{ }{
{"application", "json", o.getSwaggerBytes}, {"application", "json", o.getSwaggerBytes},
{"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes}, {"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes},
...@@ -236,7 +255,15 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl ...@@ -236,7 +255,15 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
} }
// serve the first matching media type in the sorted clause list // serve the first matching media type in the sorted clause list
data, etag, lastModified := accepts.GetDataAndETag() data, etag, lastModified, err := accepts.GetDataAndETag()
if err != nil {
klog.Errorf("Error in OpenAPI handler: %s", err)
// only return a 503 if we have no older cache data to serve
if data == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
}
w.Header().Set("Etag", etag) w.Header().Set("Etag", etag)
// ServeContent will take care of caching using eTag. // ServeContent will take care of caching using eTag.
http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data)) http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data))
......
/*
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 cp
import (
"path"
"path/filepath"
"strings"
)
type fileSpec struct {
PodName string
PodNamespace string
File pathSpec
}
type pathSpec interface {
String() string
}
// localPath represents a client-native path, which will differ based
// on the client OS, its methods will use path/filepath package which
// is OS dependant
type localPath struct {
file string
}
func newLocalPath(fileName string) localPath {
file := stripTrailingSlash(fileName)
return localPath{file: file}
}
func (p localPath) String() string {
return p.file
}
func (p localPath) Dir() localPath {
return newLocalPath(filepath.Dir(p.file))
}
func (p localPath) Base() localPath {
return newLocalPath(filepath.Base(p.file))
}
func (p localPath) Clean() localPath {
return newLocalPath(filepath.Clean(p.file))
}
func (p localPath) Join(elem pathSpec) localPath {
return newLocalPath(filepath.Join(p.file, elem.String()))
}
func (p localPath) Glob() (matches []string, err error) {
return filepath.Glob(p.file)
}
func (p localPath) StripSlashes() localPath {
return newLocalPath(stripLeadingSlash(p.file))
}
func isRelative(base, target localPath) bool {
relative, err := filepath.Rel(base.String(), target.String())
if err != nil {
return false
}
return relative == "." || relative == stripPathShortcuts(relative)
}
// remotePath represents always UNIX path, its methods will use path
// package which is always using `/`
type remotePath struct {
file string
}
func newRemotePath(fileName string) remotePath {
// we assume remote file is a linux container but we need to convert
// windows path separators to unix style for consistent processing
file := strings.ReplaceAll(stripTrailingSlash(fileName), `\`, "/")
return remotePath{file: file}
}
func (p remotePath) String() string {
return p.file
}
func (p remotePath) Dir() remotePath {
return newRemotePath(path.Dir(p.file))
}
func (p remotePath) Base() remotePath {
return newRemotePath(path.Base(p.file))
}
func (p remotePath) Clean() remotePath {
return newRemotePath(path.Clean(p.file))
}
func (p remotePath) Join(elem pathSpec) remotePath {
return newRemotePath(path.Join(p.file, elem.String()))
}
func (p remotePath) StripShortcuts() remotePath {
p = p.Clean()
return newRemotePath(stripPathShortcuts(p.file))
}
func (p remotePath) StripSlashes() remotePath {
return newRemotePath(stripLeadingSlash(p.file))
}
// strips trailing slash (if any) both unix and windows style
func stripTrailingSlash(file string) string {
if len(file) == 0 {
return file
}
if file != "/" && strings.HasSuffix(string(file[len(file)-1]), "/") {
return file[:len(file)-1]
}
return file
}
func stripLeadingSlash(file string) string {
// tar strips the leading '/' and '\' if it's there, so we will too
return strings.TrimLeft(file, `/\`)
}
// stripPathShortcuts removes any leading or trailing "../" from a given path
func stripPathShortcuts(p string) string {
newPath := p
trimmed := strings.TrimPrefix(newPath, "../")
for trimmed != newPath {
newPath = trimmed
trimmed = strings.TrimPrefix(newPath, "../")
}
// trim leftover {".", ".."}
if newPath == "." || newPath == ".." {
newPath = ""
}
if len(newPath) > 0 && string(newPath[0]) == "/" {
return newPath[1:]
}
return newPath
}
...@@ -198,13 +198,13 @@ func (o *CreateSecretDockerRegistryOptions) Complete(f cmdutil.Factory, cmd *cob ...@@ -198,13 +198,13 @@ func (o *CreateSecretDockerRegistryOptions) Complete(f cmdutil.Factory, cmd *cob
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil { if err != nil {
return nil return err
} }
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy) cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter() printer, err := o.PrintFlags.ToPrinter()
if err != nil { if err != nil {
return nil return err
} }
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
......
...@@ -303,12 +303,11 @@ func (c *Controller) syncEndpoints(key string) error { ...@@ -303,12 +303,11 @@ func (c *Controller) syncEndpoints(key string) error {
return err return err
} }
// This means that if a Service transitions away from a nil selector, any // If a selector is specified, clean up any mirrored slices.
// mirrored EndpointSlices will not be cleaned up. #91072 tracks this issue
// for this controller along with the Endpoints and EndpointSlice
// controllers.
if svc.Spec.Selector != nil { if svc.Spec.Selector != nil {
return nil klog.V(4).Infof("%s/%s Service now has selector, cleaning up any mirrored EndpointSlices", namespace, name)
c.endpointSliceTracker.DeleteService(namespace, name)
return c.deleteMirroredSlices(namespace, name)
} }
endpointSlices, err := endpointSlicesMirroredForService(c.endpointSliceLister, namespace, name) endpointSlices, err := endpointSlicesMirroredForService(c.endpointSliceLister, namespace, name)
...@@ -371,7 +370,7 @@ func (c *Controller) onServiceUpdate(prevObj, obj interface{}) { ...@@ -371,7 +370,7 @@ func (c *Controller) onServiceUpdate(prevObj, obj interface{}) {
service := obj.(*v1.Service) service := obj.(*v1.Service)
prevService := prevObj.(*v1.Service) prevService := prevObj.(*v1.Service)
if service == nil || prevService == nil { if service == nil || prevService == nil {
utilruntime.HandleError(fmt.Errorf("onServiceUpdate() expected type v1.Endpoints, got %T, %T", prevObj, obj)) utilruntime.HandleError(fmt.Errorf("onServiceUpdate() expected type v1.Service, got %T, %T", prevObj, obj))
return return
} }
if (service.Spec.Selector == nil) != (prevService.Spec.Selector == nil) { if (service.Spec.Selector == nil) != (prevService.Spec.Selector == nil) {
......
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util"
......
...@@ -151,13 +151,13 @@ func (rc *reconciler) reconcile() { ...@@ -151,13 +151,13 @@ func (rc *reconciler) reconcile() {
// The operation key format is different depending on whether the volume // The operation key format is different depending on whether the volume
// allows multi attach across different nodes. // allows multi attach across different nodes.
if util.IsMultiAttachAllowed(attachedVolume.VolumeSpec) { if util.IsMultiAttachAllowed(attachedVolume.VolumeSpec) {
if rc.attacherDetacher.IsOperationPending(attachedVolume.VolumeName, "" /* podName */, attachedVolume.NodeName) { if !rc.attacherDetacher.IsOperationSafeToRetry(attachedVolume.VolumeName, "" /* podName */, attachedVolume.NodeName, operationexecutor.DetachOperationName) {
klog.V(10).Infof("Operation for volume %q is already running for node %q. Can't start detach", attachedVolume.VolumeName, attachedVolume.NodeName) klog.V(10).Infof("Operation for volume %q is already running or still in exponential backoff for node %q. Can't start detach", attachedVolume.VolumeName, attachedVolume.NodeName)
continue continue
} }
} else { } else {
if rc.attacherDetacher.IsOperationPending(attachedVolume.VolumeName, "" /* podName */, "" /* nodeName */) { if !rc.attacherDetacher.IsOperationSafeToRetry(attachedVolume.VolumeName, "" /* podName */, "" /* nodeName */, operationexecutor.DetachOperationName) {
klog.V(10).Infof("Operation for volume %q is already running in the cluster. Can't start detach for %q", attachedVolume.VolumeName, attachedVolume.NodeName) klog.V(10).Infof("Operation for volume %q is already running or still in exponential backoff in the cluster. Can't start detach for %q", attachedVolume.VolumeName, attachedVolume.NodeName)
continue continue
} }
} }
...@@ -193,6 +193,8 @@ func (rc *reconciler) reconcile() { ...@@ -193,6 +193,8 @@ func (rc *reconciler) reconcile() {
// Before triggering volume detach, mark volume as detached and update the node status // Before triggering volume detach, mark volume as detached and update the node status
// If it fails to update node status, skip detach volume // If it fails to update node status, skip detach volume
// If volume detach operation fails, the volume needs to be added back to report as attached so that node status
// has the correct volume attachment information.
err = rc.actualStateOfWorld.RemoveVolumeFromReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName) err = rc.actualStateOfWorld.RemoveVolumeFromReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName)
if err != nil { if err != nil {
klog.V(5).Infof("RemoveVolumeFromReportAsAttached failed while removing volume %q from node %q with: %v", klog.V(5).Infof("RemoveVolumeFromReportAsAttached failed while removing volume %q from node %q with: %v",
...@@ -222,10 +224,17 @@ func (rc *reconciler) reconcile() { ...@@ -222,10 +224,17 @@ func (rc *reconciler) reconcile() {
klog.Warningf(attachedVolume.GenerateMsgDetailed("attacherDetacher.DetachVolume started", fmt.Sprintf("This volume is not safe to detach, but maxWaitForUnmountDuration %v expired, force detaching", rc.maxWaitForUnmountDuration))) klog.Warningf(attachedVolume.GenerateMsgDetailed("attacherDetacher.DetachVolume started", fmt.Sprintf("This volume is not safe to detach, but maxWaitForUnmountDuration %v expired, force detaching", rc.maxWaitForUnmountDuration)))
} }
} }
if err != nil && !exponentialbackoff.IsExponentialBackoff(err) { if err != nil {
// Ignore exponentialbackoff.IsExponentialBackoff errors, they are expected. // Add volume back to ReportAsAttached if DetachVolume call failed so that node status updater will add it back to VolumeAttached list.
// Log all other errors. // This function is also called during executing the volume detach operation in operation_generoator.
klog.Errorf(attachedVolume.GenerateErrorDetailed("attacherDetacher.DetachVolume failed to start", err).Error()) // It is needed here too because DetachVolume call might fail before executing the actual operation in operation_executor (e.g., cannot find volume plugin etc.)
rc.actualStateOfWorld.AddVolumeToReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName)
if !exponentialbackoff.IsExponentialBackoff(err) {
// Ignore exponentialbackoff.IsExponentialBackoff errors, they are expected.
// Log all other errors.
klog.Errorf(attachedVolume.GenerateErrorDetailed("attacherDetacher.DetachVolume failed to start", err).Error())
}
} }
} }
} }
......
...@@ -350,7 +350,7 @@ func (c *Controller) pvcAddedUpdated(obj interface{}) { ...@@ -350,7 +350,7 @@ func (c *Controller) pvcAddedUpdated(obj interface{}) {
utilruntime.HandleError(fmt.Errorf("couldn't get key for Persistent Volume Claim %#v: %v", pvc, err)) utilruntime.HandleError(fmt.Errorf("couldn't get key for Persistent Volume Claim %#v: %v", pvc, err))
return return
} }
klog.V(4).InfoS("Got event on PVC", key) klog.V(4).InfoS("Got event on PVC", "pvc", klog.KObj(pvc))
if protectionutil.NeedToAddFinalizer(pvc, volumeutil.PVCProtectionFinalizer) || protectionutil.IsDeletionCandidate(pvc, volumeutil.PVCProtectionFinalizer) { if protectionutil.NeedToAddFinalizer(pvc, volumeutil.PVCProtectionFinalizer) || protectionutil.IsDeletionCandidate(pvc, volumeutil.PVCProtectionFinalizer) {
c.queue.Add(key) c.queue.Add(key)
......
...@@ -361,6 +361,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r ...@@ -361,6 +361,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r
} }
return fmt.Errorf("failed to open log file %q: %v", path, err) return fmt.Errorf("failed to open log file %q: %v", path, err)
} }
defer newF.Close()
f.Close() f.Close()
if err := watcher.Remove(f.Name()); err != nil && !os.IsNotExist(err) { if err := watcher.Remove(f.Name()); err != nil && !os.IsNotExist(err) {
klog.ErrorS(err, "Failed to remove file watch", "path", f.Name()) klog.ErrorS(err, "Failed to remove file watch", "path", f.Name())
......
...@@ -18,6 +18,7 @@ package stats ...@@ -18,6 +18,7 @@ package stats
import ( import (
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
cadvisorapiv2 "github.com/google/cadvisor/info/v2" cadvisorapiv2 "github.com/google/cadvisor/info/v2"
...@@ -84,6 +85,10 @@ func (h hostStatsProvider) getPodEtcHostsStats(podUID types.UID, rootFsInfo *cad ...@@ -84,6 +85,10 @@ func (h hostStatsProvider) getPodEtcHostsStats(podUID types.UID, rootFsInfo *cad
if !isEtcHostsSupported { if !isEtcHostsSupported {
return nil, nil return nil, nil
} }
// Some pods have an explicit /etc/hosts mount and the Kubelet will not create an etc-hosts file for them
if _, err := os.Stat(podEtcHostsPath); os.IsNotExist(err) {
return nil, nil
}
metrics := volume.NewMetricsDu(podEtcHostsPath) metrics := volume.NewMetricsDu(podEtcHostsPath)
hostMetrics, err := metrics.GetMetrics() hostMetrics, err := metrics.GetMetrics()
......
...@@ -262,7 +262,8 @@ func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error ...@@ -262,7 +262,8 @@ func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error
c.supportsSELinux, err = c.kubeVolHost.GetHostUtil().GetSELinuxSupport(dir) c.supportsSELinux, err = c.kubeVolHost.GetHostUtil().GetSELinuxSupport(dir)
if err != nil { if err != nil {
klog.V(2).Info(log("error checking for SELinux support: %s", err)) // The volume is mounted. Return UncertainProgressError, so kubelet will unmount it when user deletes the pod.
return volumetypes.NewUncertainProgressError(fmt.Sprintf("error checking for SELinux support: %s", err))
} }
if c.supportsFSGroup(fsType, mounterArgs.FsGroup, c.fsGroupPolicy) { if c.supportsFSGroup(fsType, mounterArgs.FsGroup, c.fsGroupPolicy) {
......
...@@ -27,6 +27,7 @@ import ( ...@@ -27,6 +27,7 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/mount-utils" "k8s.io/mount-utils"
utilexec "k8s.io/utils/exec" utilexec "k8s.io/utils/exec"
"k8s.io/utils/io"
"k8s.io/utils/keymutex" "k8s.io/utils/keymutex"
utilstrings "k8s.io/utils/strings" utilstrings "k8s.io/utils/strings"
...@@ -34,6 +35,7 @@ import ( ...@@ -34,6 +35,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
ioutil "k8s.io/kubernetes/pkg/volume/util" ioutil "k8s.io/kubernetes/pkg/volume/util"
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler" "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
) )
...@@ -210,10 +212,20 @@ func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*v ...@@ -210,10 +212,20 @@ func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*v
// Find globalPDPath from pod volume directory(mountPath) // Find globalPDPath from pod volume directory(mountPath)
var globalPDPath string var globalPDPath string
mounter := plugin.host.GetMounter(plugin.GetPluginName()) mounter := plugin.host.GetMounter(plugin.GetPluginName())
paths, err := mounter.GetMountRefs(mountPath) // Try really hard to get the global mount of the volume, an error returned from here would
// leave the global mount still mounted, while marking the volume as unused.
// The volume can then be mounted on several nodes, resulting in volume
// corruption.
paths, err := util.GetReliableMountRefs(mounter, mountPath)
if io.IsInconsistentReadError(err) {
klog.Errorf("Failed to read mount refs from /proc/mounts for %s: %s", mountPath, err)
klog.Errorf("Kubelet cannot unmount volume at %s, please unmount it and all mounts of the same device manually.", mountPath)
return nil, err
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, path := range paths { for _, path := range paths {
if strings.Contains(path, plugin.host.GetPluginDir(iscsiPluginName)) { if strings.Contains(path, plugin.host.GetPluginDir(iscsiPluginName)) {
globalPDPath = path globalPDPath = path
......
...@@ -108,7 +108,7 @@ func (hu *FakeHostUtil) GetOwner(pathname string) (int64, int64, error) { ...@@ -108,7 +108,7 @@ func (hu *FakeHostUtil) GetOwner(pathname string) (int64, int64, error) {
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux. // GetSELinuxSupport tests if pathname is on a mount that supports SELinux.
// Not implemented for testing // Not implemented for testing
func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) { func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return false, errors.New("GetSELinuxSupport not implemented") return false, nil
} }
// GetMode returns permissions of pathname. // GetMode returns permissions of pathname.
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/util/selinux"
"k8s.io/mount-utils" "k8s.io/mount-utils"
utilpath "k8s.io/utils/path" utilpath "k8s.io/utils/path"
) )
...@@ -229,8 +230,16 @@ func DoMakeRShared(path string, mountInfoFilename string) error { ...@@ -229,8 +230,16 @@ func DoMakeRShared(path string, mountInfoFilename string) error {
return nil return nil
} }
// selinux.SELinuxEnabled implementation for unit tests
type seLinuxEnabledFunc func() bool
// GetSELinux is common implementation of GetSELinuxSupport on Linux. // GetSELinux is common implementation of GetSELinuxSupport on Linux.
func GetSELinux(path string, mountInfoFilename string) (bool, error) { func GetSELinux(path string, mountInfoFilename string, selinuxEnabled seLinuxEnabledFunc) (bool, error) {
// Skip /proc/mounts parsing if SELinux is disabled.
if !selinuxEnabled() {
return false, nil
}
info, err := findMountInfo(path, mountInfoFilename) info, err := findMountInfo(path, mountInfoFilename)
if err != nil { if err != nil {
return false, err return false, err
...@@ -253,7 +262,7 @@ func GetSELinux(path string, mountInfoFilename string) (bool, error) { ...@@ -253,7 +262,7 @@ func GetSELinux(path string, mountInfoFilename string) (bool, error) {
// GetSELinuxSupport returns true if given path is on a mount that supports // GetSELinuxSupport returns true if given path is on a mount that supports
// SELinux. // SELinux.
func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) { func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return GetSELinux(pathname, procMountInfoPath) return GetSELinux(pathname, procMountInfoPath, selinux.SELinuxEnabled)
} }
// GetOwner returns the integer ID for the user and group of the given path // GetOwner returns the integer ID for the user and group of the given path
......
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
k8sRuntime "k8s.io/apimachinery/pkg/util/runtime" k8sRuntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2" "k8s.io/klog/v2"
...@@ -106,6 +106,13 @@ type NestedPendingOperations interface { ...@@ -106,6 +106,13 @@ type NestedPendingOperations interface {
volumeName v1.UniqueVolumeName, volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName, podName volumetypes.UniquePodName,
nodeName types.NodeName) bool nodeName types.NodeName) bool
// IsOperationSafeToRetry returns false if an operation for the given volumeName
// and one of podName or nodeName is pending or in exponential backoff, otherwise it returns true
IsOperationSafeToRetry(
volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName,
nodeName types.NodeName, operationName string) bool
} }
// NewNestedPendingOperations returns a new instance of NestedPendingOperations. // NewNestedPendingOperations returns a new instance of NestedPendingOperations.
...@@ -185,6 +192,33 @@ func (grm *nestedPendingOperations) Run( ...@@ -185,6 +192,33 @@ func (grm *nestedPendingOperations) Run(
return nil return nil
} }
func (grm *nestedPendingOperations) IsOperationSafeToRetry(
volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName,
nodeName types.NodeName,
operationName string) bool {
grm.lock.RLock()
defer grm.lock.RUnlock()
opKey := operationKey{volumeName, podName, nodeName}
exist, previousOpIndex := grm.isOperationExists(opKey)
if !exist {
return true
}
previousOp := grm.operations[previousOpIndex]
if previousOp.operationPending {
return false
}
backOffErr := previousOp.expBackoff.SafeToRetry(fmt.Sprintf("%+v", opKey))
if backOffErr != nil {
if previousOp.operationName == operationName {
return false
}
}
return true
}
func (grm *nestedPendingOperations) IsOperationPending( func (grm *nestedPendingOperations) IsOperationPending(
volumeName v1.UniqueVolumeName, volumeName v1.UniqueVolumeName,
......
...@@ -141,6 +141,9 @@ type OperationExecutor interface { ...@@ -141,6 +141,9 @@ type OperationExecutor interface {
// IsOperationPending returns true if an operation for the given volumeName // IsOperationPending returns true if an operation for the given volumeName
// and one of podName or nodeName is pending, otherwise it returns false // and one of podName or nodeName is pending, otherwise it returns false
IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, nodeName types.NodeName) bool IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, nodeName types.NodeName) bool
// IsOperationSafeToRetry returns false if an operation for the given volumeName
// and one of podName or nodeName is pending or in exponential backoff, otherwise it returns true
IsOperationSafeToRetry(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, nodeName types.NodeName, operationName string) bool
// ExpandInUseVolume will resize volume's file system to expected size without unmounting the volume. // ExpandInUseVolume will resize volume's file system to expected size without unmounting the volume.
ExpandInUseVolume(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error ExpandInUseVolume(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
// ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin // ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin
...@@ -661,6 +664,14 @@ func (oe *operationExecutor) IsOperationPending( ...@@ -661,6 +664,14 @@ func (oe *operationExecutor) IsOperationPending(
return oe.pendingOperations.IsOperationPending(volumeName, podName, nodeName) return oe.pendingOperations.IsOperationPending(volumeName, podName, nodeName)
} }
func (oe *operationExecutor) IsOperationSafeToRetry(
volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName,
nodeName types.NodeName,
operationName string) bool {
return oe.pendingOperations.IsOperationSafeToRetry(volumeName, podName, nodeName, operationName)
}
func (oe *operationExecutor) AttachVolume( func (oe *operationExecutor) AttachVolume(
volumeToAttach VolumeToAttach, volumeToAttach VolumeToAttach,
actualStateOfWorld ActualStateOfWorldAttacherUpdater) error { actualStateOfWorld ActualStateOfWorldAttacherUpdater) error {
......
...@@ -47,6 +47,7 @@ import ( ...@@ -47,6 +47,7 @@ import (
const ( const (
unknownVolumePlugin string = "UnknownVolumePlugin" unknownVolumePlugin string = "UnknownVolumePlugin"
unknownAttachableVolumePlugin string = "UnknownAttachableVolumePlugin" unknownAttachableVolumePlugin string = "UnknownAttachableVolumePlugin"
DetachOperationName string = "volume_detach"
) )
// InTreeToCSITranslator contains methods required to check migratable status // InTreeToCSITranslator contains methods required to check migratable status
...@@ -490,9 +491,9 @@ func (og *operationGenerator) GenerateDetachVolumeFunc( ...@@ -490,9 +491,9 @@ func (og *operationGenerator) GenerateDetachVolumeFunc(
} }
return volumetypes.GeneratedOperations{ return volumetypes.GeneratedOperations{
OperationName: "volume_detach", OperationName: DetachOperationName,
OperationFunc: detachVolumeFunc, OperationFunc: detachVolumeFunc,
CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(pluginName, volumeToDetach.VolumeSpec), "volume_detach"), CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(pluginName, volumeToDetach.VolumeSpec), DetachOperationName),
EventRecorderFunc: nil, // nil because we do not want to generate event on error EventRecorderFunc: nil, // nil because we do not want to generate event on error
}, nil }, nil
} }
......
...@@ -29,7 +29,6 @@ import ( ...@@ -29,7 +29,6 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/volume/util/hostutil"
"k8s.io/mount-utils" "k8s.io/mount-utils"
) )
...@@ -109,12 +108,12 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin ...@@ -109,12 +108,12 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin
notMount = true notMount = true
} }
if !notMount { if !notMount {
linuxHostUtil := hostutil.NewHostUtil() // It's already mounted, so check if it's bind-mounted to the same path
mntInfo, err := linuxHostUtil.FindMountInfo(bindPathTarget) samePath, err := checkSubPathFileEqual(subpath, bindPathTarget)
if err != nil { if err != nil {
return false, "", fmt.Errorf("error calling findMountInfo for %s: %s", bindPathTarget, err) return false, "", fmt.Errorf("error checking subpath mount info for %s: %s", bindPathTarget, err)
} }
if mntInfo.Root != subpath.Path { if !samePath {
// It's already mounted but not what we want, unmount it // It's already mounted but not what we want, unmount it
if err = mounter.Unmount(bindPathTarget); err != nil { if err = mounter.Unmount(bindPathTarget); err != nil {
return false, "", fmt.Errorf("error ummounting %s: %s", bindPathTarget, err) return false, "", fmt.Errorf("error ummounting %s: %s", bindPathTarget, err)
...@@ -155,6 +154,23 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin ...@@ -155,6 +154,23 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin
return false, bindPathTarget, nil return false, bindPathTarget, nil
} }
func checkSubPathFileEqual(subpath Subpath, bindMountTarget string) (bool, error) {
s, err := os.Lstat(subpath.Path)
if err != nil {
return false, fmt.Errorf("stat %s failed: %s", subpath.Path, err)
}
t, err := os.Lstat(bindMountTarget)
if err != nil {
return false, fmt.Errorf("lstat %s failed: %s", bindMountTarget, err)
}
if !os.SameFile(s, t) {
return false, nil
}
return true, nil
}
func getSubpathBindTarget(subpath Subpath) string { func getSubpathBindTarget(subpath Subpath) string {
// containerName is DNS label, i.e. safe as a directory name. // containerName is DNS label, i.e. safe as a directory name.
return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex)) return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex))
......
...@@ -20,18 +20,12 @@ import ( ...@@ -20,18 +20,12 @@ import (
"context" "context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
storagehelpers "k8s.io/component-helpers/storage/volume"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
"time"
"k8s.io/component-helpers/scheduling/corev1"
"k8s.io/klog/v2"
"k8s.io/mount-utils"
utilexec "k8s.io/utils/exec"
utilstrings "k8s.io/utils/strings"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1" storage "k8s.io/api/storage/v1"
...@@ -40,13 +34,21 @@ import ( ...@@ -40,13 +34,21 @@ import (
apiruntime "k8s.io/apimachinery/pkg/runtime" apiruntime "k8s.io/apimachinery/pkg/runtime"
utypes "k8s.io/apimachinery/pkg/types" utypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/component-helpers/scheduling/corev1"
storagehelpers "k8s.io/component-helpers/storage/volume"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
podutil "k8s.io/kubernetes/pkg/api/v1/pod" podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/securitycontext" "k8s.io/kubernetes/pkg/securitycontext"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util/types" "k8s.io/kubernetes/pkg/volume/util/types"
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler" "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
"k8s.io/mount-utils"
utilexec "k8s.io/utils/exec"
"k8s.io/utils/io"
utilstrings "k8s.io/utils/strings"
) )
const ( const (
...@@ -732,3 +734,27 @@ func IsDeviceMountableVolume(volumeSpec *volume.Spec, volumePluginMgr *volume.Vo ...@@ -732,3 +734,27 @@ func IsDeviceMountableVolume(volumeSpec *volume.Spec, volumePluginMgr *volume.Vo
return false return false
} }
// GetReliableMountRefs calls mounter.GetMountRefs and retries on IsInconsistentReadError.
// To be used in volume reconstruction of volume plugins that don't have any protection
// against mounting a single volume on multiple nodes (such as attach/detach).
func GetReliableMountRefs(mounter mount.Interface, mountPath string) ([]string, error) {
var paths []string
var lastErr error
err := wait.PollImmediate(10*time.Millisecond, time.Minute, func() (bool, error) {
var err error
paths, err = mounter.GetMountRefs(mountPath)
if io.IsInconsistentReadError(err) {
lastErr = err
return false, nil
}
if err != nil {
return false, err
}
return true, nil
})
if err == wait.ErrWaitTimeout {
return nil, lastErr
}
return paths, err
}
...@@ -10,7 +10,7 @@ require ( ...@@ -10,7 +10,7 @@ require (
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
k8s.io/klog/v2 v2.9.0 k8s.io/klog/v2 v2.9.0
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 k8s.io/utils v0.0.0-20210521133846-da695404a2bc
) )
replace k8s.io/mount-utils => ../mount-utils replace k8s.io/mount-utils => ../mount-utils
...@@ -27,5 +27,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C ...@@ -27,5 +27,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
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.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20210521133846-da695404a2bc h1:dx6VGe+PnOW/kD/2UV4aUSsRfJGd7+lcqgJ6Xg0HwUs=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210521133846-da695404a2bc/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
...@@ -32,7 +32,7 @@ const ( ...@@ -32,7 +32,7 @@ const (
// At least number of fields per line in /proc/<pid>/mountinfo. // At least number of fields per line in /proc/<pid>/mountinfo.
expectedAtLeastNumFieldsPerMountInfo = 10 expectedAtLeastNumFieldsPerMountInfo = 10
// How many times to retry for a consistent read of /proc/mounts. // How many times to retry for a consistent read of /proc/mounts.
maxListTries = 3 maxListTries = 10
) )
// IsCorruptedMnt return true if err is about corrupted mount point // IsCorruptedMnt return true if err is about corrupted mount point
......
...@@ -84,15 +84,9 @@ func NormalizeWindowsPath(path string) string { ...@@ -84,15 +84,9 @@ func NormalizeWindowsPath(path string) string {
// ValidateDiskNumber : disk number should be a number in [0, 99] // ValidateDiskNumber : disk number should be a number in [0, 99]
func ValidateDiskNumber(disk string) error { func ValidateDiskNumber(disk string) error {
diskNum, err := strconv.Atoi(disk) if _, err := strconv.Atoi(disk); err != nil {
if err != nil { return fmt.Errorf("wrong disk number format: %q, err: %v", disk, err)
return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
} }
if diskNum < 0 || diskNum > 99 {
return fmt.Errorf("disk number out of range: %q", disk)
}
return nil return nil
} }
......
...@@ -30,6 +30,9 @@ var ErrLimitReached = errors.New("the read limit is reached") ...@@ -30,6 +30,9 @@ var ErrLimitReached = errors.New("the read limit is reached")
// ConsistentRead repeatedly reads a file until it gets the same content twice. // ConsistentRead repeatedly reads a file until it gets the same content twice.
// This is useful when reading files in /proc that are larger than page size // This is useful when reading files in /proc that are larger than page size
// and kernel may modify them between individual read() syscalls. // and kernel may modify them between individual read() syscalls.
// It returns InconsistentReadError when it cannot get a consistent read in
// given nr. of attempts. Caller should retry, kernel is probably under heavy
// mount/unmount load.
func ConsistentRead(filename string, attempts int) ([]byte, error) { func ConsistentRead(filename string, attempts int) ([]byte, error) {
return consistentReadSync(filename, attempts, nil) return consistentReadSync(filename, attempts, nil)
} }
...@@ -56,7 +59,28 @@ func consistentReadSync(filename string, attempts int, sync func(int)) ([]byte, ...@@ -56,7 +59,28 @@ func consistentReadSync(filename string, attempts int, sync func(int)) ([]byte,
// Files are different, continue reading // Files are different, continue reading
oldContent = newContent oldContent = newContent
} }
return nil, fmt.Errorf("could not get consistent content of %s after %d attempts", filename, attempts) return nil, InconsistentReadError{filename, attempts}
}
// InconsistentReadError is returned from ConsistentRead when it cannot get
// a consistent read in given nr. of attempts. Caller should retry, kernel is
// probably under heavy mount/unmount load.
type InconsistentReadError struct {
filename string
attempts int
}
func (i InconsistentReadError) Error() string {
return fmt.Sprintf("could not get consistent content of %s after %d attempts", i.filename, i.attempts)
}
var _ error = InconsistentReadError{}
func IsInconsistentReadError(err error) bool {
if _, ok := err.(InconsistentReadError); ok {
return true
}
return false
} }
// ReadAtMost reads up to `limit` bytes from `r`, and reports an error // ReadAtMost reads up to `limit` bytes from `r`, and reports an error
......
# WARNING ! Please read before using mount functionality
# THIS REPOSITORY is moved : Please use https://github.com/kubernetes/mount-utils for all your work
This package has been moved to new location. Please use the new repo for bug fixes and enhancements.
All existing dependencies on this repo are being removed. Eventually this repo will be deprecated.
If you are using this repo or planning to use, you must use the new repo mentioned here for this functionality.
New repo : https://github.com/kubernetes/mount-utils
New go module: k8s.io/mount-utils
For Kubernetes/Kubernetes project the code is available under staging directory.
...@@ -46,86 +46,182 @@ func AllPtrFieldsNil(obj interface{}) bool { ...@@ -46,86 +46,182 @@ func AllPtrFieldsNil(obj interface{}) bool {
return true return true
} }
// Int32Ptr returns a pointer to an int32 // Int32 returns a pointer to an int32.
func Int32Ptr(i int32) *int32 { func Int32(i int32) *int32 {
return &i return &i
} }
// Int32PtrDerefOr dereference the int32 ptr and returns it if not nil, var Int32Ptr = Int32 // for back-compat
// else returns def.
func Int32PtrDerefOr(ptr *int32, def int32) int32 { // Int32Deref dereferences the int32 ptr and returns it if not nil, or else
// returns def.
func Int32Deref(ptr *int32, def int32) int32 {
if ptr != nil { if ptr != nil {
return *ptr return *ptr
} }
return def return def
} }
// Int64Ptr returns a pointer to an int64 var Int32PtrDerefOr = Int32Deref // for back-compat
func Int64Ptr(i int64) *int64 {
// Int32Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Int32Equal(a, b *int32) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// Int64 returns a pointer to an int64.
func Int64(i int64) *int64 {
return &i return &i
} }
// Int64PtrDerefOr dereference the int64 ptr and returns it if not nil, var Int64Ptr = Int64 // for back-compat
// else returns def.
func Int64PtrDerefOr(ptr *int64, def int64) int64 { // Int64Deref dereferences the int64 ptr and returns it if not nil, or else
// returns def.
func Int64Deref(ptr *int64, def int64) int64 {
if ptr != nil { if ptr != nil {
return *ptr return *ptr
} }
return def return def
} }
// BoolPtr returns a pointer to a bool var Int64PtrDerefOr = Int64Deref // for back-compat
func BoolPtr(b bool) *bool {
// Int64Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Int64Equal(a, b *int64) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// Bool returns a pointer to a bool.
func Bool(b bool) *bool {
return &b return &b
} }
// BoolPtrDerefOr dereference the bool ptr and returns it if not nil, var BoolPtr = Bool // for back-compat
// else returns def.
func BoolPtrDerefOr(ptr *bool, def bool) bool { // BoolDeref dereferences the bool ptr and returns it if not nil, or else
// returns def.
func BoolDeref(ptr *bool, def bool) bool {
if ptr != nil { if ptr != nil {
return *ptr return *ptr
} }
return def return def
} }
// StringPtr returns a pointer to the passed string. var BoolPtrDerefOr = BoolDeref // for back-compat
func StringPtr(s string) *string {
// BoolEqual returns true if both arguments are nil or both arguments
// dereference to the same value.
func BoolEqual(a, b *bool) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// String returns a pointer to a string.
func String(s string) *string {
return &s return &s
} }
// StringPtrDerefOr dereference the string ptr and returns it if not nil, var StringPtr = String // for back-compat
// else returns def.
func StringPtrDerefOr(ptr *string, def string) string { // StringDeref dereferences the string ptr and returns it if not nil, or else
// returns def.
func StringDeref(ptr *string, def string) string {
if ptr != nil { if ptr != nil {
return *ptr return *ptr
} }
return def return def
} }
// Float32Ptr returns a pointer to the passed float32. var StringPtrDerefOr = StringDeref // for back-compat
func Float32Ptr(i float32) *float32 {
// StringEqual returns true if both arguments are nil or both arguments
// dereference to the same value.
func StringEqual(a, b *string) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// Float32 returns a pointer to the a float32.
func Float32(i float32) *float32 {
return &i return &i
} }
// Float32PtrDerefOr dereference the float32 ptr and returns it if not nil, var Float32Ptr = Float32
// else returns def.
func Float32PtrDerefOr(ptr *float32, def float32) float32 { // Float32Deref dereferences the float32 ptr and returns it if not nil, or else
// returns def.
func Float32Deref(ptr *float32, def float32) float32 {
if ptr != nil { if ptr != nil {
return *ptr return *ptr
} }
return def return def
} }
// Float64Ptr returns a pointer to the passed float64. var Float32PtrDerefOr = Float32Deref // for back-compat
func Float64Ptr(i float64) *float64 {
// Float32Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Float32Equal(a, b *float32) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
// Float64 returns a pointer to the a float64.
func Float64(i float64) *float64 {
return &i return &i
} }
// Float64PtrDerefOr dereference the float64 ptr and returns it if not nil, var Float64Ptr = Float64
// else returns def.
func Float64PtrDerefOr(ptr *float64, def float64) float64 { // Float64Deref dereferences the float64 ptr and returns it if not nil, or else
// returns def.
func Float64Deref(ptr *float64, def float64) float64 {
if ptr != nil { if ptr != nil {
return *ptr return *ptr
} }
return def return def
} }
var Float64PtrDerefOr = Float64Deref // for back-compat
// Float64Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Float64Equal(a, b *float64) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment