Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

sysctl: Add complete sysctl support #473

Merged
merged 3 commits into from
Mar 11, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 52 additions & 6 deletions grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,13 +648,12 @@ func (a *agentGRPC) CreateContainer(ctx context.Context, req *pb.CreateContainer
return emptyResp, err
}

if ociSpec.Linux.Resources.CPU != nil && ociSpec.Linux.Resources.CPU.Cpus != "" {
availableCpuset, err := getAvailableCpusetList(ociSpec.Linux.Resources.CPU.Cpus)
if err != nil {
return emptyResp, err
}
if err := a.handleCPUSet(ociSpec); err != nil {
return emptyResp, err
}

ociSpec.Linux.Resources.CPU.Cpus = availableCpuset
if err := a.applyNetworkSysctls(ociSpec); err != nil {
return emptyResp, err
}

if a.sandbox.guestHooksPresent {
Expand Down Expand Up @@ -699,6 +698,53 @@ func (a *agentGRPC) CreateContainer(ctx context.Context, req *pb.CreateContainer
return a.finishCreateContainer(ctr, req, config)
}

// Path overridden in unit tests
var procSysDir = "/proc/sys"

// writeSystemProperty writes the value to a path under /proc/sys as determined from the key.
// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward.
func writeSystemProperty(key, value string) error {
keyPath := strings.Replace(key, ".", "/", -1)
return ioutil.WriteFile(filepath.Join(procSysDir, keyPath), []byte(value), 0644)
}

func isNetworkSysctl(sysctl string) bool {
return strings.HasPrefix(sysctl, "net.")
}

// libcontainer checks if the container is running in a separate network namespace
// before applying the network related sysctls. If it sees that the network namespace of the container
// is the same as the "host", it errors out. Since we do no create a new net namespace inside the guest,
// libcontainer would error out while verifying network sysctls. To overcome this, we dont pass
// network sysctls to libcontainer, we instead have the agent directly apply them. All other namespaced
// sysctls are applied by libcontainer.
func (a *agentGRPC) applyNetworkSysctls(ociSpec *specs.Spec) error {
sysctls := ociSpec.Linux.Sysctl
for key, value := range sysctls {
if isNetworkSysctl(key) {
if err := writeSystemProperty(key, value); err != nil {
return err
}
delete(sysctls, key)
}
}

ociSpec.Linux.Sysctl = sysctls
return nil
}

func (a *agentGRPC) handleCPUSet(ociSpec *specs.Spec) error {
if ociSpec.Linux.Resources.CPU != nil && ociSpec.Linux.Resources.CPU.Cpus != "" {
availableCpuset, err := getAvailableCpusetList(ociSpec.Linux.Resources.CPU.Cpus)
if err != nil {
return err
}

ociSpec.Linux.Resources.CPU.Cpus = availableCpuset
}
return nil
}

func posixRlimitsToRlimits(posixRlimits []specs.POSIXRlimit) []configs.Rlimit {
var rlimits []configs.Rlimit

Expand Down
92 changes: 92 additions & 0 deletions grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,98 @@ func TestListProcesses(t *testing.T) {
assert.NotEmpty(r.ProcessList)
}

func TestIsNetworkSysctl(t *testing.T) {
assert := assert.New(t)

sysctl := "net.core.somaxconn"
isNet := isNetworkSysctl(sysctl)
assert.True(isNet)

sysctl = "kernel.shmmax"
isNet = isNetworkSysctl(sysctl)
assert.False(isNet)
}

func TestWriteSystemProperty(t *testing.T) {
assert := assert.New(t)

tmpDir, err := ioutil.TempDir("", "procsys")
assert.Nil(err)
defer os.RemoveAll(tmpDir)

key := "net.core.somaxconn"
value := "1024"
procSysDir = filepath.Join(tmpDir, "proc", "sys")
err = os.MkdirAll(procSysDir, 0755)
assert.Nil(err)

netCoreDir := filepath.Join(procSysDir, "net", "core")
err = os.MkdirAll(netCoreDir, 0755)
assert.Nil(err)

sysFile := filepath.Join(netCoreDir, "somaxconn")
fd, err := os.Create(sysFile)
assert.Nil(err)
fd.Close()

err = writeSystemProperty(key, value)
assert.Nil(err)

// Read file and verify
content, err := ioutil.ReadFile(sysFile)
assert.Nil(err)
assert.Equal(value, string(content))

// Following checks require root privileges to remove a read-only dir
if os.Geteuid() != 0 {
return
}

// Remove write permissions for procSysDir to what they normally are
// for /proc/sys so that files cannot be created
err = os.Chmod(procSysDir, 0555)
assert.Nil(err)

// Nonexistent sys file
key = "net.ipv4.ip_forward"
value = "1"
err = writeSystemProperty(key, value)
assert.NotNil(err)
}

func TestApplyNetworkSysctls(t *testing.T) {
assert := assert.New(t)
a := &agentGRPC{}

spec := &specs.Spec{}
spec.Linux = &specs.Linux{}

spec.Linux.Sysctl = make(map[string]string)
spec.Linux.Sysctl["kernel.shmmax"] = "512"

err := a.applyNetworkSysctls(spec)
assert.Nil(err)
assert.Equal(len(spec.Linux.Sysctl), 1)
assert.Equal(spec.Linux.Sysctl["kernel.shmmax"], "512")

// Check with network sysctl
spec.Linux.Sysctl["net.core.somaxconn"] = "1024"
tmpDir, err := ioutil.TempDir("", "procsys")
assert.Nil(err)
defer os.RemoveAll(tmpDir)

procSysDir = filepath.Join(tmpDir, "proc", "sys")
netCoreDir := filepath.Join(procSysDir, "net", "core")
err = os.MkdirAll(netCoreDir, 0755)
assert.Nil(err)

assert.Equal(len(spec.Linux.Sysctl), 2)
err = a.applyNetworkSysctls(spec)
assert.Nil(err)
assert.Equal(len(spec.Linux.Sysctl), 1)
assert.Equal(spec.Linux.Sysctl["kernel.shmmax"], "512")
}

func TestUpdateContainer(t *testing.T) {
containerID := "1"
assert := assert.New(t)
Expand Down