Commit 5f6a6c68 authored by Darren Shepherd's avatar Darren Shepherd

Update vendor

parent 4da04f56
...@@ -9,7 +9,7 @@ package=github.com/opencontainers/runc/libcontainer/nsenter ...@@ -9,7 +9,7 @@ package=github.com/opencontainers/runc/libcontainer/nsenter
package=github.com/opencontainers/runc/libcontainer/specconv package=github.com/opencontainers/runc/libcontainer/specconv
package=github.com/opencontainers/runc/contrib/cmd/recvtty package=github.com/opencontainers/runc/contrib/cmd/recvtty
k8s.io/kubernetes v1.13.3-k3s.7 https://github.com/rancher/k3s.git transitive=true,staging=true k8s.io/kubernetes v1.13.4-k3s.1 https://github.com/rancher/k3s.git transitive=true,staging=true
github.com/rancher/norman 816007443daf04e96c7806c2a637dfab8ede9429 https://github.com/ibuildthecloud/norman.git github.com/rancher/norman 816007443daf04e96c7806c2a637dfab8ede9429 https://github.com/ibuildthecloud/norman.git
github.com/coreos/flannel 823afe66b2266bf71f5bec24e6e28b26d70cfc7c https://github.com/ibuildthecloud/flannel.git github.com/coreos/flannel 823afe66b2266bf71f5bec24e6e28b26d70cfc7c https://github.com/ibuildthecloud/flannel.git
......
...@@ -25,6 +25,19 @@ go get -u github.com/evanphx/json-patch ...@@ -25,6 +25,19 @@ go get -u github.com/evanphx/json-patch
* [Comparing JSON documents](#comparing-json-documents) * [Comparing JSON documents](#comparing-json-documents)
* [Combine merge patches](#combine-merge-patches) * [Combine merge patches](#combine-merge-patches)
# Configuration
* There is a global configuration variable `jsonpatch.SupportNegativeIndices`.
This defaults to `true` and enables the non-standard practice of allowing
negative indices to mean indices starting at the end of an array. This
functionality can be disabled by setting `jsonpatch.SupportNegativeIndices =
false`.
* There is a global configuration variable `jsonpatch.AccumulatedCopySizeLimit`,
which limits the total size increase in bytes caused by "copy" operations in a
patch. It defaults to 0, which means there is no limit.
## Create and apply a merge patch ## Create and apply a merge patch
Given both an original JSON document and a modified JSON document, you can create Given both an original JSON document and a modified JSON document, you can create
a [Merge Patch](https://tools.ietf.org/html/rfc7396) document. a [Merge Patch](https://tools.ietf.org/html/rfc7396) document.
......
package jsonpatch
import "fmt"
// AccumulatedCopySizeError is an error type returned when the accumulated size
// increase caused by copy operations in a patch operation has exceeded the
// limit.
type AccumulatedCopySizeError struct {
limit int64
accumulated int64
}
// NewAccumulatedCopySizeError returns an AccumulatedCopySizeError.
func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError {
return &AccumulatedCopySizeError{limit: l, accumulated: a}
}
// Error implements the error interface.
func (a *AccumulatedCopySizeError) Error() string {
return fmt.Sprintf("Unable to complete the copy, the accumulated size increase of copy is %d, exceeding the limit %d", a.accumulated, a.limit)
}
// ArraySizeError is an error type returned when the array size has exceeded
// the limit.
type ArraySizeError struct {
limit int
size int
}
// NewArraySizeError returns an ArraySizeError.
func NewArraySizeError(l, s int) *ArraySizeError {
return &ArraySizeError{limit: l, size: s}
}
// Error implements the error interface.
func (a *ArraySizeError) Error() string {
return fmt.Sprintf("Unable to create array of size %d, limit is %d", a.size, a.limit)
}
...@@ -14,6 +14,16 @@ const ( ...@@ -14,6 +14,16 @@ const (
eAry eAry
) )
var (
// SupportNegativeIndices decides whether to support non-standard practice of
// allowing negative indices to mean indices starting at the end of an array.
// Default to true.
SupportNegativeIndices bool = true
// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
// "copy" operations in a patch.
AccumulatedCopySizeLimit int64 = 0
)
type lazyNode struct { type lazyNode struct {
raw *json.RawMessage raw *json.RawMessage
doc partialDoc doc partialDoc
...@@ -61,6 +71,20 @@ func (n *lazyNode) UnmarshalJSON(data []byte) error { ...@@ -61,6 +71,20 @@ func (n *lazyNode) UnmarshalJSON(data []byte) error {
return nil return nil
} }
func deepCopy(src *lazyNode) (*lazyNode, int, error) {
if src == nil {
return nil, 0, nil
}
a, err := src.MarshalJSON()
if err != nil {
return nil, 0, err
}
sz := len(a)
ra := make(json.RawMessage, sz)
copy(ra, a)
return newLazyNode(&ra), sz, nil
}
func (n *lazyNode) intoDoc() (*partialDoc, error) { func (n *lazyNode) intoDoc() (*partialDoc, error) {
if n.which == eDoc { if n.which == eDoc {
return &n.doc, nil return &n.doc, nil
...@@ -342,35 +366,14 @@ func (d *partialDoc) remove(key string) error { ...@@ -342,35 +366,14 @@ func (d *partialDoc) remove(key string) error {
return nil return nil
} }
// set should only be used to implement the "replace" operation, so "key" must
// be an already existing index in "d".
func (d *partialArray) set(key string, val *lazyNode) error { func (d *partialArray) set(key string, val *lazyNode) error {
if key == "-" {
*d = append(*d, val)
return nil
}
idx, err := strconv.Atoi(key) idx, err := strconv.Atoi(key)
if err != nil { if err != nil {
return err return err
} }
(*d)[idx] = val
sz := len(*d)
if idx+1 > sz {
sz = idx + 1
}
ary := make([]*lazyNode, sz)
cur := *d
copy(ary, cur)
if idx >= len(ary) {
return fmt.Errorf("Unable to access invalid index: %d", idx)
}
ary[idx] = val
*d = ary
return nil return nil
} }
...@@ -385,17 +388,26 @@ func (d *partialArray) add(key string, val *lazyNode) error { ...@@ -385,17 +388,26 @@ func (d *partialArray) add(key string, val *lazyNode) error {
return err return err
} }
ary := make([]*lazyNode, len(*d)+1) sz := len(*d) + 1
ary := make([]*lazyNode, sz)
cur := *d cur := *d
if idx < -len(ary) || idx >= len(ary) { if idx >= len(ary) {
return fmt.Errorf("Unable to access invalid index: %d", idx) return fmt.Errorf("Unable to access invalid index: %d", idx)
} }
if idx < 0 { if SupportNegativeIndices {
idx += len(ary) if idx < -len(ary) {
return fmt.Errorf("Unable to access invalid index: %d", idx)
}
if idx < 0 {
idx += len(ary)
}
} }
copy(ary[0:idx], cur[0:idx]) copy(ary[0:idx], cur[0:idx])
ary[idx] = val ary[idx] = val
copy(ary[idx+1:], cur[idx:]) copy(ary[idx+1:], cur[idx:])
...@@ -426,11 +438,18 @@ func (d *partialArray) remove(key string) error { ...@@ -426,11 +438,18 @@ func (d *partialArray) remove(key string) error {
cur := *d cur := *d
if idx < -len(cur) || idx >= len(cur) { if idx >= len(cur) {
return fmt.Errorf("Unable to remove invalid index: %d", idx) return fmt.Errorf("Unable to access invalid index: %d", idx)
} }
if idx < 0 {
idx += len(cur) if SupportNegativeIndices {
if idx < -len(cur) {
return fmt.Errorf("Unable to access invalid index: %d", idx)
}
if idx < 0 {
idx += len(cur)
}
} }
ary := make([]*lazyNode, len(cur)-1) ary := make([]*lazyNode, len(cur)-1)
...@@ -511,7 +530,7 @@ func (p Patch) move(doc *container, op operation) error { ...@@ -511,7 +530,7 @@ func (p Patch) move(doc *container, op operation) error {
return fmt.Errorf("jsonpatch move operation does not apply: doc is missing destination path: %s", path) return fmt.Errorf("jsonpatch move operation does not apply: doc is missing destination path: %s", path)
} }
return con.set(key, val) return con.add(key, val)
} }
func (p Patch) test(doc *container, op operation) error { func (p Patch) test(doc *container, op operation) error {
...@@ -545,7 +564,7 @@ func (p Patch) test(doc *container, op operation) error { ...@@ -545,7 +564,7 @@ func (p Patch) test(doc *container, op operation) error {
return fmt.Errorf("Testing value %s failed", path) return fmt.Errorf("Testing value %s failed", path)
} }
func (p Patch) copy(doc *container, op operation) error { func (p Patch) copy(doc *container, op operation, accumulatedCopySize *int64) error {
from := op.from() from := op.from()
con, key := findObject(doc, from) con, key := findObject(doc, from)
...@@ -567,7 +586,16 @@ func (p Patch) copy(doc *container, op operation) error { ...@@ -567,7 +586,16 @@ func (p Patch) copy(doc *container, op operation) error {
return fmt.Errorf("jsonpatch copy operation does not apply: doc is missing destination path: %s", path) return fmt.Errorf("jsonpatch copy operation does not apply: doc is missing destination path: %s", path)
} }
return con.set(key, val) valCopy, sz, err := deepCopy(val)
if err != nil {
return err
}
(*accumulatedCopySize) += int64(sz)
if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit {
return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize)
}
return con.add(key, valCopy)
} }
// Equal indicates if 2 JSON documents have the same structural equality. // Equal indicates if 2 JSON documents have the same structural equality.
...@@ -620,6 +648,8 @@ func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { ...@@ -620,6 +648,8 @@ func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
err = nil err = nil
var accumulatedCopySize int64
for _, op := range p { for _, op := range p {
switch op.kind() { switch op.kind() {
case "add": case "add":
...@@ -633,7 +663,7 @@ func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { ...@@ -633,7 +663,7 @@ func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
case "test": case "test":
err = p.test(&pd, op) err = p.test(&pd, op)
case "copy": case "copy":
err = p.copy(&pd, op) err = p.copy(&pd, op, &accumulatedCopySize)
default: default:
err = fmt.Errorf("Unexpected kind: %s", op.kind()) err = fmt.Errorf("Unexpected kind: %s", op.kind())
} }
......
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package containerd
import (
"context"
"fmt"
"net"
"sync"
"time"
containersapi "github.com/containerd/containerd/api/services/containers/v1"
tasksapi "github.com/containerd/containerd/api/services/tasks/v1"
versionapi "github.com/containerd/containerd/api/services/version/v1"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
ptypes "github.com/gogo/protobuf/types"
"google.golang.org/grpc"
"k8s.io/kubernetes/pkg/kubelet/util"
)
const (
// k8sNamespace is the namespace we use to connect containerd.
k8sNamespace = "k8s.io"
)
type client struct {
containerService containersapi.ContainersClient
taskService tasksapi.TasksClient
versionService versionapi.VersionClient
}
type containerdClient interface {
LoadContainer(ctx context.Context, id string) (*containers.Container, error)
TaskPid(ctx context.Context, id string) (uint32, error)
Version(ctx context.Context) (string, error)
}
var once sync.Once
var ctrdClient containerdClient = nil
const (
address = "/run/containerd/containerd.sock"
maxBackoffDelay = 3 * time.Second
connectionTimeout = 2 * time.Second
)
// Client creates a containerd client
func Client() (containerdClient, error) {
var retErr error
once.Do(func() {
tryConn, err := net.DialTimeout("unix", address, connectionTimeout)
if err != nil {
retErr = fmt.Errorf("containerd: cannot unix dial containerd api service: %v", err)
return
}
tryConn.Close()
addr, dialer, err := util.GetAddressAndDialer(address)
if err != nil {
retErr = err
return
}
gopts := []grpc.DialOption{
grpc.WithInsecure(),
grpc.WithDialer(dialer),
grpc.WithBlock(),
grpc.WithBackoffMaxDelay(maxBackoffDelay),
grpc.WithTimeout(connectionTimeout),
}
unary, stream := newNSInterceptors(k8sNamespace)
gopts = append(gopts,
grpc.WithUnaryInterceptor(unary),
grpc.WithStreamInterceptor(stream),
)
conn, err := grpc.Dial(addr, gopts...)
if err != nil {
retErr = err
return
}
ctrdClient = &client{
containerService: containersapi.NewContainersClient(conn),
taskService: tasksapi.NewTasksClient(conn),
versionService: versionapi.NewVersionClient(conn),
}
})
return ctrdClient, retErr
}
func (c *client) LoadContainer(ctx context.Context, id string) (*containers.Container, error) {
r, err := c.containerService.Get(ctx, &containersapi.GetContainerRequest{
ID: id,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return containerFromProto(r.Container), nil
}
func (c *client) TaskPid(ctx context.Context, id string) (uint32, error) {
response, err := c.taskService.Get(ctx, &tasksapi.GetRequest{
ContainerID: id,
})
if err != nil {
return 0, errdefs.FromGRPC(err)
}
return response.Process.Pid, nil
}
func (c *client) Version(ctx context.Context) (string, error) {
response, err := c.versionService.Version(ctx, &ptypes.Empty{})
if err != nil {
return "", errdefs.FromGRPC(err)
}
return response.Version, nil
}
func containerFromProto(containerpb containersapi.Container) *containers.Container {
var runtime containers.RuntimeInfo
if containerpb.Runtime != nil {
runtime = containers.RuntimeInfo{
Name: containerpb.Runtime.Name,
Options: containerpb.Runtime.Options,
}
}
return &containers.Container{
ID: containerpb.ID,
Labels: containerpb.Labels,
Image: containerpb.Image,
Runtime: runtime,
Spec: containerpb.Spec,
Snapshotter: containerpb.Snapshotter,
SnapshotKey: containerpb.SnapshotKey,
Extensions: containerpb.Extensions,
}
}
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package containerd
import (
"flag"
"fmt"
"path"
"regexp"
"strings"
"golang.org/x/net/context"
"k8s.io/klog"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/manager/watcher"
)
var ArgContainerdEndpoint = flag.String("containerd", "unix:///var/run/containerd.sock", "containerd endpoint")
// The namespace under which containerd aliases are unique.
const k8sContainerdNamespace = "containerd"
// Regexp that identifies containerd cgroups, containers started with
// --cgroup-parent have another prefix than 'containerd'
var containerdCgroupRegexp = regexp.MustCompile(`([a-z0-9]{64})`)
type containerdFactory struct {
machineInfoFactory info.MachineInfoFactory
client containerdClient
version string
// Information about the mounted cgroup subsystems.
cgroupSubsystems libcontainer.CgroupSubsystems
// Information about mounted filesystems.
fsInfo fs.FsInfo
includedMetrics container.MetricSet
}
func (self *containerdFactory) String() string {
return k8sContainerdNamespace
}
func (self *containerdFactory) NewContainerHandler(name string, inHostNamespace bool) (handler container.ContainerHandler, err error) {
client, err := Client()
if err != nil {
return
}
metadataEnvs := []string{}
return newContainerdContainerHandler(
client,
name,
self.machineInfoFactory,
self.fsInfo,
&self.cgroupSubsystems,
inHostNamespace,
metadataEnvs,
self.includedMetrics,
)
}
// Returns the containerd ID from the full container name.
func ContainerNameToContainerdID(name string) string {
id := path.Base(name)
if matches := containerdCgroupRegexp.FindStringSubmatch(id); matches != nil {
return matches[1]
}
return id
}
// isContainerName returns true if the cgroup with associated name
// corresponds to a containerd container.
func isContainerName(name string) bool {
// TODO: May be check with HasPrefix ContainerdNamespace
if strings.HasSuffix(name, ".mount") {
return false
}
return containerdCgroupRegexp.MatchString(path.Base(name))
}
// Containerd can handle and accept all containerd created containers
func (self *containerdFactory) CanHandleAndAccept(name string) (bool, bool, error) {
// if the container is not associated with containerd, we can't handle it or accept it.
if !isContainerName(name) {
return false, false, nil
}
// Check if the container is known to containerd and it is running.
id := ContainerNameToContainerdID(name)
// If container and task lookup in containerd fails then we assume
// that the container state is not known to containerd
ctx := context.Background()
_, err := self.client.LoadContainer(ctx, id)
if err != nil {
return false, false, fmt.Errorf("failed to load container: %v", err)
}
return true, true, nil
}
func (self *containerdFactory) DebugInfo() map[string][]string {
return map[string][]string{}
}
// Register root container before running this function!
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includedMetrics container.MetricSet) error {
client, err := Client()
if err != nil {
return fmt.Errorf("unable to create containerd client: %v", err)
}
containerdVersion, err := client.Version(context.Background())
if err != nil {
return fmt.Errorf("failed to fetch containerd client version: %v", err)
}
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
if err != nil {
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
klog.V(1).Infof("Registering containerd factory")
f := &containerdFactory{
cgroupSubsystems: cgroupSubsystems,
client: client,
fsInfo: fsInfo,
machineInfoFactory: factory,
version: containerdVersion,
includedMetrics: includedMetrics,
}
container.RegisterContainerHandlerFactory(f, []watcher.ContainerWatchSource{watcher.Raw})
return nil
}
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//This code has been taken from containerd repo to avoid large library import
package containerd
import (
"github.com/containerd/containerd/namespaces"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
type namespaceInterceptor struct {
namespace string
}
func (ni namespaceInterceptor) unary(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
_, ok := namespaces.Namespace(ctx)
if !ok {
ctx = namespaces.WithNamespace(ctx, ni.namespace)
}
return invoker(ctx, method, req, reply, cc, opts...)
}
func (ni namespaceInterceptor) stream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
_, ok := namespaces.Namespace(ctx)
if !ok {
ctx = namespaces.WithNamespace(ctx, ni.namespace)
}
return streamer(ctx, desc, cc, method, opts...)
}
func newNSInterceptors(ns string) (grpc.UnaryClientInterceptor, grpc.StreamClientInterceptor) {
ni := namespaceInterceptor{
namespace: ns,
}
return grpc.UnaryClientInterceptor(ni.unary), grpc.StreamClientInterceptor(ni.stream)
}
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Handler for containerd containers.
package containerd
import (
"encoding/json"
"fmt"
"path"
"strings"
"time"
"github.com/containerd/containerd/errdefs"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
"golang.org/x/net/context"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/fs"
info "github.com/google/cadvisor/info/v1"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
type containerdContainerHandler struct {
machineInfoFactory info.MachineInfoFactory
// Absolute path to the cgroup hierarchies of this container.
// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
cgroupPaths map[string]string
fsInfo fs.FsInfo
// Metadata associated with the container.
reference info.ContainerReference
envs map[string]string
labels map[string]string
// Image name used for this container.
image string
// Filesystem handler.
includedMetrics container.MetricSet
libcontainerHandler *containerlibcontainer.Handler
}
var _ container.ContainerHandler = &containerdContainerHandler{}
// newContainerdContainerHandler returns a new container.ContainerHandler
func newContainerdContainerHandler(
client containerdClient,
name string,
machineInfoFactory info.MachineInfoFactory,
fsInfo fs.FsInfo,
cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
inHostNamespace bool,
metadataEnvs []string,
includedMetrics container.MetricSet,
) (container.ContainerHandler, error) {
// Create the cgroup paths.
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
for key, val := range cgroupSubsystems.MountPoints {
cgroupPaths[key] = path.Join(val, name)
}
// Generate the equivalent cgroup manager for this container.
cgroupManager := &cgroupfs.Manager{
Cgroups: &libcontainerconfigs.Cgroup{
Name: name,
},
Paths: cgroupPaths,
}
id := ContainerNameToContainerdID(name)
// We assume that if load fails then the container is not known to containerd.
ctx := context.Background()
cntr, err := client.LoadContainer(ctx, id)
if err != nil {
return nil, err
}
var spec specs.Spec
if err := json.Unmarshal(cntr.Spec.Value, &spec); err != nil {
return nil, err
}
// Cgroup is created during task creation. When cadvisor sees the cgroup,
// task may not be fully created yet. Use a retry+backoff to tolerant the
// race condition.
// TODO(random-liu): Use cri-containerd client to talk with cri-containerd
// instead. cri-containerd has some internal synchronization to make sure
// `ContainerStatus` only returns result after `StartContainer` finishes.
var taskPid uint32
backoff := 100 * time.Millisecond
retry := 5
for {
taskPid, err = client.TaskPid(ctx, id)
if err == nil {
break
}
retry--
if !errdefs.IsNotFound(err) || retry == 0 {
return nil, err
}
time.Sleep(backoff)
backoff *= 2
}
rootfs := "/"
if !inHostNamespace {
rootfs = "/rootfs"
}
containerReference := info.ContainerReference{
Id: id,
Name: name,
Namespace: k8sContainerdNamespace,
Aliases: []string{id, name},
}
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), includedMetrics)
handler := &containerdContainerHandler{
machineInfoFactory: machineInfoFactory,
cgroupPaths: cgroupPaths,
fsInfo: fsInfo,
envs: make(map[string]string),
labels: cntr.Labels,
includedMetrics: includedMetrics,
reference: containerReference,
libcontainerHandler: libcontainerHandler,
}
// Add the name and bare ID as aliases of the container.
handler.image = cntr.Image
for _, envVar := range spec.Process.Env {
if envVar != "" {
splits := strings.SplitN(envVar, "=", 2)
if len(splits) == 2 {
handler.envs[splits[0]] = splits[1]
}
}
}
return handler, nil
}
func (self *containerdContainerHandler) ContainerReference() (info.ContainerReference, error) {
return self.reference, nil
}
func (self *containerdContainerHandler) needNet() bool {
// Since containerd does not handle networking ideally we need to return based
// on includedMetrics list. Here the assumption is the presence of cri-containerd
// label
if self.includedMetrics.Has(container.NetworkUsageMetrics) {
//TODO change it to exported cri-containerd constants
return self.labels["io.cri-containerd.kind"] == "sandbox"
}
return false
}
func (self *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
// TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
// to false. Revisit when we support disk usage stats for containerd
hasFilesystem := false
spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
spec.Labels = self.labels
spec.Envs = self.envs
spec.Image = self.image
return spec, err
}
func (self *containerdContainerHandler) getFsStats(stats *info.ContainerStats) error {
mi, err := self.machineInfoFactory.GetMachineInfo()
if err != nil {
return err
}
if self.includedMetrics.Has(container.DiskIOMetrics) {
common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
}
return nil
}
func (self *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
stats, err := self.libcontainerHandler.GetStats()
if err != nil {
return stats, err
}
// Clean up stats for containers that don't have their own network - this
// includes containers running in Kubernetes pods that use the network of the
// infrastructure container. This stops metrics being reported multiple times
// for each container in a pod.
if !self.needNet() {
stats.Network = info.NetworkStats{}
}
// Get filesystem stats.
err = self.getFsStats(stats)
return stats, err
}
func (self *containerdContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
return []info.ContainerReference{}, nil
}
func (self *containerdContainerHandler) GetCgroupPath(resource string) (string, error) {
path, ok := self.cgroupPaths[resource]
if !ok {
return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.reference.Name)
}
return path, nil
}
func (self *containerdContainerHandler) GetContainerLabels() map[string]string {
return self.labels
}
func (self *containerdContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
return self.libcontainerHandler.GetProcesses()
}
func (self *containerdContainerHandler) Exists() bool {
return common.CgroupExists(self.cgroupPaths)
}
func (self *containerdContainerHandler) Type() container.ContainerType {
return container.ContainerTypeContainerd
}
func (self *containerdContainerHandler) Start() {
}
func (self *containerdContainerHandler) Cleanup() {
}
func (self *containerdContainerHandler) GetContainerIPAddress() string {
// containerd doesnt take care of networking.So it doesnt maintain networking states
return ""
}
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
"github.com/google/cadvisor/cache/memory" "github.com/google/cadvisor/cache/memory"
"github.com/google/cadvisor/collector" "github.com/google/cadvisor/collector"
"github.com/google/cadvisor/container" "github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/containerd"
"github.com/google/cadvisor/container/docker" "github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/container/raw" "github.com/google/cadvisor/container/raw"
"github.com/google/cadvisor/events" "github.com/google/cadvisor/events"
...@@ -275,6 +276,11 @@ func (self *manager) Start() error { ...@@ -275,6 +276,11 @@ func (self *manager) Start() error {
klog.V(5).Infof("Registration of the Docker container factory failed: %v.", err) klog.V(5).Infof("Registration of the Docker container factory failed: %v.", err)
} }
err = containerd.Register(self, self.fsInfo, self.includedMetrics)
if err != nil {
klog.V(5).Infof("Registration of the containerd container factory failed: %v", err)
}
err = raw.Register(self, self.fsInfo, self.includedMetrics, self.rawContainerCgroupPathPrefixWhiteList) err = raw.Register(self, self.fsInfo, self.includedMetrics, self.rawContainerCgroupPathPrefixWhiteList)
if err != nil { if err != nil {
klog.Errorf("Registration of the raw container factory failed: %v", err) klog.Errorf("Registration of the raw container factory failed: %v", err)
......
...@@ -3156,6 +3156,7 @@ message PodSpec { ...@@ -3156,6 +3156,7 @@ message PodSpec {
// EnableServiceLinks indicates whether information about services should be injected into pod's // EnableServiceLinks indicates whether information about services should be injected into pod's
// environment variables, matching the syntax of Docker links. // environment variables, matching the syntax of Docker links.
// Optional: Defaults to true.
// +optional // +optional
optional bool enableServiceLinks = 30; optional bool enableServiceLinks = 30;
} }
......
...@@ -2918,6 +2918,7 @@ type PodSpec struct { ...@@ -2918,6 +2918,7 @@ type PodSpec struct {
RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"` RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"`
// EnableServiceLinks indicates whether information about services should be injected into pod's // EnableServiceLinks indicates whether information about services should be injected into pod's
// environment variables, matching the syntax of Docker links. // environment variables, matching the syntax of Docker links.
// Optional: Defaults to true.
// +optional // +optional
EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"` EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"`
} }
......
...@@ -341,6 +341,17 @@ func NewTooManyRequestsError(message string) *StatusError { ...@@ -341,6 +341,17 @@ func NewTooManyRequestsError(message string) *StatusError {
}} }}
} }
// NewRequestEntityTooLargeError returns an error indicating that the request
// entity was too large.
func NewRequestEntityTooLargeError(message string) *StatusError {
return &StatusError{metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusRequestEntityTooLarge,
Reason: metav1.StatusReasonRequestEntityTooLarge,
Message: fmt.Sprintf("Request entity too large: %s", message),
}}
}
// NewGenericServerResponse returns a new error for server responses that are not in a recognizable form. // NewGenericServerResponse returns a new error for server responses that are not in a recognizable form.
func NewGenericServerResponse(code int, verb string, qualifiedResource schema.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) *StatusError { func NewGenericServerResponse(code int, verb string, qualifiedResource schema.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) *StatusError {
reason := metav1.StatusReasonUnknown reason := metav1.StatusReasonUnknown
...@@ -527,6 +538,19 @@ func IsTooManyRequests(err error) bool { ...@@ -527,6 +538,19 @@ func IsTooManyRequests(err error) bool {
return false return false
} }
// IsRequestEntityTooLargeError determines if err is an error which indicates
// the request entity is too large.
func IsRequestEntityTooLargeError(err error) bool {
if ReasonForError(err) == metav1.StatusReasonRequestEntityTooLarge {
return true
}
switch t := err.(type) {
case APIStatus:
return t.Status().Code == http.StatusRequestEntityTooLarge
}
return false
}
// IsUnexpectedServerError returns true if the server response was not in the expected API format, // IsUnexpectedServerError returns true if the server response was not in the expected API format,
// and may be the result of another HTTP actor. // and may be the result of another HTTP actor.
func IsUnexpectedServerError(err error) bool { func IsUnexpectedServerError(err error) bool {
......
...@@ -713,6 +713,10 @@ const ( ...@@ -713,6 +713,10 @@ const (
// Status code 406 // Status code 406
StatusReasonNotAcceptable StatusReason = "NotAcceptable" StatusReasonNotAcceptable StatusReason = "NotAcceptable"
// StatusReasonRequestEntityTooLarge means that the request entity is too large.
// Status code 413
StatusReasonRequestEntityTooLarge StatusReason = "RequestEntityTooLarge"
// StatusReasonUnsupportedMediaType means that the content type sent by the client is not acceptable // StatusReasonUnsupportedMediaType means that the content type sent by the client is not acceptable
// to the server - for instance, attempting to send protobuf for a resource that supports only json and yaml. // to the server - for instance, attempting to send protobuf for a resource that supports only json and yaml.
// API calls that return UnsupportedMediaType can never succeed. // API calls that return UnsupportedMediaType can never succeed.
......
...@@ -9,6 +9,7 @@ load( ...@@ -9,6 +9,7 @@ load(
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = [ srcs = [
"codec_test.go",
"conversion_test.go", "conversion_test.go",
"converter_test.go", "converter_test.go",
"embedded_test.go", "embedded_test.go",
......
...@@ -283,6 +283,7 @@ var _ GroupVersioner = multiGroupVersioner{} ...@@ -283,6 +283,7 @@ var _ GroupVersioner = multiGroupVersioner{}
type multiGroupVersioner struct { type multiGroupVersioner struct {
target schema.GroupVersion target schema.GroupVersion
acceptedGroupKinds []schema.GroupKind acceptedGroupKinds []schema.GroupKind
coerce bool
} }
// NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds. // NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds.
...@@ -294,6 +295,22 @@ func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKi ...@@ -294,6 +295,22 @@ func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKi
return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds} return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds}
} }
// NewCoercingMultiGroupVersioner returns the provided group version for any incoming kind.
// Incoming kinds that match the provided groupKinds are preferred.
// Kind may be empty in the provided group kind, in which case any kind will match.
// Examples:
// gv=mygroup/__internal, groupKinds=mygroup/Foo, anothergroup/Bar
// KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group/kind)
//
// gv=mygroup/__internal, groupKinds=mygroup, anothergroup
// KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group)
//
// gv=mygroup/__internal, groupKinds=mygroup, anothergroup
// KindForGroupVersionKinds(yetanother/v1/Baz, yetanother/v1/Bar) -> mygroup/__internal/Baz (no preferred group/kind match, uses first kind in list)
func NewCoercingMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner {
return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds, coerce: true}
}
// KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will // KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will
// use the originating kind where possible. // use the originating kind where possible.
func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) { func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
...@@ -308,5 +325,8 @@ func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersio ...@@ -308,5 +325,8 @@ func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersio
return v.target.WithKind(src.Kind), true return v.target.WithKind(src.Kind), true
} }
} }
if v.coerce && len(kinds) > 0 {
return v.target.WithKind(kinds[0].Kind), true
}
return schema.GroupVersionKind{}, false return schema.GroupVersionKind{}, false
} }
...@@ -79,6 +79,10 @@ type APIGroupVersion struct { ...@@ -79,6 +79,10 @@ type APIGroupVersion struct {
Admit admission.Interface Admit admission.Interface
MinRequestTimeout time.Duration MinRequestTimeout time.Duration
// The limit on the request body size that would be accepted and decoded in a write request.
// 0 means no limit.
MaxRequestBodyBytes int64
} }
// InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container. // InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container.
...@@ -87,9 +91,9 @@ type APIGroupVersion struct { ...@@ -87,9 +91,9 @@ type APIGroupVersion struct {
func (g *APIGroupVersion) InstallREST(container *restful.Container) error { func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version) prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version)
installer := &APIInstaller{ installer := &APIInstaller{
group: g, group: g,
prefix: prefix, prefix: prefix,
minRequestTimeout: g.MinRequestTimeout, minRequestTimeout: g.MinRequestTimeout,
} }
apiResources, ws, registrationErrors := installer.Install() apiResources, ws, registrationErrors := installer.Install()
......
...@@ -79,7 +79,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte ...@@ -79,7 +79,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte
} }
decoder := scope.Serializer.DecoderToVersion(s.Serializer, scope.HubGroupVersion) decoder := scope.Serializer.DecoderToVersion(s.Serializer, scope.HubGroupVersion)
body, err := readBody(req) body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
......
...@@ -66,7 +66,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco ...@@ -66,7 +66,7 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco
options := &metav1.DeleteOptions{} options := &metav1.DeleteOptions{}
if allowsOptions { if allowsOptions {
body, err := readBody(req) body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
...@@ -227,7 +227,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco ...@@ -227,7 +227,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
options := &metav1.DeleteOptions{} options := &metav1.DeleteOptions{}
if checkBody { if checkBody {
body, err := readBody(req) body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
......
...@@ -47,6 +47,11 @@ import ( ...@@ -47,6 +47,11 @@ import (
utiltrace "k8s.io/apiserver/pkg/util/trace" utiltrace "k8s.io/apiserver/pkg/util/trace"
) )
const (
// maximum number of operations a single json patch may contain.
maxJSONPatchOperations = 10000
)
// PatchResource returns a function that will handle a resource patch. // PatchResource returns a function that will handle a resource patch.
func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc { func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface, patchTypes []string) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
...@@ -88,7 +93,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface ...@@ -88,7 +93,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
patchJS, err := readBody(req) patchJS, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
...@@ -287,6 +292,11 @@ func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr ...@@ -287,6 +292,11 @@ func (p *jsonPatcher) applyJSPatch(versionedJS []byte) (patchedJS []byte, retErr
if err != nil { if err != nil {
return nil, errors.NewBadRequest(err.Error()) return nil, errors.NewBadRequest(err.Error())
} }
if len(patchObj) > maxJSONPatchOperations {
return nil, errors.NewRequestEntityTooLargeError(
fmt.Sprintf("The allowed maximum operations in a JSON patch is %d, got %d",
maxJSONPatchOperations, len(patchObj)))
}
patchedJS, err := patchObj.Apply(versionedJS) patchedJS, err := patchObj.Apply(versionedJS)
if err != nil { if err != nil {
return nil, errors.NewGenericServerResponse(http.StatusUnprocessableEntity, "", schema.GroupResource{}, "", err.Error(), 0, false) return nil, errors.NewGenericServerResponse(http.StatusUnprocessableEntity, "", schema.GroupResource{}, "", err.Error(), 0, false)
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"context" "context"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
...@@ -69,6 +70,8 @@ type RequestScope struct { ...@@ -69,6 +70,8 @@ type RequestScope struct {
// HubGroupVersion indicates what version objects read from etcd or incoming requests should be converted to for in-memory handling. // HubGroupVersion indicates what version objects read from etcd or incoming requests should be converted to for in-memory handling.
HubGroupVersion schema.GroupVersion HubGroupVersion schema.GroupVersion
MaxRequestBodyBytes int64
} }
func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Request) { func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Request) {
...@@ -322,9 +325,23 @@ func summarizeData(data []byte, maxLength int) string { ...@@ -322,9 +325,23 @@ func summarizeData(data []byte, maxLength int) string {
} }
} }
func readBody(req *http.Request) ([]byte, error) { func limitedReadBody(req *http.Request, limit int64) ([]byte, error) {
defer req.Body.Close() defer req.Body.Close()
return ioutil.ReadAll(req.Body) if limit <= 0 {
return ioutil.ReadAll(req.Body)
}
lr := &io.LimitedReader{
R: req.Body,
N: limit + 1,
}
data, err := ioutil.ReadAll(lr)
if err != nil {
return nil, err
}
if lr.N <= 0 {
return nil, errors.NewRequestEntityTooLargeError(fmt.Sprintf("limit is %d", limit))
}
return data, nil
} }
func parseTimeout(str string) time.Duration { func parseTimeout(str string) time.Duration {
......
...@@ -64,7 +64,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac ...@@ -64,7 +64,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac
ctx := req.Context() ctx := req.Context()
ctx = request.WithNamespace(ctx, namespace) ctx = request.WithNamespace(ctx, namespace)
body, err := readBody(req) body, err := limitedReadBody(req, scope.MaxRequestBodyBytes)
if err != nil { if err != nil {
scope.err(err, w, req) scope.err(err, w, req)
return return
......
...@@ -503,6 +503,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag ...@@ -503,6 +503,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
HubGroupVersion: schema.GroupVersion{Group: fqKindToRegister.Group, Version: runtime.APIVersionInternal}, HubGroupVersion: schema.GroupVersion{Group: fqKindToRegister.Group, Version: runtime.APIVersionInternal},
MetaGroupVersion: metav1.SchemeGroupVersion, MetaGroupVersion: metav1.SchemeGroupVersion,
MaxRequestBodyBytes: a.group.MaxRequestBodyBytes,
} }
if a.group.MetaGroupVersion != nil { if a.group.MetaGroupVersion != nil {
reqScope.MetaGroupVersion = *a.group.MetaGroupVersion reqScope.MetaGroupVersion = *a.group.MetaGroupVersion
......
...@@ -110,6 +110,7 @@ go_library( ...@@ -110,6 +110,7 @@ go_library(
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library", "//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library",
"//vendor/github.com/evanphx/json-patch:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/pborman/uuid:go_default_library", "//vendor/github.com/pborman/uuid:go_default_library",
"//vendor/golang.org/x/net/http2:go_default_library", "//vendor/golang.org/x/net/http2:go_default_library",
......
...@@ -25,8 +25,10 @@ import ( ...@@ -25,8 +25,10 @@ import (
goruntime "runtime" goruntime "runtime"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/pborman/uuid" "github.com/pborman/uuid"
"k8s.io/klog" "k8s.io/klog"
...@@ -143,6 +145,13 @@ type Config struct { ...@@ -143,6 +145,13 @@ type Config struct {
// If specified, long running requests such as watch will be allocated a random timeout between this value, and // If specified, long running requests such as watch will be allocated a random timeout between this value, and
// twice this value. Note that it is up to the request handlers to ignore or honor this timeout. In seconds. // twice this value. Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
MinRequestTimeout int MinRequestTimeout int
// The limit on the total size increase all "copy" operations in a json
// patch may cause.
// This affects all places that applies json patch in the binary.
JSONPatchMaxCopyBytes int64
// The limit on the request body size that would be accepted and decoded in a write request.
// 0 means no limit.
MaxRequestBodyBytes int64
// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further // MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
// request has to wait. Applies only to non-mutating requests. // request has to wait. Applies only to non-mutating requests.
MaxRequestsInFlight int MaxRequestsInFlight int
...@@ -231,20 +240,36 @@ type AuthorizationInfo struct { ...@@ -231,20 +240,36 @@ type AuthorizationInfo struct {
// NewConfig returns a Config struct with the default values // NewConfig returns a Config struct with the default values
func NewConfig(codecs serializer.CodecFactory) *Config { func NewConfig(codecs serializer.CodecFactory) *Config {
return &Config{ return &Config{
Serializer: codecs, Serializer: codecs,
BuildHandlerChainFunc: DefaultBuildHandlerChain, BuildHandlerChainFunc: DefaultBuildHandlerChain,
HandlerChainWaitGroup: new(utilwaitgroup.SafeWaitGroup), HandlerChainWaitGroup: new(utilwaitgroup.SafeWaitGroup),
LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix), LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix),
DisabledPostStartHooks: sets.NewString(), DisabledPostStartHooks: sets.NewString(),
HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz, healthz.LogHealthz}, HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz, healthz.LogHealthz},
EnableIndex: true, EnableIndex: true,
EnableDiscovery: true, EnableDiscovery: true,
EnableProfiling: true, EnableProfiling: true,
EnableMetrics: true, EnableMetrics: true,
MaxRequestsInFlight: 400, MaxRequestsInFlight: 400,
MaxMutatingRequestsInFlight: 200, MaxMutatingRequestsInFlight: 200,
RequestTimeout: time.Duration(60) * time.Second, RequestTimeout: time.Duration(60) * time.Second,
MinRequestTimeout: 1800, MinRequestTimeout: 1800,
// 10MB is the recommended maximum client request size in bytes
// the etcd server should accept. See
// https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/server.go#L90.
// A request body might be encoded in json, and is converted to
// proto when persisted in etcd. Assuming the upper bound of
// the size ratio is 10:1, we set 100MB as the largest size
// increase the "copy" operations in a json patch may cause.
JSONPatchMaxCopyBytes: int64(100 * 1024 * 1024),
// 10MB is the recommended maximum client request size in bytes
// the etcd server should accept. See
// https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/server.go#L90.
// A request body might be encoded in json, and is converted to
// proto when persisted in etcd. Assuming the upper bound of
// the size ratio is 10:1, we set 100MB as the largest request
// body size to be accepted and decoded in a write request.
MaxRequestBodyBytes: int64(100 * 1024 * 1024),
// Default to treating watch as a long-running operation // Default to treating watch as a long-running operation
// Generic API servers have no inherent long-running subresources // Generic API servers have no inherent long-running subresources
...@@ -357,6 +382,20 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G ...@@ -357,6 +382,20 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
healthzChecks: c.HealthzChecks, healthzChecks: c.HealthzChecks,
DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer), DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer),
maxRequestBodyBytes: c.MaxRequestBodyBytes,
}
for {
if c.JSONPatchMaxCopyBytes <= 0 {
break
}
existing := atomic.LoadInt64(&jsonpatch.AccumulatedCopySizeLimit)
if existing > 0 && existing < c.JSONPatchMaxCopyBytes {
break
}
if atomic.CompareAndSwapInt64(&jsonpatch.AccumulatedCopySizeLimit, existing, c.JSONPatchMaxCopyBytes) {
break
}
} }
for k, v := range delegationTarget.PostStartHooks() { for k, v := range delegationTarget.PostStartHooks() {
......
...@@ -147,6 +147,10 @@ type GenericAPIServer struct { ...@@ -147,6 +147,10 @@ type GenericAPIServer struct {
// HandlerChainWaitGroup allows you to wait for all chain handlers finish after the server shutdown. // HandlerChainWaitGroup allows you to wait for all chain handlers finish after the server shutdown.
HandlerChainWaitGroup *utilwaitgroup.SafeWaitGroup HandlerChainWaitGroup *utilwaitgroup.SafeWaitGroup
// The limit on the request body size that would be accepted and decoded in a write request.
// 0 means no limit.
maxRequestBodyBytes int64
} }
// DelegationTarget is an interface which allows for composition of API servers with top level handling that works // DelegationTarget is an interface which allows for composition of API servers with top level handling that works
...@@ -317,6 +321,7 @@ func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *A ...@@ -317,6 +321,7 @@ func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *A
if apiGroupInfo.OptionsExternalVersion != nil { if apiGroupInfo.OptionsExternalVersion != nil {
apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion
} }
apiGroupVersion.MaxRequestBodyBytes = s.maxRequestBodyBytes
if err := apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer); err != nil { if err := apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer); err != nil {
return fmt.Errorf("unable to setup API %v: %v", apiGroupInfo, err) return fmt.Errorf("unable to setup API %v: %v", apiGroupInfo, err)
......
...@@ -42,7 +42,15 @@ type ServerRunOptions struct { ...@@ -42,7 +42,15 @@ type ServerRunOptions struct {
MaxMutatingRequestsInFlight int MaxMutatingRequestsInFlight int
RequestTimeout time.Duration RequestTimeout time.Duration
MinRequestTimeout int MinRequestTimeout int
TargetRAMMB int // We intentionally did not add a flag for this option. Users of the
// apiserver library can wire it to a flag.
JSONPatchMaxCopyBytes int64
// The limit on the request body size that would be accepted and
// decoded in a write request. 0 means no limit.
// We intentionally did not add a flag for this option. Users of the
// apiserver library can wire it to a flag.
MaxRequestBodyBytes int64
TargetRAMMB int
} }
func NewServerRunOptions() *ServerRunOptions { func NewServerRunOptions() *ServerRunOptions {
...@@ -52,6 +60,8 @@ func NewServerRunOptions() *ServerRunOptions { ...@@ -52,6 +60,8 @@ func NewServerRunOptions() *ServerRunOptions {
MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight, MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
RequestTimeout: defaults.RequestTimeout, RequestTimeout: defaults.RequestTimeout,
MinRequestTimeout: defaults.MinRequestTimeout, MinRequestTimeout: defaults.MinRequestTimeout,
JSONPatchMaxCopyBytes: defaults.JSONPatchMaxCopyBytes,
MaxRequestBodyBytes: defaults.MaxRequestBodyBytes,
} }
} }
...@@ -63,6 +73,8 @@ func (s *ServerRunOptions) ApplyTo(c *server.Config) error { ...@@ -63,6 +73,8 @@ func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
c.RequestTimeout = s.RequestTimeout c.RequestTimeout = s.RequestTimeout
c.MinRequestTimeout = s.MinRequestTimeout c.MinRequestTimeout = s.MinRequestTimeout
c.JSONPatchMaxCopyBytes = s.JSONPatchMaxCopyBytes
c.MaxRequestBodyBytes = s.MaxRequestBodyBytes
c.PublicAddress = s.AdvertiseAddress c.PublicAddress = s.AdvertiseAddress
return nil return nil
...@@ -107,10 +119,18 @@ func (s *ServerRunOptions) Validate() []error { ...@@ -107,10 +119,18 @@ func (s *ServerRunOptions) Validate() []error {
errors = append(errors, fmt.Errorf("--min-request-timeout can not be negative value")) errors = append(errors, fmt.Errorf("--min-request-timeout can not be negative value"))
} }
if s.JSONPatchMaxCopyBytes < 0 {
errors = append(errors, fmt.Errorf("--json-patch-max-copy-bytes can not be negative value"))
}
if s.MaxRequestBodyBytes < 0 {
errors = append(errors, fmt.Errorf("--max-resource-write-bytes can not be negative value"))
}
return errors return errors
} }
// AddFlags adds flags for a specific APIServer to the specified FlagSet // AddUniversalFlags adds flags for a specific APIServer to the specified FlagSet
func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) { func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to // Note: the weird ""+ in below lines seems to be the only way to get gofmt to
// arrange these text blocks sensibly. Grrr. // arrange these text blocks sensibly. Grrr.
......
...@@ -85,7 +85,7 @@ func NewStorageCodec(opts StorageCodecConfig) (runtime.Codec, error) { ...@@ -85,7 +85,7 @@ func NewStorageCodec(opts StorageCodecConfig) (runtime.Codec, error) {
) )
decoder := opts.StorageSerializer.DecoderToVersion( decoder := opts.StorageSerializer.DecoderToVersion(
recognizer.NewDecoder(decoders...), recognizer.NewDecoder(decoders...),
runtime.NewMultiGroupVersioner( runtime.NewCoercingMultiGroupVersioner(
opts.MemoryVersion, opts.MemoryVersion,
schema.GroupKind{Group: opts.MemoryVersion.Group}, schema.GroupKind{Group: opts.MemoryVersion.Group},
schema.GroupKind{Group: opts.StorageVersion.Group}, schema.GroupKind{Group: opts.StorageVersion.Group},
......
...@@ -335,6 +335,13 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string, ...@@ -335,6 +335,13 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
chanSize = 1000 chanSize = 1000
} }
// With some events already sent, update resourceVersion so that
// events that were buffered and not yet processed won't be delivered
// to this watcher second time causing going back in time.
if len(initEvents) > 0 {
watchRV = initEvents[len(initEvents)-1].ResourceVersion
}
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
forget := forgetWatcher(c, c.watcherIdx, triggerValue, triggerSupported) forget := forgetWatcher(c, c.watcherIdx, triggerValue, triggerSupported)
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "13" gitMinor = "13"
gitVersion = "v1.13.3-k3s.7" gitVersion = "v1.13.4-k3s.1"
gitCommit = "96a2a74e26d1bfd15c1e0cd6bd4721af975f7a84" gitCommit = "7f72ee72d6dce73e2d36dbd3402413d146fad01e"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2019-03-01T04:28+00:00Z" buildDate = "2019-03-04T01:05+00:00Z"
) )
...@@ -110,6 +110,8 @@ type Options struct { ...@@ -110,6 +110,8 @@ type Options struct {
master string master string
// healthzPort is the port to be used by the healthz server. // healthzPort is the port to be used by the healthz server.
healthzPort int32 healthzPort int32
// metricsPort is the port to be used by the metrics server.
metricsPort int32
scheme *runtime.Scheme scheme *runtime.Scheme
codecs serializer.CodecFactory codecs serializer.CodecFactory
...@@ -133,8 +135,9 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) { ...@@ -133,8 +135,9 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.Var(utilflag.IPVar{Val: &o.config.BindAddress}, "bind-address", "The IP address for the proxy server to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)") fs.Var(utilflag.IPVar{Val: &o.config.BindAddress}, "bind-address", "The IP address for the proxy server to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)")
fs.StringVar(&o.master, "master", o.master, "The address of the Kubernetes API server (overrides any value in kubeconfig)") fs.StringVar(&o.master, "master", o.master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
fs.Int32Var(&o.healthzPort, "healthz-port", o.healthzPort, "The port to bind the health check server. Use 0 to disable.") fs.Int32Var(&o.healthzPort, "healthz-port", o.healthzPort, "The port to bind the health check server. Use 0 to disable.")
fs.Var(utilflag.IPVar{Val: &o.config.HealthzBindAddress}, "healthz-bind-address", "The IP address and port for the health check server to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)") fs.Var(utilflag.IPVar{Val: &o.config.HealthzBindAddress}, "healthz-bind-address", "The IP address for the health check server to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)")
fs.Var(utilflag.IPVar{Val: &o.config.MetricsBindAddress}, "metrics-bind-address", "The IP address and port for the metrics server to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)") fs.Int32Var(&o.metricsPort, "metrics-port", o.metricsPort, "The port to bind the metrics server. Use 0 to disable.")
fs.Var(utilflag.IPVar{Val: &o.config.MetricsBindAddress}, "metrics-bind-address", "The IP address for the metrics server to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)")
fs.Int32Var(o.config.OOMScoreAdj, "oom-score-adj", utilpointer.Int32PtrDerefOr(o.config.OOMScoreAdj, int32(qos.KubeProxyOOMScoreAdj)), "The oom-score-adj value for kube-proxy process. Values must be within the range [-1000, 1000]") fs.Int32Var(o.config.OOMScoreAdj, "oom-score-adj", utilpointer.Int32PtrDerefOr(o.config.OOMScoreAdj, int32(qos.KubeProxyOOMScoreAdj)), "The oom-score-adj value for kube-proxy process. Values must be within the range [-1000, 1000]")
fs.StringVar(&o.config.ResourceContainer, "resource-container", o.config.ResourceContainer, "Absolute name of the resource-only container to create and run the Kube-proxy in (Default: /kube-proxy).") fs.StringVar(&o.config.ResourceContainer, "resource-container", o.config.ResourceContainer, "Absolute name of the resource-only container to create and run the Kube-proxy in (Default: /kube-proxy).")
fs.MarkDeprecated("resource-container", "This feature will be removed in a later release.") fs.MarkDeprecated("resource-container", "This feature will be removed in a later release.")
...@@ -182,6 +185,7 @@ func NewOptions() *Options { ...@@ -182,6 +185,7 @@ func NewOptions() *Options {
return &Options{ return &Options{
config: new(kubeproxyconfig.KubeProxyConfiguration), config: new(kubeproxyconfig.KubeProxyConfiguration),
healthzPort: ports.ProxyHealthzPort, healthzPort: ports.ProxyHealthzPort,
metricsPort: ports.ProxyStatusPort,
scheme: scheme.Scheme, scheme: scheme.Scheme,
codecs: scheme.Codecs, codecs: scheme.Codecs,
CleanupIPVS: true, CleanupIPVS: true,
...@@ -193,6 +197,7 @@ func (o *Options) Complete() error { ...@@ -193,6 +197,7 @@ func (o *Options) Complete() error {
if len(o.ConfigFile) == 0 && len(o.WriteConfigTo) == 0 { if len(o.ConfigFile) == 0 && len(o.WriteConfigTo) == 0 {
klog.Warning("WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.") klog.Warning("WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.")
o.applyDeprecatedHealthzPortToConfig() o.applyDeprecatedHealthzPortToConfig()
o.applyDeprecatedMetricsPortToConfig()
} }
// Load the config file here in Complete, so that Validate validates the fully-resolved config. // Load the config file here in Complete, so that Validate validates the fully-resolved config.
...@@ -305,6 +310,20 @@ func (o *Options) applyDeprecatedHealthzPortToConfig() { ...@@ -305,6 +310,20 @@ func (o *Options) applyDeprecatedHealthzPortToConfig() {
o.config.HealthzBindAddress = fmt.Sprintf("%s:%d", o.config.HealthzBindAddress, o.healthzPort) o.config.HealthzBindAddress = fmt.Sprintf("%s:%d", o.config.HealthzBindAddress, o.healthzPort)
} }
func (o *Options) applyDeprecatedMetricsPortToConfig() {
if o.metricsPort == 0 {
o.config.MetricsBindAddress = ""
return
}
index := strings.Index(o.config.MetricsBindAddress, ":")
if index != -1 {
o.config.MetricsBindAddress = o.config.MetricsBindAddress[0:index]
}
o.config.MetricsBindAddress = fmt.Sprintf("%s:%d", o.config.MetricsBindAddress, o.metricsPort)
}
// loadConfigFromFile loads the contents of file and decodes it as a // loadConfigFromFile loads the contents of file and decodes it as a
// KubeProxyConfiguration object. // KubeProxyConfiguration object.
func (o *Options) loadConfigFromFile(file string) (*kubeproxyconfig.KubeProxyConfiguration, error) { func (o *Options) loadConfigFromFile(file string) (*kubeproxyconfig.KubeProxyConfiguration, error) {
......
...@@ -19,10 +19,14 @@ limitations under the License. ...@@ -19,10 +19,14 @@ limitations under the License.
package options package options
import ( import (
"flag"
"os"
"github.com/spf13/pflag" "github.com/spf13/pflag"
// ensure libs have a chance to globally register their flags // ensure libs have a chance to globally register their flags
_ "github.com/google/cadvisor/container/common" _ "github.com/google/cadvisor/container/common"
_ "github.com/google/cadvisor/container/containerd"
_ "github.com/google/cadvisor/container/docker" _ "github.com/google/cadvisor/container/docker"
_ "github.com/google/cadvisor/container/raw" _ "github.com/google/cadvisor/container/raw"
_ "github.com/google/cadvisor/machine" _ "github.com/google/cadvisor/machine"
...@@ -32,4 +36,44 @@ import ( ...@@ -32,4 +36,44 @@ import (
// addCadvisorFlags adds flags from cadvisor // addCadvisorFlags adds flags from cadvisor
func addCadvisorFlags(fs *pflag.FlagSet) { func addCadvisorFlags(fs *pflag.FlagSet) {
// lookup flags in global flag set and re-register the values with our flagset
global := flag.CommandLine
local := pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
// These flags were also implicit from cadvisor, but are actually used by something in the core repo:
// TODO(mtaufen): This one is stil used by our salt, but for heaven's sake it's even deprecated in cadvisor
register(global, local, "docker_root")
// e2e node tests rely on this
register(global, local, "housekeeping_interval")
// These flags were implicit from cadvisor, and are mistakes that should be registered deprecated:
const deprecated = "This is a cadvisor flag that was mistakenly registered with the Kubelet. Due to legacy concerns, it will follow the standard CLI deprecation timeline before being removed."
registerDeprecated(global, local, "application_metrics_count_limit", deprecated)
registerDeprecated(global, local, "boot_id_file", deprecated)
registerDeprecated(global, local, "container_hints", deprecated)
registerDeprecated(global, local, "containerd", deprecated)
registerDeprecated(global, local, "docker", deprecated)
registerDeprecated(global, local, "docker_env_metadata_whitelist", deprecated)
registerDeprecated(global, local, "docker_only", deprecated)
registerDeprecated(global, local, "docker-tls", deprecated)
registerDeprecated(global, local, "docker-tls-ca", deprecated)
registerDeprecated(global, local, "docker-tls-cert", deprecated)
registerDeprecated(global, local, "docker-tls-key", deprecated)
registerDeprecated(global, local, "enable_load_reader", deprecated)
registerDeprecated(global, local, "event_storage_age_limit", deprecated)
registerDeprecated(global, local, "event_storage_event_limit", deprecated)
registerDeprecated(global, local, "global_housekeeping_interval", deprecated)
registerDeprecated(global, local, "log_cadvisor_usage", deprecated)
registerDeprecated(global, local, "machine_id_file", deprecated)
registerDeprecated(global, local, "storage_driver_user", deprecated)
registerDeprecated(global, local, "storage_driver_password", deprecated)
registerDeprecated(global, local, "storage_driver_host", deprecated)
registerDeprecated(global, local, "storage_driver_db", deprecated)
registerDeprecated(global, local, "storage_driver_table", deprecated)
registerDeprecated(global, local, "storage_driver_secure", deprecated)
registerDeprecated(global, local, "storage_driver_buffer_duration", deprecated)
// finally, add cadvisor flags to the provided flagset
fs.AddFlagSet(local)
} }
...@@ -12,7 +12,7 @@ package=k8s.io/kubernetes/cmd/hyperkube ...@@ -12,7 +12,7 @@ package=k8s.io/kubernetes/cmd/hyperkube
$(cat ./Godeps/Godeps.json | jq -r '(.Deps | .[] | "\(.ImportPath) \(.Comment) \(.Rev)\n")' | sed 's/null//' | awk '{print $1 " " $2}' | grep -Ev 'github.com/opencontainers/runc|bitbucket.org/ww/goautoneg|github.com/google/cadvisor' | sort -k2,1 | uniq -f1) $(cat ./Godeps/Godeps.json | jq -r '(.Deps | .[] | "\(.ImportPath) \(.Comment) \(.Rev)\n")' | sed 's/null//' | awk '{print $1 " " $2}' | grep -Ev 'github.com/opencontainers/runc|bitbucket.org/ww/goautoneg|github.com/google/cadvisor' | sort -k2,1 | uniq -f1)
bitbucket.org/ww/goautoneg a547fc61f48d567d5b4ec6f8aee5573d8efce11d https://github.com/rancher/goautoneg.git bitbucket.org/ww/goautoneg a547fc61f48d567d5b4ec6f8aee5573d8efce11d https://github.com/rancher/goautoneg.git
github.com/ibuildthecloud/kvsql c649f12fe5250718e4e024b8b40e7de796ab095e github.com/ibuildthecloud/kvsql c649f12fe5250718e4e024b8b40e7de796ab095e
github.com/google/cadvisor 91dab6eb91496ed68acbef68b02b34b3392ca754 https://github.com/ibuildthecloud/cadvisor.git github.com/google/cadvisor 87e237ff35b9d752ba58860a06e0ebe57816cbb7 https://github.com/ibuildthecloud/cadvisor.git
github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb
EOF EOF
......
...@@ -72943,7 +72943,7 @@ ...@@ -72943,7 +72943,7 @@
"type": "string" "type": "string"
}, },
"enableServiceLinks": { "enableServiceLinks": {
"description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.", "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.",
"type": "boolean" "type": "boolean"
}, },
"hostAliases": { "hostAliases": {
...@@ -966,6 +966,12 @@ func (dsc *DaemonSetsController) manage(ds *apps.DaemonSet, hash string) error { ...@@ -966,6 +966,12 @@ func (dsc *DaemonSetsController) manage(ds *apps.DaemonSet, hash string) error {
failedPodsObserved += failedPodsObservedOnNode failedPodsObserved += failedPodsObservedOnNode
} }
// Remove pods assigned to not existing nodes when daemonset pods are scheduled by default scheduler.
// If node doesn't exist then pods are never scheduled and can't be deleted by PodGCController.
if utilfeature.DefaultFeatureGate.Enabled(features.ScheduleDaemonSetPods) {
podsToDelete = append(podsToDelete, getPodsWithoutNode(nodeList, nodeToDaemonPods)...)
}
// Label new pods using the hash label value of the current history when creating them // Label new pods using the hash label value of the current history when creating them
if err = dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods, hash); err != nil { if err = dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods, hash); err != nil {
return err return err
...@@ -1535,3 +1541,20 @@ func isControlledByDaemonSet(p *v1.Pod, uuid types.UID) bool { ...@@ -1535,3 +1541,20 @@ func isControlledByDaemonSet(p *v1.Pod, uuid types.UID) bool {
func failedPodsBackoffKey(ds *apps.DaemonSet, nodeName string) string { func failedPodsBackoffKey(ds *apps.DaemonSet, nodeName string) string {
return fmt.Sprintf("%s/%d/%s", ds.UID, ds.Status.ObservedGeneration, nodeName) return fmt.Sprintf("%s/%d/%s", ds.UID, ds.Status.ObservedGeneration, nodeName)
} }
// getPodsWithoutNode returns list of pods assigned to not existing nodes.
func getPodsWithoutNode(runningNodesList []*v1.Node, nodeToDaemonPods map[string][]*v1.Pod) []string {
var results []string
isNodeRunning := make(map[string]bool)
for _, node := range runningNodesList {
isNodeRunning[node.Name] = true
}
for n, pods := range nodeToDaemonPods {
if !isNodeRunning[n] {
for _, pod := range pods {
results = append(results, pod.Name)
}
}
}
return results
}
...@@ -460,6 +460,7 @@ func (kl *Kubelet) defaultNodeStatusFuncs() []func(*v1.Node) error { ...@@ -460,6 +460,7 @@ func (kl *Kubelet) defaultNodeStatusFuncs() []func(*v1.Node) error {
nodestatus.PIDPressureCondition(kl.clock.Now, kl.evictionManager.IsUnderPIDPressure, kl.recordNodeStatusEvent), nodestatus.PIDPressureCondition(kl.clock.Now, kl.evictionManager.IsUnderPIDPressure, kl.recordNodeStatusEvent),
nodestatus.ReadyCondition(kl.clock.Now, kl.runtimeState.runtimeErrors, kl.runtimeState.networkErrors, validateHostFunc, kl.containerManager.Status, kl.recordNodeStatusEvent), nodestatus.ReadyCondition(kl.clock.Now, kl.runtimeState.runtimeErrors, kl.runtimeState.networkErrors, validateHostFunc, kl.containerManager.Status, kl.recordNodeStatusEvent),
nodestatus.VolumesInUse(kl.volumeManager.ReconcilerStatesHasBeenSynced, kl.volumeManager.GetVolumesInUse), nodestatus.VolumesInUse(kl.volumeManager.ReconcilerStatesHasBeenSynced, kl.volumeManager.GetVolumesInUse),
nodestatus.RemoveOutOfDiskCondition(),
// TODO(mtaufen): I decided not to move this setter for now, since all it does is send an event // TODO(mtaufen): I decided not to move this setter for now, since all it does is send an event
// and record state back to the Kubelet runtime object. In the future, I'd like to isolate // and record state back to the Kubelet runtime object. In the future, I'd like to isolate
// these side-effects by decoupling the decisions to send events and partial status recording // these side-effects by decoupling the decisions to send events and partial status recording
......
...@@ -647,3 +647,18 @@ func VolumeLimits(volumePluginListFunc func() []volume.VolumePluginWithAttachLim ...@@ -647,3 +647,18 @@ func VolumeLimits(volumePluginListFunc func() []volume.VolumePluginWithAttachLim
return nil return nil
} }
} }
// RemoveOutOfDiskCondition removes stale OutOfDisk condition
// OutOfDisk condition has been removed from kubelet in 1.12
func RemoveOutOfDiskCondition() Setter {
return func(node *v1.Node) error {
var conditions []v1.NodeCondition
for i := range node.Status.Conditions {
if node.Status.Conditions[i].Type != v1.NodeOutOfDisk {
conditions = append(conditions, node.Status.Conditions[i])
}
}
node.Status.Conditions = conditions
return nil
}
}
...@@ -1267,6 +1267,7 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue ...@@ -1267,6 +1267,7 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue
} }
backoff.Gc() backoff.Gc()
podSchedulingCycle := podQueue.SchedulingCycle()
// Retry asynchronously. // Retry asynchronously.
// Note that this is extremely rudimentary and we need a more real error handling path. // Note that this is extremely rudimentary and we need a more real error handling path.
go func() { go func() {
...@@ -1294,7 +1295,7 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue ...@@ -1294,7 +1295,7 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue
pod, err := c.client.CoreV1().Pods(podID.Namespace).Get(podID.Name, metav1.GetOptions{}) pod, err := c.client.CoreV1().Pods(podID.Namespace).Get(podID.Name, metav1.GetOptions{})
if err == nil { if err == nil {
if len(pod.Spec.NodeName) == 0 { if len(pod.Spec.NodeName) == 0 {
podQueue.AddUnschedulableIfNotPresent(pod) podQueue.AddUnschedulableIfNotPresent(pod, podSchedulingCycle)
} else { } else {
if c.volumeBinder != nil { if c.volumeBinder != nil {
// Volume binder only wants to keep unassigned pods // Volume binder only wants to keep unassigned pods
......
...@@ -60,7 +60,14 @@ const unschedulableQTimeInterval = 60 * time.Second ...@@ -60,7 +60,14 @@ const unschedulableQTimeInterval = 60 * time.Second
type SchedulingQueue interface { type SchedulingQueue interface {
Add(pod *v1.Pod) error Add(pod *v1.Pod) error
AddIfNotPresent(pod *v1.Pod) error AddIfNotPresent(pod *v1.Pod) error
AddUnschedulableIfNotPresent(pod *v1.Pod) error // AddUnschedulableIfNotPresent adds an unschedulable pod back to scheduling queue.
// The podSchedulingCycle represents the current scheduling cycle number which can be
// returned by calling SchedulingCycle().
AddUnschedulableIfNotPresent(pod *v1.Pod, podSchedulingCycle int64) error
// SchedulingCycle returns the current number of scheduling cycle which is
// cached by scheduling queue. Normally, incrementing this number whenever
// a pod is popped (e.g. called Pop()) is enough.
SchedulingCycle() int64
// Pop removes the head of the queue and returns it. It blocks if the // Pop removes the head of the queue and returns it. It blocks if the
// queue is empty and waits until a new item is added to the queue. // queue is empty and waits until a new item is added to the queue.
Pop() (*v1.Pod, error) Pop() (*v1.Pod, error)
...@@ -112,10 +119,15 @@ func (f *FIFO) AddIfNotPresent(pod *v1.Pod) error { ...@@ -112,10 +119,15 @@ func (f *FIFO) AddIfNotPresent(pod *v1.Pod) error {
// AddUnschedulableIfNotPresent adds an unschedulable pod back to the queue. In // AddUnschedulableIfNotPresent adds an unschedulable pod back to the queue. In
// FIFO it is added to the end of the queue. // FIFO it is added to the end of the queue.
func (f *FIFO) AddUnschedulableIfNotPresent(pod *v1.Pod) error { func (f *FIFO) AddUnschedulableIfNotPresent(pod *v1.Pod, podSchedulingCycle int64) error {
return f.FIFO.AddIfNotPresent(pod) return f.FIFO.AddIfNotPresent(pod)
} }
// SchedulingCycle implements SchedulingQueue.SchedulingCycle interface.
func (f *FIFO) SchedulingCycle() int64 {
return 0
}
// Update updates a pod in the FIFO. // Update updates a pod in the FIFO.
func (f *FIFO) Update(oldPod, newPod *v1.Pod) error { func (f *FIFO) Update(oldPod, newPod *v1.Pod) error {
return f.FIFO.Update(newPod) return f.FIFO.Update(newPod)
...@@ -212,12 +224,14 @@ type PriorityQueue struct { ...@@ -212,12 +224,14 @@ type PriorityQueue struct {
// nominatedPods is a structures that stores pods which are nominated to run // nominatedPods is a structures that stores pods which are nominated to run
// on nodes. // on nodes.
nominatedPods *nominatedPodMap nominatedPods *nominatedPodMap
// receivedMoveRequest is set to true whenever we receive a request to move a // schedulingCycle represents sequence number of scheduling cycle and is incremented
// pod from the unschedulableQ to the activeQ, and is set to false, when we pop // when a pod is popped.
// a pod from the activeQ. It indicates if we received a move request when a schedulingCycle int64
// pod was in flight (we were trying to schedule it). In such a case, we put // moveRequestCycle caches the sequence number of scheduling cycle when we
// the pod back into the activeQ if it is determined unschedulable. // received a move request. Unscheduable pods in and before this scheduling
receivedMoveRequest bool // cycle will be put back to activeQueue if we were trying to schedule them
// when we received move request.
moveRequestCycle int64
// closed indicates that the queue is closed. // closed indicates that the queue is closed.
// It is mainly used to let Pop() exit its control loop while waiting for an item. // It is mainly used to let Pop() exit its control loop while waiting for an item.
...@@ -254,11 +268,12 @@ func activeQComp(pod1, pod2 interface{}) bool { ...@@ -254,11 +268,12 @@ func activeQComp(pod1, pod2 interface{}) bool {
// NewPriorityQueue creates a PriorityQueue object. // NewPriorityQueue creates a PriorityQueue object.
func NewPriorityQueue(stop <-chan struct{}) *PriorityQueue { func NewPriorityQueue(stop <-chan struct{}) *PriorityQueue {
pq := &PriorityQueue{ pq := &PriorityQueue{
clock: util.RealClock{}, clock: util.RealClock{},
stop: stop, stop: stop,
activeQ: newHeap(cache.MetaNamespaceKeyFunc, activeQComp), activeQ: newHeap(cache.MetaNamespaceKeyFunc, activeQComp),
unschedulableQ: newUnschedulablePodsMap(), unschedulableQ: newUnschedulablePodsMap(),
nominatedPods: newNominatedPodMap(), nominatedPods: newNominatedPodMap(),
moveRequestCycle: -1,
} }
pq.cond.L = &pq.lock pq.cond.L = &pq.lock
...@@ -316,10 +331,19 @@ func isPodUnschedulable(pod *v1.Pod) bool { ...@@ -316,10 +331,19 @@ func isPodUnschedulable(pod *v1.Pod) bool {
return cond != nil && cond.Status == v1.ConditionFalse && cond.Reason == v1.PodReasonUnschedulable return cond != nil && cond.Status == v1.ConditionFalse && cond.Reason == v1.PodReasonUnschedulable
} }
// AddUnschedulableIfNotPresent does nothing if the pod is present in either // SchedulingCycle returns current scheduling cycle.
// queue. Otherwise it adds the pod to the unschedulable queue if func (p *PriorityQueue) SchedulingCycle() int64 {
// p.receivedMoveRequest is false, and to the activeQ if p.receivedMoveRequest is true. p.lock.RLock()
func (p *PriorityQueue) AddUnschedulableIfNotPresent(pod *v1.Pod) error { defer p.lock.RUnlock()
return p.schedulingCycle
}
// AddUnschedulableIfNotPresent does nothing if the pod is present in any
// queue. If pod is unschedulable, it adds pod to unschedulable queue if
// p.moveRequestCycle > podSchedulingCycle or to backoff queue if p.moveRequestCycle
// <= podSchedulingCycle but pod is subject to backoff. In other cases, it adds pod to
// active queue.
func (p *PriorityQueue) AddUnschedulableIfNotPresent(pod *v1.Pod, podSchedulingCycle int64) error {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
if p.unschedulableQ.get(pod) != nil { if p.unschedulableQ.get(pod) != nil {
...@@ -328,7 +352,7 @@ func (p *PriorityQueue) AddUnschedulableIfNotPresent(pod *v1.Pod) error { ...@@ -328,7 +352,7 @@ func (p *PriorityQueue) AddUnschedulableIfNotPresent(pod *v1.Pod) error {
if _, exists, _ := p.activeQ.Get(pod); exists { if _, exists, _ := p.activeQ.Get(pod); exists {
return fmt.Errorf("pod is already present in the activeQ") return fmt.Errorf("pod is already present in the activeQ")
} }
if !p.receivedMoveRequest && isPodUnschedulable(pod) { if podSchedulingCycle > p.moveRequestCycle && isPodUnschedulable(pod) {
p.unschedulableQ.addOrUpdate(pod) p.unschedulableQ.addOrUpdate(pod)
p.nominatedPods.add(pod, "") p.nominatedPods.add(pod, "")
return nil return nil
...@@ -362,8 +386,8 @@ func (p *PriorityQueue) flushUnschedulableQLeftover() { ...@@ -362,8 +386,8 @@ func (p *PriorityQueue) flushUnschedulableQLeftover() {
} }
// Pop removes the head of the active queue and returns it. It blocks if the // Pop removes the head of the active queue and returns it. It blocks if the
// activeQ is empty and waits until a new item is added to the queue. It also // activeQ is empty and waits until a new item is added to the queue. It
// clears receivedMoveRequest to mark the beginning of a new scheduling cycle. // increments scheduling cycle when a pod is popped.
func (p *PriorityQueue) Pop() (*v1.Pod, error) { func (p *PriorityQueue) Pop() (*v1.Pod, error) {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
...@@ -381,7 +405,7 @@ func (p *PriorityQueue) Pop() (*v1.Pod, error) { ...@@ -381,7 +405,7 @@ func (p *PriorityQueue) Pop() (*v1.Pod, error) {
return nil, err return nil, err
} }
pod := obj.(*v1.Pod) pod := obj.(*v1.Pod)
p.receivedMoveRequest = false p.schedulingCycle++
return pod, err return pod, err
} }
...@@ -479,7 +503,7 @@ func (p *PriorityQueue) MoveAllToActiveQueue() { ...@@ -479,7 +503,7 @@ func (p *PriorityQueue) MoveAllToActiveQueue() {
} }
} }
p.unschedulableQ.clear() p.unschedulableQ.clear()
p.receivedMoveRequest = true p.moveRequestCycle = p.schedulingCycle
p.cond.Broadcast() p.cond.Broadcast()
} }
...@@ -492,7 +516,7 @@ func (p *PriorityQueue) movePodsToActiveQueue(pods []*v1.Pod) { ...@@ -492,7 +516,7 @@ func (p *PriorityQueue) movePodsToActiveQueue(pods []*v1.Pod) {
klog.Errorf("Error adding pod %v/%v to the scheduling queue: %v", pod.Namespace, pod.Name, err) klog.Errorf("Error adding pod %v/%v to the scheduling queue: %v", pod.Namespace, pod.Name, err)
} }
} }
p.receivedMoveRequest = true p.moveRequestCycle = p.schedulingCycle
p.cond.Broadcast() p.cond.Broadcast()
} }
......
...@@ -45,20 +45,20 @@ type hashedKeyMutex struct { ...@@ -45,20 +45,20 @@ type hashedKeyMutex struct {
// Acquires a lock associated with the specified ID. // Acquires a lock associated with the specified ID.
func (km *hashedKeyMutex) LockKey(id string) { func (km *hashedKeyMutex) LockKey(id string) {
klog.V(5).Infof("hashedKeyMutex.LockKey(...) called for id %q\r\n", id) klog.V(5).Infof("hashedKeyMutex.LockKey(...) called for id %q\r\n", id)
km.mutexes[km.hash(id)%len(km.mutexes)].Lock() km.mutexes[km.hash(id)%uint32(len(km.mutexes))].Lock()
klog.V(5).Infof("hashedKeyMutex.LockKey(...) for id %q completed.\r\n", id) klog.V(5).Infof("hashedKeyMutex.LockKey(...) for id %q completed.\r\n", id)
} }
// Releases the lock associated with the specified ID. // Releases the lock associated with the specified ID.
func (km *hashedKeyMutex) UnlockKey(id string) error { func (km *hashedKeyMutex) UnlockKey(id string) error {
klog.V(5).Infof("hashedKeyMutex.UnlockKey(...) called for id %q\r\n", id) klog.V(5).Infof("hashedKeyMutex.UnlockKey(...) called for id %q\r\n", id)
km.mutexes[km.hash(id)%len(km.mutexes)].Unlock() km.mutexes[km.hash(id)%uint32(len(km.mutexes))].Unlock()
klog.V(5).Infof("hashedKeyMutex.UnlockKey(...) for id %q completed.\r\n", id) klog.V(5).Infof("hashedKeyMutex.UnlockKey(...) for id %q completed.\r\n", id)
return nil return nil
} }
func (km *hashedKeyMutex) hash(id string) int { func (km *hashedKeyMutex) hash(id string) uint32 {
h := fnv.New32a() h := fnv.New32a()
h.Write([]byte(id)) h.Write([]byte(id))
return int(h.Sum32()) return h.Sum32()
} }
...@@ -49,12 +49,13 @@ func New(mounterPath string) Interface { ...@@ -49,12 +49,13 @@ func New(mounterPath string) Interface {
} }
} }
// Mount : mounts source to target as NTFS with given options. // Mount : mounts source to target with given options.
// currently only supports cifs(smb), bind mount(for disk)
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error { func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
target = normalizeWindowsPath(target) target = normalizeWindowsPath(target)
if source == "tmpfs" { if source == "tmpfs" {
klog.V(3).Infof("azureMount: mounting source (%q), target (%q), with options (%q)", source, target, options) klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, options)
return os.MkdirAll(target, 0755) return os.MkdirAll(target, 0755)
} }
...@@ -63,9 +64,9 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio ...@@ -63,9 +64,9 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
return err return err
} }
klog.V(4).Infof("azureMount: mount options(%q) source:%q, target:%q, fstype:%q, begin to mount", klog.V(4).Infof("mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
options, source, target, fstype) options, source, target, fstype)
bindSource := "" bindSource := source
// tell it's going to mount azure disk or azure file according to options // tell it's going to mount azure disk or azure file according to options
if bind, _, _ := isBind(options); bind { if bind, _, _ := isBind(options); bind {
...@@ -73,31 +74,28 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio ...@@ -73,31 +74,28 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
bindSource = normalizeWindowsPath(source) bindSource = normalizeWindowsPath(source)
} else { } else {
if len(options) < 2 { if len(options) < 2 {
klog.Warningf("azureMount: mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting", klog.Warningf("mount options(%q) command number(%d) less than 2, source:%q, target:%q, skip mounting",
options, len(options), source, target) options, len(options), source, target)
return nil return nil
} }
// currently only cifs mount is supported // currently only cifs mount is supported
if strings.ToLower(fstype) != "cifs" { if strings.ToLower(fstype) != "cifs" {
return fmt.Errorf("azureMount: only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options) return fmt.Errorf("only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, options)
} }
bindSource = source if output, err := newSMBMapping(options[0], options[1], source); err != nil {
if isSMBMappingExist(source) {
// use PowerShell Environment Variables to store user input string to prevent command line injection klog.V(2).Infof("SMB Mapping(%s) already exists, now begin to remove and remount", source)
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1 if output, err := removeSMBMapping(source); err != nil {
cmdLine := fmt.Sprintf(`$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` + return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` + }
`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`) if output, err := newSMBMapping(options[0], options[1], source); err != nil {
return fmt.Errorf("New-SmbGlobalMapping remount failed: %v, output: %q", err, output)
cmd := exec.Command("powershell", "/c", cmdLine) }
cmd.Env = append(os.Environ(), } else {
fmt.Sprintf("smbuser=%s", options[0]), return fmt.Errorf("New-SmbGlobalMapping failed: %v, output: %q", err, output)
fmt.Sprintf("smbpassword=%s", options[1]), }
fmt.Sprintf("smbremotepath=%s", source))
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("azureMount: SmbGlobalMapping failed: %v, only SMB mount is supported now, output: %q", err, string(output))
} }
} }
...@@ -109,6 +107,44 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio ...@@ -109,6 +107,44 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
return nil return nil
} }
// do the SMB mount with username, password, remotepath
// return (output, error)
func newSMBMapping(username, password, remotepath string) (string, error) {
if username == "" || password == "" || remotepath == "" {
return "", fmt.Errorf("invalid parameter(username: %s, password: %s, remoteapth: %s)", username, password, remotepath)
}
// use PowerShell Environment Variables to store user input string to prevent command line injection
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
cmdLine := `$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`
cmd := exec.Command("powershell", "/c", cmdLine)
cmd.Env = append(os.Environ(),
fmt.Sprintf("smbuser=%s", username),
fmt.Sprintf("smbpassword=%s", password),
fmt.Sprintf("smbremotepath=%s", remotepath))
output, err := cmd.CombinedOutput()
return string(output), err
}
// check whether remotepath is already mounted
func isSMBMappingExist(remotepath string) bool {
cmd := exec.Command("powershell", "/c", `Get-SmbGlobalMapping -RemotePath $Env:smbremotepath`)
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
_, err := cmd.CombinedOutput()
return err == nil
}
// remove SMB mapping
func removeSMBMapping(remotepath string) (string, error) {
cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`)
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
output, err := cmd.CombinedOutput()
return string(output), err
}
// Unmount unmounts the target. // Unmount unmounts the target.
func (mounter *Mounter) Unmount(target string) error { func (mounter *Mounter) Unmount(target string) error {
klog.V(4).Infof("azureMount: Unmount target (%q)", target) klog.V(4).Infof("azureMount: Unmount target (%q)", target)
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "13" gitMinor = "13"
gitVersion = "v1.13.3-k3s.7" gitVersion = "v1.13.4-k3s.1"
gitCommit = "96a2a74e26d1bfd15c1e0cd6bd4721af975f7a84" gitCommit = "7f72ee72d6dce73e2d36dbd3402413d146fad01e"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2019-03-01T04:28+00:00Z" buildDate = "2019-03-04T01:05+00:00Z"
) )
...@@ -48,7 +48,7 @@ github.com/elazarl/goproxy v1.0-104-gc4fc26588b6ef8 ...@@ -48,7 +48,7 @@ github.com/elazarl/goproxy v1.0-104-gc4fc26588b6ef8
github.com/emicklei/go-restful 2.2.0-4-gff4f55a206334e github.com/emicklei/go-restful 2.2.0-4-gff4f55a206334e
github.com/emicklei/go-restful-swagger12 1.0.1 github.com/emicklei/go-restful-swagger12 1.0.1
github.com/euank/go-kmsg-parser v2.0.0 github.com/euank/go-kmsg-parser v2.0.0
github.com/evanphx/json-patch v4.0.0-3-g36442dbdb58521 github.com/evanphx/json-patch v4.1.0-19-g5858425f75500d
github.com/exponent-io/jsonpath d6023ce2651d8eafb5c75bb0c7167536102ec9f5 github.com/exponent-io/jsonpath d6023ce2651d8eafb5c75bb0c7167536102ec9f5
github.com/fatih/camelcase f6a740d52f961c60348ebb109adde9f4635d7540 github.com/fatih/camelcase f6a740d52f961c60348ebb109adde9f4635d7540
github.com/fsnotify/fsnotify v1.3.1-1-gf12c6236fe7b5c github.com/fsnotify/fsnotify v1.3.1-1-gf12c6236fe7b5c
...@@ -206,5 +206,5 @@ sigs.k8s.io/yaml v1.1.0 ...@@ -206,5 +206,5 @@ sigs.k8s.io/yaml v1.1.0
vbom.ml/util db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394 vbom.ml/util db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394
bitbucket.org/ww/goautoneg a547fc61f48d567d5b4ec6f8aee5573d8efce11d https://github.com/rancher/goautoneg.git bitbucket.org/ww/goautoneg a547fc61f48d567d5b4ec6f8aee5573d8efce11d https://github.com/rancher/goautoneg.git
github.com/ibuildthecloud/kvsql c649f12fe5250718e4e024b8b40e7de796ab095e github.com/ibuildthecloud/kvsql c649f12fe5250718e4e024b8b40e7de796ab095e
github.com/google/cadvisor 91dab6eb91496ed68acbef68b02b34b3392ca754 https://github.com/ibuildthecloud/cadvisor.git github.com/google/cadvisor 87e237ff35b9d752ba58860a06e0ebe57816cbb7 https://github.com/ibuildthecloud/cadvisor.git
github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb
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