From ed39fbeb2ad3959f37cf6c16aaf30aacb3292817 Mon Sep 17 00:00:00 2001 From: Samuel Karp Date: Wed, 3 Jun 2015 12:01:53 -0700 Subject: [PATCH] Adjust disallowed CpuShares in /containers/create Previous versions of libcontainer allowed CpuShares that were greater than the maximum or less than the minimum supported by the kernel, and relied on the kernel to do the right thing. Newer libcontainer fails after creating the container if the requested CpuShares is different from what was actually created by the kernel, which breaks compatibility with earlier Docker Remote API versions. This change explicitly adjusts the requested CpuShares in API versions < 1.20. Signed-off-by: Samuel Karp --- api/server/server.go | 1 + api/server/server_linux.go | 24 ++++++++++++ api/server/server_linux_test.go | 68 +++++++++++++++++++++++++++++++++ api/server/server_windows.go | 3 ++ 4 files changed, 96 insertions(+) create mode 100644 api/server/server_linux_test.go diff --git a/api/server/server.go b/api/server/server.go index d7ec2d47d7..0b7fa916d2 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -894,6 +894,7 @@ func (s *Server) postContainersCreate(version version.Version, w http.ResponseWr if err != nil { return err } + adjustCpuShares(version, hostConfig) containerId, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig) if err != nil { diff --git a/api/server/server_linux.go b/api/server/server_linux.go index f1a3c88250..93dd42990a 100644 --- a/api/server/server_linux.go +++ b/api/server/server_linux.go @@ -8,12 +8,21 @@ import ( "net/http" "strconv" + "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon" "github.com/docker/docker/pkg/sockets" "github.com/docker/docker/pkg/systemd" + "github.com/docker/docker/pkg/version" + "github.com/docker/docker/runconfig" "github.com/docker/libnetwork/portallocator" ) +const ( + // See http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269 + linuxMinCpuShares = 2 + linuxMaxCpuShares = 262144 +) + // newServer sets up the required serverClosers and does protocol specific checking. func (s *Server) newServer(proto, addr string) ([]serverCloser, error) { var ( @@ -96,3 +105,18 @@ func allocateDaemonPort(addr string) error { } return nil } + +func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) { + if version.LessThan("1.19") { + if hostConfig.CpuShares > 0 { + // Handle unsupported CpuShares + if hostConfig.CpuShares < linuxMinCpuShares { + logrus.Warnf("Changing requested CpuShares of %d to minimum allowed of %d", hostConfig.CpuShares, linuxMinCpuShares) + hostConfig.CpuShares = linuxMinCpuShares + } else if hostConfig.CpuShares > linuxMaxCpuShares { + logrus.Warnf("Changing requested CpuShares of %d to maximum allowed of %d", hostConfig.CpuShares, linuxMaxCpuShares) + hostConfig.CpuShares = linuxMaxCpuShares + } + } + } +} diff --git a/api/server/server_linux_test.go b/api/server/server_linux_test.go new file mode 100644 index 0000000000..47289653eb --- /dev/null +++ b/api/server/server_linux_test.go @@ -0,0 +1,68 @@ +// +build linux + +package server + +import ( + "testing" + + "github.com/docker/docker/pkg/version" + "github.com/docker/docker/runconfig" +) + +func TestAdjustCpuSharesOldApi(t *testing.T) { + apiVersion := version.Version("1.18") + hostConfig := &runconfig.HostConfig{ + CpuShares: linuxMinCpuShares - 1, + } + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != linuxMinCpuShares { + t.Errorf("Expected CpuShares to be %d", linuxMinCpuShares) + } + + hostConfig.CpuShares = linuxMaxCpuShares + 1 + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != linuxMaxCpuShares { + t.Errorf("Expected CpuShares to be %d", linuxMaxCpuShares) + } + + hostConfig.CpuShares = 0 + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != 0 { + t.Error("Expected CpuShares to be unchanged") + } + + hostConfig.CpuShares = 1024 + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != 1024 { + t.Error("Expected CpuShares to be unchanged") + } +} + +func TestAdjustCpuSharesNoAdjustment(t *testing.T) { + apiVersion := version.Version("1.19") + hostConfig := &runconfig.HostConfig{ + CpuShares: linuxMinCpuShares - 1, + } + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != linuxMinCpuShares-1 { + t.Errorf("Expected CpuShares to be %d", linuxMinCpuShares-1) + } + + hostConfig.CpuShares = linuxMaxCpuShares + 1 + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != linuxMaxCpuShares+1 { + t.Errorf("Expected CpuShares to be %d", linuxMaxCpuShares+1) + } + + hostConfig.CpuShares = 0 + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != 0 { + t.Error("Expected CpuShares to be unchanged") + } + + hostConfig.CpuShares = 1024 + adjustCpuShares(apiVersion, hostConfig) + if hostConfig.CpuShares != 1024 { + t.Error("Expected CpuShares to be unchanged") + } +} diff --git a/api/server/server_windows.go b/api/server/server_windows.go index 010582f634..2e33af8d25 100644 --- a/api/server/server_windows.go +++ b/api/server/server_windows.go @@ -54,3 +54,6 @@ func (s *Server) AcceptConnections(d *daemon.Daemon) { func allocateDaemonPort(addr string) error { return nil } + +func adjustCpuShares(version version.Version, hostConfig *runconfig.HostConfig) { +}