Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update node status to include the absense of cpu hardcapping. #22487

Merged
merged 1 commit into from
Mar 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/kubelet/cm/container_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type ContainerManager interface {

// Returns a NodeConfig that is being used by the container manager.
GetNodeConfig() NodeConfig

// Returns internal Status.
Status() Status
}

type NodeConfig struct {
Expand All @@ -41,3 +44,8 @@ type NodeConfig struct {
KubeletCgroupsName string
ContainerRuntime string
}

type Status struct {
// Any soft requirements that were unsatisfied.
SoftRequirements error
}
51 changes: 45 additions & 6 deletions pkg/kubelet/cm/container_manager_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"os"
"os/exec"
"path"
"strconv"
"strings"
"sync"
Expand All @@ -34,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/util"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/oom"
Expand Down Expand Up @@ -79,39 +81,67 @@ type containerManagerImpl struct {
cadvisorInterface cadvisor.Interface
mountUtil mount.Interface
NodeConfig
status Status
// External containers being managed.
systemContainers []*systemContainer
periodicTasks []func()
}

type features struct {
cpuHardcapping bool
}

var _ ContainerManager = &containerManagerImpl{}

// checks if the required cgroups subsystems are mounted.
// As of now, only 'cpu' and 'memory' are required.
func validateSystemRequirements(mountUtil mount.Interface) error {
// cpu quota is a soft requirement.
func validateSystemRequirements(mountUtil mount.Interface) (features, error) {
const (
cgroupMountType = "cgroup"
localErr = "system validation failed"
)
var (
cpuMountPoint string
f features
)
mountPoints, err := mountUtil.List()
if err != nil {
return fmt.Errorf("%s - %v", localErr, err)
return f, fmt.Errorf("%s - %v", localErr, err)
}

expectedCgroups := sets.NewString("cpu", "cpuacct", "cpuset", "memory")
for _, mountPoint := range mountPoints {
if mountPoint.Type == cgroupMountType {
for _, opt := range mountPoint.Opts {
if expectedCgroups.Has(opt) {
expectedCgroups.Delete(opt)
}
if opt == "cpu" {
cpuMountPoint = mountPoint.Path
}
}
}
}

if expectedCgroups.Len() > 0 {
return fmt.Errorf("%s - Following Cgroup subsystem not mounted: %v", localErr, expectedCgroups.List())
return f, fmt.Errorf("%s - Following Cgroup subsystem not mounted: %v", localErr, expectedCgroups.List())
}
return nil

// Check if cpu quota is available.
// CPU cgroup is required and so it expected to be mounted at this point.
periodExists, err := util.FileExists(path.Join(cpuMountPoint, "cpu.cfs_period_us"))
if err != nil {
glog.Errorf("failed to detect if CPU cgroup cpu.cfs_period_us is available - %v", err)
}
quotaExists, err := util.FileExists(path.Join(cpuMountPoint, "cpu.cfs_quota_us"))
if err != nil {
glog.Errorf("failed to detect if CPU cgroup cpu.cfs_quota_us is available - %v", err)
}
if quotaExists && periodExists {
f.cpuHardcapping = true
}
return f, nil
}

// TODO(vmarmol): Add limits to the system containers.
Expand Down Expand Up @@ -185,10 +215,13 @@ func setupKernelTunables(option KernelTunableBehavior) error {
}

func (cm *containerManagerImpl) setupNode() error {
if err := validateSystemRequirements(cm.mountUtil); err != nil {
f, err := validateSystemRequirements(cm.mountUtil)
if err != nil {
return err
}

if !f.cpuHardcapping {
cm.status.SoftRequirements = fmt.Errorf("CPU hardcapping unsupported")
}
// TODO: plumb kernel tunable options into container manager, right now, we modify by default
if err := setupKernelTunables(KernelTunableModify); err != nil {
return err
Expand Down Expand Up @@ -312,6 +345,12 @@ func (cm *containerManagerImpl) GetNodeConfig() NodeConfig {
return cm.NodeConfig
}

func (cm *containerManagerImpl) Status() Status {
cm.RLock()
defer cm.RUnlock()
return cm.status
}

func (cm *containerManagerImpl) Start() error {
// Setup the node
if err := cm.setupNode(); err != nil {
Expand Down
45 changes: 42 additions & 3 deletions pkg/kubelet/cm/container_manager_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ package cm

import (
"fmt"
"io/ioutil"
"os"
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"k8s.io/kubernetes/pkg/util/mount"
)
Expand Down Expand Up @@ -75,7 +79,9 @@ func fakeContainerMgrMountInt() mount.Interface {
}

func TestCgroupMountValidationSuccess(t *testing.T) {
assert.Nil(t, validateSystemRequirements(fakeContainerMgrMountInt()))
f, err := validateSystemRequirements(fakeContainerMgrMountInt())
assert.Nil(t, err)
assert.False(t, f.cpuHardcapping, "cpu hardcapping is expected to be disabled")
}

func TestCgroupMountValidationMemoryMissing(t *testing.T) {
Expand All @@ -98,7 +104,8 @@ func TestCgroupMountValidationMemoryMissing(t *testing.T) {
},
},
}
assert.Error(t, validateSystemRequirements(mountInt))
_, err := validateSystemRequirements(mountInt)
assert.Error(t, err)
}

func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
Expand All @@ -121,5 +128,37 @@ func TestCgroupMountValidationMultipleSubsytem(t *testing.T) {
},
},
}
assert.Nil(t, validateSystemRequirements(mountInt))
_, err := validateSystemRequirements(mountInt)
assert.Nil(t, err)
}

func TestSoftRequirementsValidationSuccess(t *testing.T) {
req := require.New(t)
tempDir, err := ioutil.TempDir("", "")
req.NoError(err)
req.NoError(ioutil.WriteFile(path.Join(tempDir, "cpu.cfs_period_us"), []byte("0"), os.ModePerm))
req.NoError(ioutil.WriteFile(path.Join(tempDir, "cpu.cfs_quota_us"), []byte("0"), os.ModePerm))
mountInt := &fakeMountInterface{
[]mount.MountPoint{
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuset"},
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpu"},
Path: tempDir,
},
{
Device: "cgroup",
Type: "cgroup",
Opts: []string{"rw", "relatime", "cpuacct", "memory"},
},
},
}
f, err := validateSystemRequirements(mountInt)
assert.NoError(t, err)
assert.True(t, f.cpuHardcapping, "cpu hardcapping is expected to be enabled")
}
4 changes: 4 additions & 0 deletions pkg/kubelet/cm/container_manager_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func (cm *containerManagerStub) GetNodeConfig() NodeConfig {
return NodeConfig{}
}

func (cm *containerManagerStub) Status() Status {
return Status{}
}

func NewStubContainerManager() ContainerManager {
return &containerManagerStub{}
}
4 changes: 4 additions & 0 deletions pkg/kubelet/cm/container_manager_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func (unsupportedContainerManager) GetNodeConfig() NodeConfig {
return NodeConfig{}
}

func (cm *unsupportedContainerManager) Status() Status {
return Status{}
}

func NewContainerManager(_ mount.Interface, _ cadvisor.Interface, _ NodeConfig) (ContainerManager, error) {
return &unsupportedContainerManager{}, nil
}
6 changes: 6 additions & 0 deletions pkg/kubelet/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2931,6 +2931,12 @@ func (kl *Kubelet) setNodeReadyCondition(node *api.Node) {
}
}

// Record any soft requirements that were not met in the container manager.
status := kl.containerManager.Status()
if status.SoftRequirements != nil {
newNodeReadyCondition.Message = fmt.Sprintf("%s. WARNING: %s", newNodeReadyCondition.Message, status.SoftRequirements.Error())
}

readyConditionUpdated := false
needToRecordEvent := false
for i := range node.Status.Conditions {
Expand Down