Unverified Commit 355fff30 authored by Jacob Blain Christen's avatar Jacob Blain Christen Committed by GitHub

Update to Kubernetes v1.20.5 (#3094)

* Update to Kubernetes v1.20.5 * vendor: bumps for some containerd deps * go: bump to 1.16.2 for arm Signed-off-by: 's avatarJacob Blain Christen <jacob@rancher.com>
parent a2b1d28c
......@@ -190,6 +190,7 @@ steps:
environment:
GCLOUD_AUTH:
from_secret: gcloud_auth
GOLANG: golang:1.16.2-alpine3.12
commands:
- dapper ci
- echo "${DRONE_TAG}-arm" | sed -e 's/+/-/g' >.tags
......
FROM golang:1.15.8-alpine3.12
ARG GOLANG=golang:1.15.10-alpine3.12
FROM ${GOLANG}
ARG http_proxy=$http_proxy
ARG https_proxy=$https_proxy
......@@ -22,10 +23,8 @@ RUN if [ "$(go env GOARCH)" = "arm64" ]; then
tar -zxvf trivy_0.11.0_Linux-64bit.tar.gz && \
mv trivy /usr/local/bin; \
fi
RUN mkdir -p /go/src/golang.org/x && \
cd /go/src/golang.org/x && git clone https://github.com/golang/tools && cd tools && \
git checkout -b current aa82965741a9fecd12b026fbb3d3c6ed3231b8f8 && \
go install golang.org/x/tools/cmd/goimports && cd
# this works for both go 1.15 and 1.16
RUN GO111MODULE=on go get golang.org/x/tools/cmd/goimports@aa82965741a9fecd12b026fbb3d3c6ed3231b8f8
RUN rm -rf /go/src /go/pkg
RUN if [ "$(go env GOARCH)" = "amd64" ]; then \
......@@ -37,7 +36,7 @@ ENV SELINUX $SELINUX
ENV GO111MODULE off
ENV DAPPER_RUN_ARGS --privileged -v k3s-cache:/go/src/github.com/rancher/k3s/.cache -v trivy-cache:/root/.cache/trivy
ENV DAPPER_ENV REPO TAG DRONE_TAG IMAGE_NAME SKIP_VALIDATE GCLOUD_AUTH GITHUB_TOKEN
ENV DAPPER_ENV REPO TAG DRONE_TAG IMAGE_NAME SKIP_VALIDATE GCLOUD_AUTH GITHUB_TOKEN GOLANG
ENV DAPPER_SOURCE /go/src/github.com/rancher/k3s/
ENV DAPPER_OUTPUT ./bin ./dist ./build/out
ENV DAPPER_DOCKER_SOCKET true
......
FROM golang:1.15.8-alpine3.12
ARG GOLANG=golang:1.15.10-alpine3.12
FROM ${GOLANG}
COPY --from=plugins/manifest:1.2.3 /bin/* /bin/
......
FROM golang:1.15.8-alpine3.12
ARG GOLANG=golang:1.15.10-alpine3.12
FROM ${GOLANG}
RUN apk -U --no-cache add bash git gcc musl-dev docker curl jq coreutils python2 openssl py-pip
......
......@@ -11,9 +11,10 @@ replace (
github.com/containerd/containerd => github.com/k3s-io/containerd v1.4.4-k3s1
github.com/containerd/continuity => github.com/k3s-io/continuity v0.0.0-20210309170710-f93269e0d5c1
github.com/containerd/cri => github.com/k3s-io/cri v1.4.0-k3s.5 // k3s-release/1.4
github.com/containerd/fifo => github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c
github.com/containerd/fifo => github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b
github.com/containerd/go-runc => github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328
github.com/containerd/typeurl => github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
github.com/containerd/ttrpc => github.com/containerd/ttrpc v1.0.1
github.com/containerd/typeurl => github.com/containerd/typeurl v1.0.1
github.com/coreos/flannel => github.com/k3s-io/flannel v0.12.0-k3s2
github.com/coreos/go-systemd => github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
github.com/docker/distribution => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
......@@ -32,34 +33,34 @@ replace (
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
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
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.20.4-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.20.4-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.20.4-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.20.4-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.20.4-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.20.4-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.20.4-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.20.4-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.20.4-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.20.4-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.20.4-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.20.4-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.20.4-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.20.4-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.20.4-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.20.4-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.20.4-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.20.4-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.20.4-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.20.4-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.20.4-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.20.4-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.20.4-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.20.4-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.20.4-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.20.4-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.20.4-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.20.4-k3s1
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.20.5-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.20.5-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.20.5-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.20.5-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.20.5-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.20.5-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.20.5-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.20.5-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.20.5-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.20.5-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.20.5-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.20.5-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.20.5-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.20.5-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.20.5-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.20.5-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.20.5-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.20.5-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.20.5-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.20.5-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.20.5-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.20.5-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.20.5-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.20.5-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.20.5-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.20.5-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.20.5-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.20.5-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34
)
......@@ -68,7 +69,7 @@ require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340
github.com/containerd/containerd v1.4.1
github.com/containerd/containerd v1.4.4
github.com/containerd/cri v1.11.1-0.20200820101445-b0cc07999aa5
github.com/coreos/flannel v0.12.0
github.com/coreos/go-iptables v0.4.5
......@@ -110,16 +111,16 @@ require (
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3
google.golang.org/grpc v1.33.2
gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.19.0
k8s.io/apimachinery v0.19.0
k8s.io/apiserver v0.19.0
k8s.io/api v1.20.5
k8s.io/apimachinery v1.20.5
k8s.io/apiserver v1.20.0
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
k8s.io/cloud-provider v0.0.0
k8s.io/component-base v0.19.0
k8s.io/controller-manager v0.0.0
k8s.io/cri-api v0.19.0
k8s.io/cloud-provider v1.20.0
k8s.io/component-base v1.20.0
k8s.io/controller-manager v1.20.0
k8s.io/cri-api v1.20.0
k8s.io/klog v1.0.0
k8s.io/kubectl v0.0.0
k8s.io/kubernetes v1.20.4
k8s.io/kubectl v1.20.5
k8s.io/kubernetes v1.20.5
sigs.k8s.io/yaml v1.2.0
)
linters:
enable:
- structcheck
- varcheck
- staticcheck
- unconvert
- gofmt
- goimports
- golint
- ineffassign
- vet
- unused
- misspell
disable:
- errcheck
run:
timeout: 3m
skip-dirs:
- vendor
language: go
go:
- 1.11.x
- tip
install:
- go get -u github.com/vbatts/git-validation
- go get -u github.com/kunalkushwaha/ltag
before_script:
- pushd ..; git clone https://github.com/containerd/project; popd
script:
- DCO_VERBOSITY=-q ../project/script/validate/dco
- ../project/script/validate/fileheader ../project/
- make deps
- make fmt
- make vet
- make test
after_success:
- bash <(curl -s https://codecov.io/bash)
......@@ -12,16 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
.PHONY: fmt vet test deps
.PHONY: check test deps
test: deps
go test -v -race -covermode=atomic -coverprofile=coverage.txt ./...
go test -v -race ./...
deps:
go get -d -t ./...
go mod vendor
fmt:
gofmt -s -l .
vet:
go vet ./...
check:
GOGC=75 golangci-lint run
......@@ -21,10 +21,10 @@ import (
)
var (
ErrClosed = errors.New("fifo closed")
ErrCtrlClosed = errors.New("control of closed fifo")
ErrRdFrmWRONLY = errors.New("reading from write-only fifo")
ErrReadClosed = errors.New("reading from a closed fifo")
ErrWrToRDONLY = errors.New("writing to read-only fifo")
ErrWriteClosed = errors.New("writing to a closed fifo")
ErrClosed = errors.New("fifo closed")
ErrCtrlClosed = errors.New("control of closed fifo")
ErrRdFrmWRONLY = errors.New("reading from write-only fifo")
ErrReadClosed = errors.New("reading from a closed fifo")
ErrWrToRDONLY = errors.New("writing to read-only fifo")
ErrWriteClosed = errors.New("writing to a closed fifo")
)
......@@ -17,6 +17,7 @@
package fifo
import (
"context"
"io"
"os"
"runtime"
......@@ -24,7 +25,6 @@ import (
"syscall"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
type fifo struct {
......
module github.com/containerd/fifo
go 1.13
require (
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.4.0
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449
)
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
......@@ -27,6 +27,7 @@ import (
"github.com/pkg/errors"
)
//nolint:golint
const O_PATH = 010000000
type handle struct {
......@@ -56,9 +57,10 @@ func getHandle(fn string) (*handle, error) {
h := &handle{
f: f,
name: fn,
dev: uint64(stat.Dev),
ino: stat.Ino,
fd: fd,
//nolint:unconvert
dev: uint64(stat.Dev),
ino: stat.Ino,
fd: fd,
}
// check /proc just in case
......@@ -83,6 +85,7 @@ func (h *handle) Path() (string, error) {
if err := syscall.Stat(h.procPath(), &stat); err != nil {
return "", errors.Wrapf(err, "path %v could not be statted", h.procPath())
}
//nolint:unconvert
if uint64(stat.Dev) != h.dev || stat.Ino != h.ino {
return "", errors.Errorf("failed to verify handle %v/%v %v/%v", stat.Dev, h.dev, stat.Ino, h.ino)
}
......
......@@ -23,7 +23,7 @@ import (
)
// SyscallConn provides raw access to the fifo's underlying filedescrptor.
// See syscall.Conn for guarentees provided by this interface.
// See syscall.Conn for guarantees provided by this interface.
func (f *fifo) SyscallConn() (syscall.RawConn, error) {
// deterministic check for closed
select {
......
......@@ -3,7 +3,6 @@ language: go
go:
- "1.13.x"
- "1.15.x"
install:
# Don't change local go.{mod, sum} by go get tools.
......
......@@ -47,9 +47,8 @@ type Client struct {
ctx context.Context
closed func()
closeOnce sync.Once
userCloseFunc func()
userCloseWaitCh chan struct{}
closeOnce sync.Once
userCloseFunc func()
errOnce sync.Once
err error
......@@ -76,15 +75,14 @@ func WithUnaryClientInterceptor(i UnaryClientInterceptor) ClientOpts {
func NewClient(conn net.Conn, opts ...ClientOpts) *Client {
ctx, cancel := context.WithCancel(context.Background())
c := &Client{
codec: codec{},
conn: conn,
channel: newChannel(conn),
calls: make(chan *callRequest),
closed: cancel,
ctx: ctx,
userCloseFunc: func() {},
userCloseWaitCh: make(chan struct{}),
interceptor: defaultClientInterceptor,
codec: codec{},
conn: conn,
channel: newChannel(conn),
calls: make(chan *callRequest),
closed: cancel,
ctx: ctx,
userCloseFunc: func() {},
interceptor: defaultClientInterceptor,
}
for _, o := range opts {
......@@ -177,17 +175,6 @@ func (c *Client) Close() error {
return nil
}
// UserOnCloseWait is used to blocks untils the user's on-close callback
// finishes.
func (c *Client) UserOnCloseWait(ctx context.Context) error {
select {
case <-c.userCloseWaitCh:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
type message struct {
messageHeader
p []byte
......@@ -264,7 +251,6 @@ func (c *Client) run() {
defer func() {
c.conn.Close()
c.userCloseFunc()
close(c.userCloseWaitCh)
}()
for {
......@@ -353,8 +339,7 @@ func filterCloseErr(err error) error {
return ErrClosed
default:
// if we have an epipe on a write or econnreset on a read , we cast to errclosed
var oerr *net.OpError
if errors.As(err, &oerr) && (oerr.Op == "write" || oerr.Op == "read") {
if oerr, ok := err.(*net.OpError); ok && (oerr.Op == "write" || oerr.Op == "read") {
serr, sok := oerr.Err.(*os.SyscallError)
if sok && ((serr.Err == syscall.EPIPE && oerr.Op == "write") ||
(serr.Err == syscall.ECONNRESET && oerr.Op == "read")) {
......
language: go
go:
- 1.9.x
- 1.10.x
- tip
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
......@@ -175,24 +176,13 @@
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]
Copyright The containerd 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
https://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,
......
......@@ -6,4 +6,14 @@
A Go package for managing the registration, marshaling, and unmarshaling of encoded types.
This package helps when types are sent over a GRPC API and marshaled as a [protobuf.Any]().
This package helps when types are sent over a GRPC API and marshaled as a [protobuf.Any](https://github.com/gogo/protobuf/blob/master/protobuf/google/protobuf/any.proto).
## Project details
**typeurl** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.
/*
Copyright The containerd 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 typeurl
// Package typeurl assists with managing the registration, marshaling, and
// unmarshaling of types encoded as protobuf.Any.
//
// A protobuf.Any is a proto message that can contain any arbitrary data. It
// consists of two components, a TypeUrl and a Value, and its proto definition
// looks like this:
//
// message Any {
// string type_url = 1;
// bytes value = 2;
// }
//
// The TypeUrl is used to distinguish the contents from other proto.Any
// messages. This typeurl library manages these URLs to enable automagic
// marshaling and unmarshaling of the contents.
//
// For example, consider this go struct:
//
// type Foo struct {
// Field1 string
// Field2 string
// }
//
// To use typeurl, types must first be registered. This is typically done in
// the init function
//
// func init() {
// typeurl.Register(&Foo{}, "Foo")
// }
//
// This will register the type Foo with the url path "Foo". The arguments to
// Register are variadic, and are used to construct a url path. Consider this
// example, from the github.com/containerd/containerd/client package:
//
// func init() {
// const prefix = "types.containerd.io"
// // register TypeUrls for commonly marshaled external types
// major := strconv.Itoa(specs.VersionMajor)
// typeurl.Register(&specs.Spec{}, prefix, "opencontainers/runtime-spec", major, "Spec")
// // this function has more Register calls, which are elided.
// }
//
// This registers several types under a more complex url, which ends up mapping
// to `types.containerd.io/opencontainers/runtime-spec/1/Spec` (or some other
// value for major).
//
// Once a type is registered, it can be marshaled to a proto.Any message simply
// by calling `MarshalAny`, like this:
//
// foo := &Foo{Field1: "value1", Field2: "value2"}
// anyFoo, err := typeurl.MarshalAny(foo)
//
// MarshalAny will resolve the correct URL for the type. If the type in
// question implements the proto.Message interface, then it will be marshaled
// as a proto message. Otherwise, it will be marshaled as json. This means that
// typeurl will work on any arbitrary data, whether or not it has a proto
// definition, as long as it can be serialized to json.
//
// To unmarshal, the process is simply inverse:
//
// iface, err := typeurl.UnmarshalAny(anyFoo)
// foo := iface.(*Foo)
//
// The correct type is automatically chosen from the type registry, and the
// returned interface can be cast straight to that type.
module github.com/containerd/typeurl
go 1.13
require (
github.com/gogo/protobuf v1.3.1
github.com/pkg/errors v0.9.1
)
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
......@@ -34,7 +34,10 @@ var (
var ErrNotFound = errors.New("not found")
// Register a type with the base url of the type
// Register a type with a base URL for JSON marshaling. When the MarshalAny and
// UnmarshalAny functions are called they will treat the Any type value as JSON.
// To use protocol buffers for handling the Any value the proto.Register
// function should be used instead of this function.
func Register(v interface{}, args ...string) {
var (
t = tryDereference(v)
......@@ -44,14 +47,14 @@ func Register(v interface{}, args ...string) {
defer mu.Unlock()
if et, ok := registry[t]; ok {
if et != p {
panic(errors.Errorf("type registred with alternate path %q != %q", et, p))
panic(errors.Errorf("type registered with alternate path %q != %q", et, p))
}
return
}
registry[t] = p
}
// TypeURL returns the type url for a registred type
// TypeURL returns the type url for a registered type.
func TypeURL(v interface{}) (string, error) {
mu.Lock()
u, ok := registry[tryDereference(v)]
......@@ -67,7 +70,7 @@ func TypeURL(v interface{}) (string, error) {
return u, nil
}
// Is returns true if the type of the Any is the same as v
// Is returns true if the type of the Any is the same as v.
func Is(any *types.Any, v interface{}) bool {
// call to check that v is a pointer
tryDereference(v)
......@@ -78,7 +81,10 @@ func Is(any *types.Any, v interface{}) bool {
return any.TypeUrl == url
}
// MarshalAny marshals the value v into an any with the correct TypeUrl
// MarshalAny marshals the value v into an any with the correct TypeUrl.
// If the provided object is already a proto.Any message, then it will be
// returned verbatim. If it is of type proto.Message, it will be marshaled as a
// protocol buffer. Otherwise, the object will be marshaled to json.
func MarshalAny(v interface{}) (*types.Any, error) {
var marshal func(v interface{}) ([]byte, error)
switch t := v.(type) {
......@@ -108,18 +114,49 @@ func MarshalAny(v interface{}) (*types.Any, error) {
}, nil
}
// UnmarshalAny unmarshals the any type into a concrete type
// UnmarshalAny unmarshals the any type into a concrete type.
func UnmarshalAny(any *types.Any) (interface{}, error) {
t, err := getTypeByUrl(any.TypeUrl)
return UnmarshalByTypeURL(any.TypeUrl, any.Value)
}
func UnmarshalByTypeURL(typeURL string, value []byte) (interface{}, error) {
return unmarshal(typeURL, value, nil)
}
func UnmarshalTo(any *types.Any, out interface{}) error {
return UnmarshalToByTypeURL(any.TypeUrl, any.Value, out)
}
func UnmarshalToByTypeURL(typeURL string, value []byte, out interface{}) error {
_, err := unmarshal(typeURL, value, out)
return err
}
func unmarshal(typeURL string, value []byte, v interface{}) (interface{}, error) {
t, err := getTypeByUrl(typeURL)
if err != nil {
return nil, err
}
v := reflect.New(t.t).Interface()
if v == nil {
v = reflect.New(t.t).Interface()
} else {
// Validate interface type provided by client
vURL, err := TypeURL(v)
if err != nil {
return nil, err
}
if typeURL != vURL {
return nil, errors.Errorf("can't unmarshal type %q to output %q", typeURL, vURL)
}
}
if t.isProto {
err = proto.Unmarshal(any.Value, v.(proto.Message))
err = proto.Unmarshal(value, v.(proto.Message))
} else {
err = json.Unmarshal(any.Value, v)
err = json.Unmarshal(value, v)
}
return v, err
}
......
......@@ -29,14 +29,13 @@ import (
"strings"
"time"
"github.com/google/cadvisor/container"
info "github.com/google/cadvisor/info/v1"
"golang.org/x/sys/unix"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/cgroups"
fs2 "github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"k8s.io/klog/v2"
"github.com/google/cadvisor/container"
info "github.com/google/cadvisor/info/v1"
)
var (
......@@ -758,16 +757,6 @@ func (h *Handler) GetProcesses() ([]int, error) {
return pids, nil
}
func minUint32(x, y uint32) uint32 {
if x < y {
return x
}
return y
}
// var to allow unit tests to stub it out
var numCpusFunc = getNumberOnlineCPUs
// Convert libcontainer stats to info.ContainerStats.
func setCPUStats(s *cgroups.Stats, ret *info.ContainerStats, withPerCPU bool) {
ret.Cpu.Usage.User = s.CpuStats.CpuUsage.UsageInUsermode
......@@ -785,37 +774,7 @@ func setCPUStats(s *cgroups.Stats, ret *info.ContainerStats, withPerCPU bool) {
// cpuacct subsystem.
return
}
numPossible := uint32(len(s.CpuStats.CpuUsage.PercpuUsage))
// Note that as of https://patchwork.kernel.org/patch/8607101/ (kernel v4.7),
// the percpu usage information includes extra zero values for all additional
// possible CPUs. This is to allow statistic collection after CPU-hotplug.
// We intentionally ignore these extra zeroes.
numActual, err := numCpusFunc()
if err != nil {
klog.Errorf("unable to determine number of actual cpus; defaulting to maximum possible number: errno %v", err)
numActual = numPossible
}
if numActual > numPossible {
// The real number of cores should never be greater than the number of
// datapoints reported in cpu usage.
klog.Errorf("PercpuUsage had %v cpus, but the actual number is %v; ignoring extra CPUs", numPossible, numActual)
}
numActual = minUint32(numPossible, numActual)
ret.Cpu.Usage.PerCpu = make([]uint64, numActual)
for i := uint32(0); i < numActual; i++ {
ret.Cpu.Usage.PerCpu[i] = s.CpuStats.CpuUsage.PercpuUsage[i]
}
}
func getNumberOnlineCPUs() (uint32, error) {
var availableCPUs unix.CPUSet
if err := unix.SchedGetaffinity(0, &availableCPUs); err != nil {
return 0, err
}
return uint32(availableCPUs.Count()), nil
ret.Cpu.Usage.PerCpu = s.CpuStats.CpuUsage.PercpuUsage
}
func setDiskIoStats(s *cgroups.Stats, ret *info.ContainerStats) {
......
......@@ -383,7 +383,7 @@ func getCoresInfo(sysFs sysfs.SysFs, cpuDirs []string) ([]info.Core, error) {
for _, cpuDir := range cpuDirs {
cpuID, err := getMatchedInt(cpuDirRegExp, cpuDir)
if err != nil {
return nil, fmt.Errorf("Unexpected format of CPU directory, cpuDirRegExp %s, cpuDir: %s", cpuDirRegExp, cpuDir)
return nil, fmt.Errorf("unexpected format of CPU directory, cpuDirRegExp %s, cpuDir: %s", cpuDirRegExp, cpuDir)
}
if !sysFs.IsCPUOnline(cpuDir) {
continue
......@@ -401,9 +401,22 @@ func getCoresInfo(sysFs sysfs.SysFs, cpuDirs []string) ([]info.Core, error) {
return nil, err
}
rawPhysicalPackageID, err := sysFs.GetCPUPhysicalPackageID(cpuDir)
if os.IsNotExist(err) {
klog.Warningf("Cannot read physical package id for %s, physical_package_id file does not exist, err: %s", cpuDir, err)
continue
} else if err != nil {
return nil, err
}
physicalPackageID, err := strconv.Atoi(rawPhysicalPackageID)
if err != nil {
return nil, err
}
coreIDx := -1
for id, core := range cores {
if core.Id == physicalID {
if core.Id == physicalID && core.SocketID == physicalPackageID {
coreIDx = id
}
}
......@@ -414,25 +427,14 @@ func getCoresInfo(sysFs sysfs.SysFs, cpuDirs []string) ([]info.Core, error) {
desiredCore := &cores[coreIDx]
desiredCore.Id = physicalID
desiredCore.SocketID = physicalPackageID
if len(desiredCore.Threads) == 0 {
desiredCore.Threads = []int{cpuID}
} else {
desiredCore.Threads = append(desiredCore.Threads, cpuID)
}
rawPhysicalPackageID, err := sysFs.GetCPUPhysicalPackageID(cpuDir)
if os.IsNotExist(err) {
klog.Warningf("Cannot read physical package id for %s, physical_package_id file does not exist, err: %s", cpuDir, err)
continue
} else if err != nil {
return nil, err
}
physicalPackageID, err := strconv.Atoi(rawPhysicalPackageID)
if err != nil {
return nil, err
}
desiredCore.SocketID = physicalPackageID
}
return cores, nil
}
......
......@@ -28,6 +28,7 @@ import (
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
"k8s.io/klog/v2"
)
// RecommendedOptions contains the recommended options for running an API server.
......@@ -124,12 +125,16 @@ func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig) error {
return err
}
if feature.DefaultFeatureGate.Enabled(features.APIPriorityAndFairness) {
config.FlowControl = utilflowcontrol.New(
config.SharedInformerFactory,
kubernetes.NewForConfigOrDie(config.ClientConfig).FlowcontrolV1beta1(),
config.MaxRequestsInFlight+config.MaxMutatingRequestsInFlight,
config.RequestTimeout/4,
)
if config.ClientConfig != nil {
config.FlowControl = utilflowcontrol.New(
config.SharedInformerFactory,
kubernetes.NewForConfigOrDie(config.ClientConfig).FlowcontrolV1beta1(),
config.MaxRequestsInFlight+config.MaxMutatingRequestsInFlight,
config.RequestTimeout/4,
)
} else {
klog.Warningf("Neither kubeconfig is provided nor service-account is mounted, so APIPriorityAndFairness will be disabled")
}
}
return nil
}
......
......@@ -3,8 +3,8 @@ package version
var (
gitMajor = "1"
gitMinor = "20"
gitVersion = "v1.20.4-k3s1"
gitCommit = "dbe940a8b30b544ac26d7947bb7dc7f62a5f484d"
gitVersion = "v1.20.5-k3s1"
gitCommit = "2a725098eaafe27dde4eadb4318c5b1561b9ab06"
gitTreeState = "clean"
buildDate = "2021-02-18T20:50:53Z"
buildDate = "2021-03-18T20:21:52Z"
)
......@@ -3,8 +3,8 @@ package version
var (
gitMajor = "1"
gitMinor = "20"
gitVersion = "v1.20.4-k3s1"
gitCommit = "dbe940a8b30b544ac26d7947bb7dc7f62a5f484d"
gitVersion = "v1.20.5-k3s1"
gitCommit = "2a725098eaafe27dde4eadb4318c5b1561b9ab06"
gitTreeState = "clean"
buildDate = "2021-02-18T20:50:53Z"
buildDate = "2021-03-18T20:21:52Z"
)
......@@ -47,7 +47,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
......@@ -55,7 +54,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
......@@ -135,7 +133,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
......@@ -146,7 +143,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
......
......@@ -92,11 +92,23 @@ func VisitContainers(podSpec *api.PodSpec, mask ContainerType, visitor Container
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func(name string) (shouldContinue bool)
func skipEmptyNames(visitor Visitor) Visitor {
return func(name string) bool {
if len(name) == 0 {
// continue visiting
return true
}
// delegate to visitor
return visitor(name)
}
}
// VisitPodSecretNames invokes the visitor function with the name of every secret
// referenced by the pod spec. If visitor returns false, visiting is short-circuited.
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited.
func VisitPodSecretNames(pod *api.Pod, visitor Visitor, containerType ContainerType) bool {
visitor = skipEmptyNames(visitor)
for _, reference := range pod.Spec.ImagePullSecrets {
if !visitor(reference.Name) {
return false
......@@ -185,6 +197,7 @@ func visitContainerSecretNames(container *api.Container, visitor Visitor) bool {
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited.
func VisitPodConfigmapNames(pod *api.Pod, visitor Visitor, containerType ContainerType) bool {
visitor = skipEmptyNames(visitor)
VisitContainers(&pod.Spec, containerType, func(c *api.Container, containerType ContainerType) bool {
return visitContainerConfigmapNames(c, visitor)
})
......
......@@ -30,10 +30,22 @@ func getClaimRefNamespace(pv *corev1.PersistentVolume) string {
// Visitor is called with each object's namespace and name, and returns true if visiting should continue
type Visitor func(namespace, name string, kubeletVisible bool) (shouldContinue bool)
func skipEmptyNames(visitor Visitor) Visitor {
return func(namespace, name string, kubeletVisible bool) bool {
if len(name) == 0 {
// continue visiting
return true
}
// delegate to visitor
return visitor(namespace, name, kubeletVisible)
}
}
// VisitPVSecretNames invokes the visitor function with the name of every secret
// referenced by the PV spec. If visitor returns false, visiting is short-circuited.
// Returns true if visiting completed, false if visiting was short-circuited.
func VisitPVSecretNames(pv *corev1.PersistentVolume, visitor Visitor) bool {
visitor = skipEmptyNames(visitor)
source := &pv.Spec.PersistentVolumeSource
switch {
case source.AzureFile != nil:
......
......@@ -82,6 +82,17 @@ type ContainerVisitor func(container *v1.Container, containerType ContainerType)
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func(name string) (shouldContinue bool)
func skipEmptyNames(visitor Visitor) Visitor {
return func(name string) bool {
if len(name) == 0 {
// continue visiting
return true
}
// delegate to visitor
return visitor(name)
}
}
// VisitContainers invokes the visitor function with a pointer to every container
// spec in the given pod spec with type set in mask. If visitor returns false,
// visiting is short-circuited. VisitContainers returns true if visiting completes,
......@@ -116,6 +127,7 @@ func VisitContainers(podSpec *v1.PodSpec, mask ContainerType, visitor ContainerV
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited.
func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool {
visitor = skipEmptyNames(visitor)
for _, reference := range pod.Spec.ImagePullSecrets {
if !visitor(reference.Name) {
return false
......@@ -205,6 +217,7 @@ func visitContainerSecretNames(container *v1.Container, visitor Visitor) bool {
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited.
func VisitPodConfigmapNames(pod *v1.Pod, visitor Visitor) bool {
visitor = skipEmptyNames(visitor)
VisitContainers(&pod.Spec, AllContainers, func(c *v1.Container, containerType ContainerType) bool {
return visitContainerConfigmapNames(c, visitor)
})
......
......@@ -6,6 +6,7 @@ go_library(
"endpointset.go",
"endpointslice_controller.go",
"endpointslice_tracker.go",
"errors.go",
"reconciler.go",
"utils.go",
],
......
......@@ -346,6 +346,10 @@ func (c *Controller) syncService(key string) error {
return err
}
if c.endpointSliceTracker.StaleSlices(service, endpointSlices) {
return &StaleInformerCache{"EndpointSlice informer cache is out of date"}
}
// We call ComputeEndpointLastChangeTriggerTime here to make sure that the
// state of the trigger time tracker gets updated even if the sync turns out
// to be no-op and we don't update the EndpointSlice objects.
......@@ -395,7 +399,7 @@ func (c *Controller) onEndpointSliceAdd(obj interface{}) {
utilruntime.HandleError(fmt.Errorf("Invalid EndpointSlice provided to onEndpointSliceAdd()"))
return
}
if managedByController(endpointSlice) && c.endpointSliceTracker.Stale(endpointSlice) {
if managedByController(endpointSlice) && c.endpointSliceTracker.ShouldSync(endpointSlice) {
c.queueServiceForEndpointSlice(endpointSlice)
}
}
......@@ -411,7 +415,18 @@ func (c *Controller) onEndpointSliceUpdate(prevObj, obj interface{}) {
utilruntime.HandleError(fmt.Errorf("Invalid EndpointSlice provided to onEndpointSliceUpdate()"))
return
}
if managedByChanged(prevEndpointSlice, endpointSlice) || (managedByController(endpointSlice) && c.endpointSliceTracker.Stale(endpointSlice)) {
// EndpointSlice generation does not change when labels change. Although the
// controller will never change LabelServiceName, users might. This check
// ensures that we handle changes to this label.
svcName := endpointSlice.Labels[discovery.LabelServiceName]
prevSvcName := prevEndpointSlice.Labels[discovery.LabelServiceName]
if svcName != prevSvcName {
klog.Warningf("%s label changed from %s to %s for %s", discovery.LabelServiceName, prevSvcName, svcName, endpointSlice.Name)
c.queueServiceForEndpointSlice(endpointSlice)
c.queueServiceForEndpointSlice(prevEndpointSlice)
return
}
if managedByChanged(prevEndpointSlice, endpointSlice) || (managedByController(endpointSlice) && c.endpointSliceTracker.ShouldSync(endpointSlice)) {
c.queueServiceForEndpointSlice(endpointSlice)
}
}
......@@ -422,7 +437,11 @@ func (c *Controller) onEndpointSliceUpdate(prevObj, obj interface{}) {
func (c *Controller) onEndpointSliceDelete(obj interface{}) {
endpointSlice := getEndpointSliceFromDeleteAction(obj)
if endpointSlice != nil && managedByController(endpointSlice) && c.endpointSliceTracker.Has(endpointSlice) {
c.queueServiceForEndpointSlice(endpointSlice)
// This returns false if we didn't expect the EndpointSlice to be
// deleted. If that is the case, we queue the Service for another sync.
if !c.endpointSliceTracker.HandleDeletion(endpointSlice) {
c.queueServiceForEndpointSlice(endpointSlice)
}
}
}
......
......@@ -19,102 +19,154 @@ package endpointslice
import (
"sync"
"k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1beta1"
"k8s.io/apimachinery/pkg/types"
)
// endpointSliceResourceVersions tracks expected EndpointSlice resource versions
// by EndpointSlice name.
type endpointSliceResourceVersions map[string]string
const (
deletionExpected = -1
)
// generationsBySlice tracks expected EndpointSlice generations by EndpointSlice
// uid. A value of deletionExpected (-1) may be used here to indicate that we
// expect this EndpointSlice to be deleted.
type generationsBySlice map[types.UID]int64
// endpointSliceTracker tracks EndpointSlices and their associated resource
// versions to help determine if a change to an EndpointSlice has been processed
// by the EndpointSlice controller.
// endpointSliceTracker tracks EndpointSlices and their associated generation to
// help determine if a change to an EndpointSlice has been processed by the
// EndpointSlice controller.
type endpointSliceTracker struct {
// lock protects resourceVersionsByService.
// lock protects generationsByService.
lock sync.Mutex
// resourceVersionsByService tracks the list of EndpointSlices and
// associated resource versions expected for a given Service.
resourceVersionsByService map[types.NamespacedName]endpointSliceResourceVersions
// generationsByService tracks the generations of EndpointSlices for each
// Service.
generationsByService map[types.NamespacedName]generationsBySlice
}
// newEndpointSliceTracker creates and initializes a new endpointSliceTracker.
func newEndpointSliceTracker() *endpointSliceTracker {
return &endpointSliceTracker{
resourceVersionsByService: map[types.NamespacedName]endpointSliceResourceVersions{},
generationsByService: map[types.NamespacedName]generationsBySlice{},
}
}
// Has returns true if the endpointSliceTracker has a resource version for the
// Has returns true if the endpointSliceTracker has a generation for the
// provided EndpointSlice.
func (est *endpointSliceTracker) Has(endpointSlice *discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
return false
}
_, ok = rrv[endpointSlice.Name]
_, ok = gfs[endpointSlice.UID]
return ok
}
// Stale returns true if this endpointSliceTracker does not have a resource
// version for the provided EndpointSlice or it does not match the resource
// version of the provided EndpointSlice.
func (est *endpointSliceTracker) Stale(endpointSlice *discovery.EndpointSlice) bool {
// ShouldSync returns true if this endpointSliceTracker does not have a
// generation for the provided EndpointSlice or it is greater than the
// generation of the tracked EndpointSlice.
func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
return true
}
return rrv[endpointSlice.Name] != endpointSlice.ResourceVersion
g, ok := gfs[endpointSlice.UID]
return !ok || endpointSlice.Generation > g
}
// StaleSlices returns true if one or more of the provided EndpointSlices
// have older generations than the corresponding tracked ones or if the tracker
// is expecting one or more of the provided EndpointSlices to be deleted.
func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
nn := types.NamespacedName{Name: service.Name, Namespace: service.Namespace}
gfs, ok := est.generationsByService[nn]
if !ok {
return false
}
for _, endpointSlice := range endpointSlices {
g, ok := gfs[endpointSlice.UID]
if ok && (g == deletionExpected || g > endpointSlice.Generation) {
return true
}
}
return false
}
// Update adds or updates the resource version in this endpointSliceTracker for
// the provided EndpointSlice.
// Update adds or updates the generation in this endpointSliceTracker for the
// provided EndpointSlice.
func (est *endpointSliceTracker) Update(endpointSlice *discovery.EndpointSlice) {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
rrv = endpointSliceResourceVersions{}
est.resourceVersionsByService[getServiceNN(endpointSlice)] = rrv
gfs = generationsBySlice{}
est.generationsByService[getServiceNN(endpointSlice)] = gfs
}
rrv[endpointSlice.Name] = endpointSlice.ResourceVersion
gfs[endpointSlice.UID] = endpointSlice.Generation
}
// DeleteService removes the set of resource versions tracked for the Service.
// DeleteService removes the set of generations tracked for the Service.
func (est *endpointSliceTracker) DeleteService(namespace, name string) {
est.lock.Lock()
defer est.lock.Unlock()
serviceNN := types.NamespacedName{Name: name, Namespace: namespace}
delete(est.resourceVersionsByService, serviceNN)
delete(est.generationsByService, serviceNN)
}
// Delete removes the resource version in this endpointSliceTracker for the
// provided EndpointSlice.
func (est *endpointSliceTracker) Delete(endpointSlice *discovery.EndpointSlice) {
// ExpectDeletion sets the generation to deletionExpected in this
// endpointSliceTracker for the provided EndpointSlice.
func (est *endpointSliceTracker) ExpectDeletion(endpointSlice *discovery.EndpointSlice) {
est.lock.Lock()
defer est.lock.Unlock()
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
gfs = generationsBySlice{}
est.generationsByService[getServiceNN(endpointSlice)] = gfs
}
gfs[endpointSlice.UID] = deletionExpected
}
// HandleDeletion removes the generation in this endpointSliceTracker for the
// provided EndpointSlice. This returns true if the tracker expected this
// EndpointSlice to be deleted and false if not.
func (est *endpointSliceTracker) HandleDeletion(endpointSlice *discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if ok {
delete(rrv, endpointSlice.Name)
g, ok := gfs[endpointSlice.UID]
delete(gfs, endpointSlice.UID)
if ok && g != deletionExpected {
return false
}
}
return true
}
// relatedResourceVersions returns the set of resource versions tracked for the
// Service corresponding to the provided EndpointSlice, and a bool to indicate
// if it exists.
func (est *endpointSliceTracker) relatedResourceVersions(endpointSlice *discovery.EndpointSlice) (endpointSliceResourceVersions, bool) {
// generationsForSliceUnsafe returns the generations for the Service
// corresponding to the provided EndpointSlice, and a bool to indicate if it
// exists. A lock must be applied before calling this function.
func (est *endpointSliceTracker) generationsForSliceUnsafe(endpointSlice *discovery.EndpointSlice) (generationsBySlice, bool) {
serviceNN := getServiceNN(endpointSlice)
vers, ok := est.resourceVersionsByService[serviceNN]
return vers, ok
generations, ok := est.generationsByService[serviceNN]
return generations, ok
}
// getServiceNN returns a namespaced name for the Service corresponding to the
......
/*
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 endpointslice
// StaleInformerCache errors indicate that the informer cache includes out of
// date resources.
type StaleInformerCache struct {
msg string
}
func (e *StaleInformerCache) Error() string { return e.msg }
func isStaleInformerCacheErr(err error) bool {
_, ok := err.(*StaleInformerCache)
return ok
}
......@@ -101,7 +101,7 @@ func (r *reconciler) reconcile(service *corev1.Service, pods []*corev1.Pod, exis
if err != nil {
errs = append(errs, fmt.Errorf("Error deleting %s EndpointSlice for Service %s/%s: %v", sliceToDelete.Name, service.Namespace, service.Name, err))
} else {
r.endpointSliceTracker.Delete(sliceToDelete)
r.endpointSliceTracker.ExpectDeletion(sliceToDelete)
metrics.EndpointSliceChanges.WithLabelValues("delete").Inc()
}
}
......@@ -293,7 +293,7 @@ func (r *reconciler) finalize(
if err != nil {
return fmt.Errorf("failed to delete %s EndpointSlice for Service %s/%s: %v", endpointSlice.Name, service.Namespace, service.Name, err)
}
r.endpointSliceTracker.Delete(endpointSlice)
r.endpointSliceTracker.ExpectDeletion(endpointSlice)
metrics.EndpointSliceChanges.WithLabelValues("delete").Inc()
}
......
......@@ -6,6 +6,7 @@ go_library(
"endpointset.go",
"endpointslice_tracker.go",
"endpointslicemirroring_controller.go",
"errors.go",
"events.go",
"reconciler.go",
"reconciler_helpers.go",
......
......@@ -19,102 +19,154 @@ package endpointslicemirroring
import (
"sync"
"k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1beta1"
"k8s.io/apimachinery/pkg/types"
)
// endpointSliceResourceVersions tracks expected EndpointSlice resource versions
// by EndpointSlice name.
type endpointSliceResourceVersions map[string]string
const (
deletionExpected = -1
)
// generationsBySlice tracks expected EndpointSlice generations by EndpointSlice
// uid. A value of deletionExpected (-1) may be used here to indicate that we
// expect this EndpointSlice to be deleted.
type generationsBySlice map[types.UID]int64
// endpointSliceTracker tracks EndpointSlices and their associated resource
// versions to help determine if a change to an EndpointSlice has been processed
// by the EndpointSlice controller.
// endpointSliceTracker tracks EndpointSlices and their associated generation to
// help determine if a change to an EndpointSlice has been processed by the
// EndpointSlice controller.
type endpointSliceTracker struct {
// lock protects resourceVersionsByService.
// lock protects generationsByService.
lock sync.Mutex
// resourceVersionsByService tracks the list of EndpointSlices and
// associated resource versions expected for a given Service.
resourceVersionsByService map[types.NamespacedName]endpointSliceResourceVersions
// generationsByService tracks the generations of EndpointSlices for each
// Service.
generationsByService map[types.NamespacedName]generationsBySlice
}
// newEndpointSliceTracker creates and initializes a new endpointSliceTracker.
func newEndpointSliceTracker() *endpointSliceTracker {
return &endpointSliceTracker{
resourceVersionsByService: map[types.NamespacedName]endpointSliceResourceVersions{},
generationsByService: map[types.NamespacedName]generationsBySlice{},
}
}
// Has returns true if the endpointSliceTracker has a resource version for the
// Has returns true if the endpointSliceTracker has a generation for the
// provided EndpointSlice.
func (est *endpointSliceTracker) Has(endpointSlice *discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
return false
}
_, ok = rrv[endpointSlice.Name]
_, ok = gfs[endpointSlice.UID]
return ok
}
// Stale returns true if this endpointSliceTracker does not have a resource
// version for the provided EndpointSlice or it does not match the resource
// version of the provided EndpointSlice.
func (est *endpointSliceTracker) Stale(endpointSlice *discovery.EndpointSlice) bool {
// ShouldSync returns true if this endpointSliceTracker does not have a
// generation for the provided EndpointSlice or it is greater than the
// generation of the tracked EndpointSlice.
func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
return true
}
return rrv[endpointSlice.Name] != endpointSlice.ResourceVersion
g, ok := gfs[endpointSlice.UID]
return !ok || endpointSlice.Generation > g
}
// StaleSlices returns true if one or more of the provided EndpointSlices
// have older generations than the corresponding tracked ones or if the tracker
// is expecting one or more of the provided EndpointSlices to be deleted.
func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
nn := types.NamespacedName{Name: service.Name, Namespace: service.Namespace}
gfs, ok := est.generationsByService[nn]
if !ok {
return false
}
for _, endpointSlice := range endpointSlices {
g, ok := gfs[endpointSlice.UID]
if ok && (g == deletionExpected || g > endpointSlice.Generation) {
return true
}
}
return false
}
// Update adds or updates the resource version in this endpointSliceTracker for
// the provided EndpointSlice.
// Update adds or updates the generation in this endpointSliceTracker for the
// provided EndpointSlice.
func (est *endpointSliceTracker) Update(endpointSlice *discovery.EndpointSlice) {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
rrv = endpointSliceResourceVersions{}
est.resourceVersionsByService[getServiceNN(endpointSlice)] = rrv
gfs = generationsBySlice{}
est.generationsByService[getServiceNN(endpointSlice)] = gfs
}
rrv[endpointSlice.Name] = endpointSlice.ResourceVersion
gfs[endpointSlice.UID] = endpointSlice.Generation
}
// DeleteService removes the set of resource versions tracked for the Service.
// DeleteService removes the set of generations tracked for the Service.
func (est *endpointSliceTracker) DeleteService(namespace, name string) {
est.lock.Lock()
defer est.lock.Unlock()
serviceNN := types.NamespacedName{Name: name, Namespace: namespace}
delete(est.resourceVersionsByService, serviceNN)
delete(est.generationsByService, serviceNN)
}
// Delete removes the resource version in this endpointSliceTracker for the
// provided EndpointSlice.
func (est *endpointSliceTracker) Delete(endpointSlice *discovery.EndpointSlice) {
// ExpectDeletion sets the generation to deletionExpected in this
// endpointSliceTracker for the provided EndpointSlice.
func (est *endpointSliceTracker) ExpectDeletion(endpointSlice *discovery.EndpointSlice) {
est.lock.Lock()
defer est.lock.Unlock()
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if !ok {
gfs = generationsBySlice{}
est.generationsByService[getServiceNN(endpointSlice)] = gfs
}
gfs[endpointSlice.UID] = deletionExpected
}
// HandleDeletion removes the generation in this endpointSliceTracker for the
// provided EndpointSlice. This returns true if the tracker expected this
// EndpointSlice to be deleted and false if not.
func (est *endpointSliceTracker) HandleDeletion(endpointSlice *discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
rrv, ok := est.relatedResourceVersions(endpointSlice)
gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
if ok {
delete(rrv, endpointSlice.Name)
g, ok := gfs[endpointSlice.UID]
delete(gfs, endpointSlice.UID)
if ok && g != deletionExpected {
return false
}
}
return true
}
// relatedResourceVersions returns the set of resource versions tracked for the
// Service corresponding to the provided EndpointSlice, and a bool to indicate
// if it exists.
func (est *endpointSliceTracker) relatedResourceVersions(endpointSlice *discovery.EndpointSlice) (endpointSliceResourceVersions, bool) {
// generationsForSliceUnsafe returns the generations for the Service
// corresponding to the provided EndpointSlice, and a bool to indicate if it
// exists. A lock must be applied before calling this function.
func (est *endpointSliceTracker) generationsForSliceUnsafe(endpointSlice *discovery.EndpointSlice) (generationsBySlice, bool) {
serviceNN := getServiceNN(endpointSlice)
vers, ok := est.resourceVersionsByService[serviceNN]
return vers, ok
generations, ok := est.generationsByService[serviceNN]
return generations, ok
}
// getServiceNN returns a namespaced name for the Service corresponding to the
......
......@@ -316,6 +316,10 @@ func (c *Controller) syncEndpoints(key string) error {
return err
}
if c.endpointSliceTracker.StaleSlices(svc, endpointSlices) {
return &StaleInformerCache{"EndpointSlice informer cache is out of date"}
}
err = c.reconciler.reconcile(endpoints, endpointSlices)
if err != nil {
return err
......@@ -439,7 +443,7 @@ func (c *Controller) onEndpointSliceAdd(obj interface{}) {
utilruntime.HandleError(fmt.Errorf("onEndpointSliceAdd() expected type discovery.EndpointSlice, got %T", obj))
return
}
if managedByController(endpointSlice) && c.endpointSliceTracker.Stale(endpointSlice) {
if managedByController(endpointSlice) && c.endpointSliceTracker.ShouldSync(endpointSlice) {
c.queueEndpointsForEndpointSlice(endpointSlice)
}
}
......@@ -455,7 +459,18 @@ func (c *Controller) onEndpointSliceUpdate(prevObj, obj interface{}) {
utilruntime.HandleError(fmt.Errorf("onEndpointSliceUpdated() expected type discovery.EndpointSlice, got %T, %T", prevObj, obj))
return
}
if managedByChanged(prevEndpointSlice, endpointSlice) || (managedByController(endpointSlice) && c.endpointSliceTracker.Stale(endpointSlice)) {
// EndpointSlice generation does not change when labels change. Although the
// controller will never change LabelServiceName, users might. This check
// ensures that we handle changes to this label.
svcName := endpointSlice.Labels[discovery.LabelServiceName]
prevSvcName := prevEndpointSlice.Labels[discovery.LabelServiceName]
if svcName != prevSvcName {
klog.Warningf("%s label changed from %s to %s for %s", discovery.LabelServiceName, prevSvcName, svcName, endpointSlice.Name)
c.queueEndpointsForEndpointSlice(endpointSlice)
c.queueEndpointsForEndpointSlice(prevEndpointSlice)
return
}
if managedByChanged(prevEndpointSlice, endpointSlice) || (managedByController(endpointSlice) && c.endpointSliceTracker.ShouldSync(endpointSlice)) {
c.queueEndpointsForEndpointSlice(endpointSlice)
}
}
......@@ -470,7 +485,11 @@ func (c *Controller) onEndpointSliceDelete(obj interface{}) {
return
}
if managedByController(endpointSlice) && c.endpointSliceTracker.Has(endpointSlice) {
c.queueEndpointsForEndpointSlice(endpointSlice)
// This returns false if we didn't expect the EndpointSlice to be
// deleted. If that is the case, we queue the Service for another sync.
if !c.endpointSliceTracker.HandleDeletion(endpointSlice) {
c.queueEndpointsForEndpointSlice(endpointSlice)
}
}
}
......
/*
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 endpointslicemirroring
// StaleInformerCache errors indicate that the informer cache includes out of
// date resources.
type StaleInformerCache struct {
msg string
}
func (e *StaleInformerCache) Error() string { return e.msg }
......@@ -263,7 +263,7 @@ func (r *reconciler) finalize(endpoints *corev1.Endpoints, slices slicesByAction
if err != nil {
return fmt.Errorf("failed to delete %s EndpointSlice for Endpoints %s/%s: %v", endpointSlice.Name, endpoints.Namespace, endpoints.Name, err)
}
r.endpointSliceTracker.Delete(endpointSlice)
r.endpointSliceTracker.ExpectDeletion(endpointSlice)
metrics.EndpointSliceChanges.WithLabelValues("delete").Inc()
}
......
......@@ -1477,13 +1477,13 @@ func (nc *Controller) markNodeForTainting(node *v1.Node, status v1.ConditionStat
defer nc.evictorLock.Unlock()
if status == v1.ConditionFalse {
if !taintutils.TaintExists(node.Spec.Taints, NotReadyTaintTemplate) {
nc.zoneNoExecuteTainter[utilnode.GetZoneKey(node)].SetRemove(node.Name)
nc.zoneNoExecuteTainter[utilnode.GetZoneKey(node)].Remove(node.Name)
}
}
if status == v1.ConditionUnknown {
if !taintutils.TaintExists(node.Spec.Taints, UnreachableTaintTemplate) {
nc.zoneNoExecuteTainter[utilnode.GetZoneKey(node)].SetRemove(node.Name)
nc.zoneNoExecuteTainter[utilnode.GetZoneKey(node)].Remove(node.Name)
}
}
......
......@@ -194,15 +194,6 @@ func (q *UniqueQueue) Clear() {
}
}
// SetRemove remove value from the set if value existed
func (q *UniqueQueue) SetRemove(value string) {
q.lock.Lock()
defer q.lock.Unlock()
if q.set.Has(value) {
q.set.Delete(value)
}
}
// RateLimitedTimedQueue is a unique item priority queue ordered by
// the expected next time of execution. It is also rate limited.
type RateLimitedTimedQueue struct {
......@@ -289,11 +280,6 @@ func (q *RateLimitedTimedQueue) Clear() {
q.queue.Clear()
}
// SetRemove remove value from the set of the queue
func (q *RateLimitedTimedQueue) SetRemove(value string) {
q.queue.SetRemove(value)
}
// SwapLimiter safely swaps current limiter for this queue with the
// passed one if capacities or qps's differ.
func (q *RateLimitedTimedQueue) SwapLimiter(newQPS float32) {
......
......@@ -245,7 +245,7 @@ func Discover(machineInfo *cadvisorapi.MachineInfo) (*CPUTopology, error) {
return &CPUTopology{
NumCPUs: machineInfo.NumCores,
NumSockets: len(machineInfo.Topology),
NumSockets: machineInfo.NumSockets,
NumCores: numPhysicalCores,
CPUDetails: CPUDetails,
}, nil
......
......@@ -817,7 +817,11 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
v1.NamespaceNodeLease,
util.SetNodeOwnerFunc(klet.heartbeatClient, string(klet.nodeName)))
klet.shutdownManager = nodeshutdown.NewManager(klet.GetActivePods, killPodNow(klet.podWorkers, kubeDeps.Recorder), kubeCfg.ShutdownGracePeriod.Duration, kubeCfg.ShutdownGracePeriodCriticalPods.Duration)
// setup node shutdown manager
shutdownManager, shutdownAdmitHandler := nodeshutdown.NewManager(klet.GetActivePods, killPodNow(klet.podWorkers, kubeDeps.Recorder), klet.syncNodeStatus, kubeCfg.ShutdownGracePeriod.Duration, kubeCfg.ShutdownGracePeriodCriticalPods.Duration)
klet.shutdownManager = shutdownManager
klet.admitHandlers.AddPodAdmitHandler(shutdownAdmitHandler)
// Finally, put the most recent version of the config on the Kubelet, so
// people can see how it was configured.
......
......@@ -368,17 +368,46 @@ func (kl *Kubelet) getMountedVolumePathListFromDisk(podUID types.UID) ([]string,
return mountedVolumes, nil
}
// podVolumesSubpathsDirExists returns true if the pod volume-subpaths directory for
// a given pod exists
func (kl *Kubelet) podVolumeSubpathsDirExists(podUID types.UID) (bool, error) {
podVolDir := kl.getPodVolumeSubpathsDir(podUID)
// getPodVolumeSubpathListFromDisk returns a list of the volume-subpath paths by reading the
// subpath directories for the given pod from the disk.
func (kl *Kubelet) getPodVolumeSubpathListFromDisk(podUID types.UID) ([]string, error) {
volumes := []string{}
podSubpathsDir := kl.getPodVolumeSubpathsDir(podUID)
if pathExists, pathErr := mount.PathExists(podVolDir); pathErr != nil {
return true, fmt.Errorf("error checking if path %q exists: %v", podVolDir, pathErr)
if pathExists, pathErr := mount.PathExists(podSubpathsDir); pathErr != nil {
return nil, fmt.Errorf("error checking if path %q exists: %v", podSubpathsDir, pathErr)
} else if !pathExists {
return false, nil
return volumes, nil
}
return true, nil
// Explicitly walks /<volume>/<container name>/<subPathIndex>
volumePluginDirs, err := ioutil.ReadDir(podSubpathsDir)
if err != nil {
klog.Errorf("Could not read directory %s: %v", podSubpathsDir, err)
return volumes, err
}
for _, volumePluginDir := range volumePluginDirs {
volumePluginName := volumePluginDir.Name()
volumePluginPath := filepath.Join(podSubpathsDir, volumePluginName)
containerDirs, err := ioutil.ReadDir(volumePluginPath)
if err != nil {
return volumes, fmt.Errorf("could not read directory %s: %v", volumePluginPath, err)
}
for _, containerDir := range containerDirs {
containerName := containerDir.Name()
containerPath := filepath.Join(volumePluginPath, containerName)
// Switch to ReadDirNoStat at the subPathIndex level to prevent issues with stat'ing
// mount points that may not be responsive
subPaths, err := utilpath.ReadDirNoStat(containerPath)
if err != nil {
return volumes, fmt.Errorf("could not read directory %s: %v", containerPath, err)
}
for _, subPathDir := range subPaths {
volumes = append(volumes, filepath.Join(containerPath, subPathDir))
}
}
}
return volumes, nil
}
// GetRequestedContainersInfo returns container info.
......
......@@ -18,6 +18,7 @@ package kubelet
import (
"fmt"
"syscall"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
......@@ -120,25 +121,49 @@ func (kl *Kubelet) cleanupOrphanedPodDirs(pods []*v1.Pod, runningPods []*kubecon
klog.V(3).Infof("Orphaned pod %q found, but volumes are not cleaned up", uid)
continue
}
// If there are still volume directories, do not delete directory
allVolumesCleanedUp := true
// If there are still volume directories, attempt to rmdir them
volumePaths, err := kl.getPodVolumePathListFromDisk(uid)
if err != nil {
orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("orphaned pod %q found, but error %v occurred during reading volume dir from disk", uid, err))
continue
}
if len(volumePaths) > 0 {
orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("orphaned pod %q found, but volume paths are still present on disk", uid))
continue
for _, volumePath := range volumePaths {
if err := syscall.Rmdir(volumePath); err != nil {
orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("orphaned pod %q found, but failed to rmdir() volume at path %v: %v", uid, volumePath, err))
allVolumesCleanedUp = false
} else {
klog.Warningf("Cleaned up orphaned volume from pod %q at %s", uid, volumePath)
}
}
}
// If there are any volume-subpaths, do not cleanup directories
volumeSubpathExists, err := kl.podVolumeSubpathsDirExists(uid)
// If there are any volume-subpaths, attempt to rmdir them
subpathVolumePaths, err := kl.getPodVolumeSubpathListFromDisk(uid)
if err != nil {
orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("orphaned pod %q found, but error %v occurred during reading of volume-subpaths dir from disk", uid, err))
continue
}
if volumeSubpathExists {
orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("orphaned pod %q found, but volume subpaths are still present on disk", uid))
if len(subpathVolumePaths) > 0 {
for _, subpathVolumePath := range subpathVolumePaths {
if err := syscall.Rmdir(subpathVolumePath); err != nil {
orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("orphaned pod %q found, but failed to rmdir() subpath at path %v: %v", uid, subpathVolumePath, err))
allVolumesCleanedUp = false
} else {
klog.Warningf("Cleaned up orphaned volume subpath from pod %q at %s", uid, subpathVolumePath)
}
}
}
if !allVolumesCleanedUp {
// Not all volumes were removed, so don't clean up the pod directory yet. It is likely
// that there are still mountpoints left which could stall RemoveAllOneFilesystem which
// would otherwise be called below.
// Errors for all removal operations have already been recorded, so don't add another
// one here.
continue
}
......
......@@ -11,10 +11,12 @@ go_library(
deps = select({
"@io_bazel_rules_go//go/platform:aix": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:android": [
"//pkg/features:go_default_library",
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
"//pkg/kubelet/nodeshutdown/systemd:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/kubelet/util/format:go_default_library",
......@@ -26,25 +28,32 @@ go_library(
],
"@io_bazel_rules_go//go/platform:darwin": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:illumos": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:ios": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:js": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux": [
"//pkg/features:go_default_library",
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
"//pkg/kubelet/nodeshutdown/systemd:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/kubelet/util/format:go_default_library",
......@@ -56,21 +65,27 @@ go_library(
],
"@io_bazel_rules_go//go/platform:nacl": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:plan9": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:solaris": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"@io_bazel_rules_go//go/platform:windows": [
"//pkg/kubelet/eviction:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library",
],
"//conditions:default": [],
}),
......
......@@ -31,14 +31,16 @@ import (
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/eviction"
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
"k8s.io/kubernetes/pkg/kubelet/nodeshutdown/systemd"
kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/kubelet/util/format"
)
const (
nodeShutdownReason = "Shutdown"
nodeShutdownMessage = "Node is shutting, evicting pods"
nodeShutdownReason = "Shutdown"
nodeShutdownMessage = "Node is shutting, evicting pods"
nodeShutdownNotAdmitMessage = "Node is in progress of shutting down, not admitting any new pods"
)
var systemDbus = func() (dbusInhibiter, error) {
......@@ -63,8 +65,9 @@ type Manager struct {
shutdownGracePeriodRequested time.Duration
shutdownGracePeriodCriticalPods time.Duration
getPods eviction.ActivePodsFunc
killPod eviction.KillPodFunc
getPods eviction.ActivePodsFunc
killPod eviction.KillPodFunc
syncNodeStatus func()
dbusCon dbusInhibiter
inhibitLock systemd.InhibitLock
......@@ -76,14 +79,30 @@ type Manager struct {
}
// NewManager returns a new node shutdown manager.
func NewManager(getPodsFunc eviction.ActivePodsFunc, killPodFunc eviction.KillPodFunc, shutdownGracePeriodRequested, shutdownGracePeriodCriticalPods time.Duration) *Manager {
return &Manager{
func NewManager(getPodsFunc eviction.ActivePodsFunc, killPodFunc eviction.KillPodFunc, syncNodeStatus func(), shutdownGracePeriodRequested, shutdownGracePeriodCriticalPods time.Duration) (*Manager, lifecycle.PodAdmitHandler) {
manager := &Manager{
getPods: getPodsFunc,
killPod: killPodFunc,
syncNodeStatus: syncNodeStatus,
shutdownGracePeriodRequested: shutdownGracePeriodRequested,
shutdownGracePeriodCriticalPods: shutdownGracePeriodCriticalPods,
clock: clock.RealClock{},
}
return manager, manager
}
// Admit rejects all pods if node is shutting
func (m *Manager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
nodeShuttingDown := m.ShutdownStatus() != nil
if nodeShuttingDown {
return lifecycle.PodAdmitResult{
Admit: false,
Reason: nodeShutdownReason,
Message: nodeShutdownNotAdmitMessage,
}
}
return lifecycle.PodAdmitResult{Admit: true}
}
// Start starts the node shutdown manager and will start watching the node for shutdown events.
......@@ -158,6 +177,9 @@ func (m *Manager) Start() error {
m.nodeShuttingDownMutex.Unlock()
if isShuttingDown {
// Update node status and ready condition
go m.syncNodeStatus()
m.processShutdownEvent()
} else {
m.aquireInhibitLock()
......@@ -173,6 +195,9 @@ func (m *Manager) aquireInhibitLock() error {
if err != nil {
return err
}
if m.inhibitLock != 0 {
m.dbusCon.ReleaseInhibitLock(m.inhibitLock)
}
m.inhibitLock = lock
return nil
}
......
......@@ -22,14 +22,21 @@ import (
"time"
"k8s.io/kubernetes/pkg/kubelet/eviction"
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
)
// Manager is a fake node shutdown manager for non linux platforms.
type Manager struct{}
// NewManager returns a fake node shutdown manager for non linux platforms.
func NewManager(getPodsFunc eviction.ActivePodsFunc, killPodFunc eviction.KillPodFunc, shutdownGracePeriodRequested, shutdownGracePeriodCriticalPods time.Duration) *Manager {
return &Manager{}
func NewManager(getPodsFunc eviction.ActivePodsFunc, killPodFunc eviction.KillPodFunc, syncNodeStatus func(), shutdownGracePeriodRequested, shutdownGracePeriodCriticalPods time.Duration) (*Manager, lifecycle.PodAdmitHandler) {
m := &Manager{}
return m, m
}
// Admit returns a fake Pod admission which always returns true
func (m *Manager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
return lifecycle.PodAdmitResult{Admit: true}
}
// Start is a no-op always returning nil for non linux platforms.
......
......@@ -142,11 +142,12 @@ func (bus *DBusCon) MonitorShutdown() (<-chan bool, error) {
case event := <-busChan:
if event == nil || len(event.Body) == 0 {
klog.Errorf("Failed obtaining shutdown event, PrepareForShutdown event was empty")
continue
}
shutdownActive, ok := event.Body[0].(bool)
if !ok {
klog.Errorf("Failed obtaining shutdown event, PrepareForShutdown event was not bool type as expected")
return
continue
}
shutdownChan <- shutdownActive
}
......@@ -178,7 +179,7 @@ InhibitDelayMaxSec=%.0f
`, inhibitDelayMax.Seconds())
logindOverridePath := filepath.Join(logindConfigDirectory, kubeletLogindConf)
if err := ioutil.WriteFile(logindOverridePath, []byte(inhibitOverride), 0755); err != nil {
if err := ioutil.WriteFile(logindOverridePath, []byte(inhibitOverride), 0644); err != nil {
return fmt.Errorf("failed writing logind shutdown inhibit override file %v: %v", logindOverridePath, err)
}
......
......@@ -160,6 +160,12 @@ func (w *Watcher) handleCreateEvent(event fsnotify.Event) error {
klog.V(6).Infof("Handling create event: %v", event)
fi, err := os.Stat(event.Name)
// TODO: This is a workaround for Windows 20H2 issue for os.Stat(). Please see
// microsoft/Windows-Containers#97 for details.
// Once the issue is resvolved, the following os.Lstat() is not needed.
if err != nil && runtime.GOOS == "windows" {
fi, err = os.Lstat(event.Name)
}
if err != nil {
return fmt.Errorf("stat file %s failed: %v", event.Name, err)
}
......
......@@ -25,6 +25,7 @@ go_library(
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/discovery/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
......
......@@ -36,6 +36,7 @@ import (
v1 "k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1beta1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/record"
......@@ -846,14 +847,23 @@ func (proxier *Proxier) syncProxyRules() {
serviceUpdateResult := proxy.UpdateServiceMap(proxier.serviceMap, proxier.serviceChanges)
endpointUpdateResult := proxier.endpointsMap.Update(proxier.endpointsChanges)
staleServices := serviceUpdateResult.UDPStaleClusterIP
// We need to detect stale connections to UDP Services so we
// can clean dangling conntrack entries that can blackhole traffic.
conntrackCleanupServiceIPs := serviceUpdateResult.UDPStaleClusterIP
conntrackCleanupServiceNodePorts := sets.NewInt()
// merge stale services gathered from updateEndpointsMap
// an UDP service that changes from 0 to non-0 endpoints is considered stale.
for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && conntrack.IsClearConntrackNeeded(svcInfo.Protocol()) {
klog.V(2).Infof("Stale %s service %v -> %s", strings.ToLower(string(svcInfo.Protocol())), svcPortName, svcInfo.ClusterIP().String())
staleServices.Insert(svcInfo.ClusterIP().String())
conntrackCleanupServiceIPs.Insert(svcInfo.ClusterIP().String())
for _, extIP := range svcInfo.ExternalIPStrings() {
staleServices.Insert(extIP)
conntrackCleanupServiceIPs.Insert(extIP)
}
nodePort := svcInfo.NodePort()
if svcInfo.Protocol() == v1.ProtocolUDP && nodePort != 0 {
klog.V(2).Infof("Stale %s service NodePort %v -> %d", strings.ToLower(string(svcInfo.Protocol())), svcPortName, nodePort)
conntrackCleanupServiceNodePorts.Insert(nodePort)
}
}
}
......@@ -1278,16 +1288,6 @@ func (proxier *Proxier) syncProxyRules() {
klog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err)
continue
}
if lp.Protocol == "udp" {
// TODO: We might have multiple services using the same port, and this will clear conntrack for all of them.
// This is very low impact. The NodePort range is intentionally obscure, and unlikely to actually collide with real Services.
// This only affects UDP connections, which are not common.
// See issue: https://github.com/kubernetes/kubernetes/issues/49881
err := conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, v1.ProtocolUDP)
if err != nil {
klog.Errorf("Failed to clear udp conntrack for port %d, error: %v", lp.Port, err)
}
}
replacementPortsMap[lp] = socket
}
}
......@@ -1633,14 +1633,22 @@ func (proxier *Proxier) syncProxyRules() {
}
// Finish housekeeping.
// Clear stale conntrack entries for UDP Services, this has to be done AFTER the iptables rules are programmed.
// TODO: these could be made more consistent.
klog.V(4).Infof("Deleting stale services IPs: %v", staleServices.UnsortedList())
for _, svcIP := range staleServices.UnsortedList() {
klog.V(4).Infof("Deleting conntrack stale entries for ClusterIP Services: %v", conntrackCleanupServiceIPs.UnsortedList())
for _, svcIP := range conntrackCleanupServiceIPs.UnsortedList() {
if err := conntrack.ClearEntriesForIP(proxier.exec, svcIP, v1.ProtocolUDP); err != nil {
klog.Errorf("Failed to delete stale service IP %s connections, error: %v", svcIP, err)
}
}
klog.V(4).Infof("Deleting stale endpoint connections: %v", endpointUpdateResult.StaleEndpoints)
klog.V(4).Infof("Deleting conntrack stale entries for NodePorts Services: %v", conntrackCleanupServiceNodePorts.UnsortedList())
for _, nodePort := range conntrackCleanupServiceNodePorts.UnsortedList() {
err := conntrack.ClearEntriesForPort(proxier.exec, nodePort, isIPv6, v1.ProtocolUDP)
if err != nil {
klog.Errorf("Failed to clear udp conntrack on port %d: %v", nodePort, err)
}
}
klog.V(4).Infof("Deleting stale endpoint connections %v", endpointUpdateResult.StaleEndpoints)
proxier.deleteEndpointConnections(endpointUpdateResult.StaleEndpoints)
}
......
......@@ -46,6 +46,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//pkg/util/node:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
......@@ -54,6 +55,8 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/quota/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/quota/v1/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
],
)
......
......@@ -32,10 +32,12 @@ import (
"k8s.io/apiserver/pkg/admission"
quota "k8s.io/apiserver/pkg/quota/v1"
"k8s.io/apiserver/pkg/quota/v1/generic"
"k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
"k8s.io/kubernetes/pkg/features"
)
// the name used for object count quota
......@@ -351,6 +353,10 @@ func PodUsageFunc(obj runtime.Object, clock clock.Clock) (corev1.ResourceList, e
limits = quota.Max(limits, pod.Spec.InitContainers[i].Resources.Limits)
}
if feature.DefaultFeatureGate.Enabled(features.PodOverhead) {
requests = quota.Add(requests, pod.Spec.Overhead)
limits = quota.Add(limits, pod.Spec.Overhead)
}
result = quota.Add(result, podComputeUsageHelper(requests, limits))
return result, nil
}
......
......@@ -296,13 +296,8 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
volDataKey.volHandle: csiSource.VolumeHandle,
volDataKey.driverName: csiSource.Driver,
}
if err = saveVolumeData(dataDir, volDataFileName, data); err != nil {
klog.Error(log("failed to save volume info data: %v", err))
if cleanErr := os.RemoveAll(dataDir); cleanErr != nil {
klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, cleanErr))
}
return err
}
err = saveVolumeData(dataDir, volDataFileName, data)
defer func() {
// Only if there was an error and volume operation was considered
// finished, we should remove the directory.
......@@ -315,6 +310,12 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
}
}()
if err != nil {
errMsg := log("failed to save volume info data: %v", err)
klog.Error(errMsg)
return errors.New(errMsg)
}
if !stageUnstageSet {
klog.Infof(log("attacher.MountDevice STAGE_UNSTAGE_VOLUME capability not set. Skipping MountDevice..."))
// defer does *not* remove the metadata file and it's correct - UnmountDevice needs it there.
......
......@@ -43,6 +43,7 @@ import (
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/csi/nodeinfomanager"
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
)
const (
......@@ -461,11 +462,23 @@ func (p *csiPlugin) NewMounter(
attachID := getAttachmentName(volumeHandle, driverName, node)
volData[volDataKey.attachmentID] = attachID
if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil {
if removeErr := os.RemoveAll(dataDir); removeErr != nil {
klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, removeErr))
err = saveVolumeData(dataDir, volDataFileName, volData)
defer func() {
// Only if there was an error and volume operation was considered
// finished, we should remove the directory.
if err != nil && volumetypes.IsOperationFinishedError(err) {
// attempt to cleanup volume mount dir.
if err = removeMountDir(p, dir); err != nil {
klog.Error(log("attacher.MountDevice failed to remove mount dir after error [%s]: %v", dir, err))
}
}
return nil, errors.New(log("failed to save volume info data: %v", err))
}()
if err != nil {
errorMsg := log("csi.NewMounter failed to save volume info data: %v", err)
klog.Error(errorMsg)
return nil, errors.New(errorMsg)
}
klog.V(4).Info(log("mounter created successfully"))
......@@ -706,11 +719,21 @@ func (p *csiPlugin) NewBlockVolumeMapper(spec *volume.Spec, podRef *api.Pod, opt
volDataKey.attachmentID: attachID,
}
if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil {
if removeErr := os.RemoveAll(dataDir); removeErr != nil {
klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, removeErr))
err = saveVolumeData(dataDir, volDataFileName, volData)
defer func() {
// Only if there was an error and volume operation was considered
// finished, we should remove the directory.
if err != nil && volumetypes.IsOperationFinishedError(err) {
// attempt to cleanup volume mount dir.
if err = removeMountDir(p, dataDir); err != nil {
klog.Error(log("attacher.MountDevice failed to remove mount dir after error [%s]: %v", dataDir, err))
}
}
return nil, errors.New(log("failed to save volume info data: %v", err))
}()
if err != nil {
errorMsg := log("csi.NewBlockVolumeMapper failed to save volume info data: %v", err)
klog.Error(errorMsg)
return nil, errors.New(errorMsg)
}
return mapper, nil
......
......@@ -75,7 +75,7 @@ func getUpperPath(path string) string {
// Check whether a directory/file is a link type or not
// LinkType could be SymbolicLink, Junction, or HardLink
func isLinkPath(path string) (bool, error) {
cmd := fmt.Sprintf("(Get-Item -Path %s).LinkType", path)
cmd := fmt.Sprintf("(Get-Item -LiteralPath %q).LinkType", path)
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
if err != nil {
return false, err
......@@ -113,7 +113,7 @@ func evalSymlink(path string) (string, error) {
}
}
// This command will give the target path of a given symlink
cmd := fmt.Sprintf("(Get-Item -Path %s).Target", upperpath)
cmd := fmt.Sprintf("(Get-Item -LiteralPath %q).Target", upperpath)
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
if err != nil {
return "", err
......
......@@ -1630,7 +1630,13 @@ func (az *Cloud) reconcileLoadBalancerRule(
var expectedProbes []network.Probe
var expectedRules []network.LoadBalancingRule
highAvailabilityPortsEnabled := false
for _, port := range ports {
if highAvailabilityPortsEnabled {
// Since the port is always 0 when enabling HA, only one rule should be configured.
break
}
protocols := []v1.Protocol{port.Protocol}
if v, ok := service.Annotations[ServiceAnnotationLoadBalancerMixedProtocols]; ok && v == "true" {
klog.V(2).Infof("reconcileLoadBalancerRule lb name (%s) flag(%s) is set", lbName, ServiceAnnotationLoadBalancerMixedProtocols)
......@@ -1729,6 +1735,7 @@ func (az *Cloud) reconcileLoadBalancerRule(
expectedRule.FrontendPort = to.Int32Ptr(0)
expectedRule.BackendPort = to.Int32Ptr(0)
expectedRule.Protocol = network.TransportProtocolAll
highAvailabilityPortsEnabled = true
}
// we didn't construct the probe objects for UDP or SCTP because they're not allowed on Azure.
......
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
......@@ -12,7 +11,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
......@@ -24,7 +22,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
......
......@@ -115,6 +115,12 @@ func (t *grpcTunnel) serve(c clientConn) {
connid: resp.ConnectID,
}
}
if resp.Error != "" {
// On dial error, avoid leaking serve goroutine.
return
}
case client.PacketType_DATA:
resp := pkt.GetData()
// TODO: flow control
......
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