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

[FEATURE] Memory Limits (#494, @konradmalik) #494

Merged
merged 15 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQl
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rancher/k3d v1.7.0 h1:GEN/lmtMrklqfBh4Jha06OE6cLOeKwgTFmG/UgOR6VA=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
Expand Down
15 changes: 15 additions & 0 deletions pkg/client/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
dockerunits "github.com/docker/go-units"
"github.com/imdario/mergo"
"github.com/rancher/k3d/v4/pkg/runtimes"
"github.com/rancher/k3d/v4/pkg/runtimes/docker"
k3d "github.com/rancher/k3d/v4/pkg/types"
"github.com/rancher/k3d/v4/pkg/util"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -344,6 +345,20 @@ func NodeCreate(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, c
return fmt.Errorf("Failed to create fake meminfo: %+v", err)
}
node.Volumes = append(node.Volumes, fmt.Sprintf("%s:/proc/meminfo:ro", fakemempath))
// mount empty edac folder, but only if it exists
exists, err := docker.CheckIfDirectoryExists(ctx, node.Image, "/sys/devices/system/edac")
if err != nil {
return fmt.Errorf("Failed to check for the existence of edac folder: %+v", err)
}
if exists {
log.Debugln("Found edac folder")
fakeedacpath, err := util.MakeFakeEdac(node.Name)
if err != nil {
return fmt.Errorf("Failed to create fake edac: %+v", err)
}
node.Volumes = append(node.Volumes, fmt.Sprintf("%s:/sys/devices/system/edac:ro", fakeedacpath))
}

}

/*
Expand Down
57 changes: 34 additions & 23 deletions pkg/runtimes/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,50 +177,61 @@ func getNodeContainer(ctx context.Context, node *k3d.Node) (*types.Container, er

}

// TODO just a template to work with
func executeCheckInContainer(cmd string) (int64, error) {
//[ -d \"/sys/devices/system/edac\" ] && exit 0 || exit 1
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
// executes an arbitrary command in a container while returning its exit code.
// useful to check something in docker env
func executeCheckInContainer(ctx context.Context, image string, cmd []string) (int64, error) {
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
log.Errorln("Failed to create docker client")
return -1, err
}
defer docker.Close()

reader, err := cli.ImagePull(ctx, "docker.io/library/alpine", types.ImagePullOptions{})
if err != nil {
panic(err)
if err = pullImage(ctx, docker, image); err != nil {
return -1, err
}
io.Copy(os.Stdout, reader)

resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: "alpine",
Cmd: []string{"sh", "-c", "[ -d \"/sys/devices/system/edac\" ] && exit 0 || exit 1"},
Tty: false,
resp, err := docker.ContainerCreate(ctx, &container.Config{
Image: image,
Cmd: cmd,
Tty: false,
Entrypoint: []string{},
}, nil, nil, nil, "")
if err != nil {
panic(err)
log.Errorf("Failed to create container from image %s with cmd %s", image, cmd)
return -1, err
}

if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
panic(err)
if err = startContainer(ctx, resp.ID); err != nil {
return -1, err
}

exitCode := -1
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
statusCh, errCh := docker.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
panic(err)
log.Errorf("Error while waiting for container %s to exit", resp.ID)
return -1, err
}
case status := <-statusCh:
fmt.Printf("status was %d\n", status.StatusCode)
exitCode = int(status.StatusCode)
}

err = cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
if err != nil {
panic(err)
if err = removeContainer(ctx, resp.ID); err != nil {
return -1, err
}

return int64(exitCode), nil
}

// CheckIfDirectoryExists checks for the existence of a given path inside the docker environment
func CheckIfDirectoryExists(ctx context.Context, image string, dir string) (bool, error) {
log.Tracef("checking if dir %s exists in docker environment...", dir)
shellCmd := fmt.Sprintf("[ -d \"%s\" ] && exit 0 || exit 1", dir)
cmd := []string{"sh", "-c", shellCmd}
exitCode, err := executeCheckInContainer(ctx, image, cmd)
log.Tracef("check dir container returned %d exist code", exitCode)
return exitCode == 0, err

}
17 changes: 17 additions & 0 deletions pkg/util/infofaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ func MakeFakeMeminfo(memoryBytes int64, uniqueName string) (string, error) {
return fakememinfo.Name(), nil
}

// MakeFakeEdac creates an empty edac folder to force cadvisor
// to use meminfo even for ECC memory
func MakeFakeEdac(uniqueName string) (string, error) {
dir, err := GetNodeFakerDirOrCreate(uniqueName)
if err != nil {
return "", err
}
edacPath := path.Join(dir, "edac")
// create directories if necessary
if err := createDirIfNotExists(edacPath); err != nil {
log.Errorf("Failed to create fake edac path '%s'", edacPath)
return "", err
}

return edacPath, nil
}

// returns a path to (existent or not) fake (mem or cpu)info file for a given node/container name
func fakeInfoPathForName(infoType string, uniqueName string) (string, error) {
// this file needs to be kept across reboots, keep it in ~/.k3d
Expand Down