Merge pull request #825 from brendandburns/runin

Add support for "run in"
This commit is contained in:
Victor Marmol 2014-08-07 23:47:19 -07:00
commit adc57da3f3
3 changed files with 128 additions and 0 deletions

View File

@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math/rand"
"os/exec"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@ -63,6 +64,30 @@ func NewDockerPuller(client DockerInterface) DockerPuller {
}
}
type dockerContainerCommandRunner struct{}
func (d *dockerContainerCommandRunner) getRunInContainerCommand(containerID string, cmd []string) (*exec.Cmd, error) {
args := append([]string{"exec"}, cmd...)
command := exec.Command("/usr/sbin/nsinit", args...)
command.Dir = fmt.Sprintf("/var/lib/docker/execdriver/native/%s", containerID)
return command, nil
}
// RunInContainer uses nsinit to run the command inside the container identified by containerID
func (d *dockerContainerCommandRunner) RunInContainer(containerID string, cmd []string) ([]byte, error) {
c, err := d.getRunInContainerCommand(containerID, cmd)
if err != nil {
return nil, err
}
return c.CombinedOutput()
}
// NewDockerContainerCommandRunner creates a ContainerCommandRunner which uses nsinit to run a command
// inside a container.
func NewDockerContainerCommandRunner() ContainerCommandRunner {
return &dockerContainerCommandRunner{}
}
func (p dockerPuller) Pull(image string) error {
image, tag := parseImageName(image)

View File

@ -89,6 +89,10 @@ func NewIntegrationTestKubelet(hn string, dc DockerInterface) *Kubelet {
}
}
type ContainerCommandRunner interface {
RunInContainer(containerID string, cmd []string) ([]byte, error)
}
// Kubelet is the main kubelet implementation.
type Kubelet struct {
hostname string
@ -107,6 +111,8 @@ type Kubelet struct {
dockerPuller DockerPuller
// Optional, defaults to /logs/ from /var/log
logServer http.Handler
// Optional, defaults to simple Docker implementation
runner ContainerCommandRunner
}
// Run starts the kubelet reacting to config updates
@ -666,3 +672,20 @@ func (kl *Kubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
// TODO: whitelist logs we are willing to serve
kl.logServer.ServeHTTP(w, req)
}
// Run a command in a container, returns the combined stdout, stderr as an array of bytes
func (kl *Kubelet) RunInContainer(pod *Pod, container string, cmd []string) ([]byte, error) {
if kl.runner == nil {
return nil, fmt.Errorf("no runner specified.")
}
podFullName := GetPodFullName(pod)
dockerContainers, err := getKubeletDockerContainers(kl.dockerClient)
if err != nil {
return nil, err
}
dockerContainer, found := dockerContainers.FindPodContainer(podFullName, container)
if !found {
return nil, fmt.Errorf("container not found (%s)", container)
}
return kl.runner.RunInContainer(dockerContainer.ID, cmd)
}

View File

@ -843,6 +843,86 @@ func TestGetContainerInfoOnNonExistContainer(t *testing.T) {
mockCadvisor.AssertExpectations(t)
}
type fakeContainerCommandRunner struct {
Cmd []string
ID string
E error
}
func (f *fakeContainerCommandRunner) RunInContainer(id string, cmd []string) ([]byte, error) {
f.Cmd = cmd
f.ID = id
return []byte{}, f.E
}
func TestRunInContainerNoSuchPod(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
kubelet, _, fakeDocker := makeTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{}
kubelet.runner = &fakeCommandRunner
podName := "podFoo"
podNamespace := "etcd"
containerName := "containerFoo"
output, err := kubelet.RunInContainer(
&Pod{Name: podName, Namespace: podNamespace},
containerName,
[]string{"ls"})
if output != nil {
t.Errorf("unexpected non-nil command: %v", output)
}
if err == nil {
t.Error("unexpected non-error")
}
}
func TestRunInContainer(t *testing.T) {
fakeCommandRunner := fakeContainerCommandRunner{}
kubelet, _, fakeDocker := makeTestKubelet(t)
kubelet.runner = &fakeCommandRunner
containerID := "abc1234"
podName := "podFoo"
podNamespace := "etcd"
containerName := "containerFoo"
fakeDocker.containerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s--" + containerName + "--" + podName + "." + podNamespace + "--1234"},
},
}
cmd := []string{"ls"}
_, err := kubelet.RunInContainer(
&Pod{Name: podName, Namespace: podNamespace},
containerName,
cmd)
if fakeCommandRunner.ID != containerID {
t.Errorf("unexected ID: %s", fakeCommandRunner.ID)
}
if !reflect.DeepEqual(fakeCommandRunner.Cmd, cmd) {
t.Errorf("unexpected commnd: %s", fakeCommandRunner.Cmd)
}
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func TestDockerContainerCommand(t *testing.T) {
runner := dockerContainerCommandRunner{}
containerID := "1234"
command := []string{"ls"}
cmd, _ := runner.getRunInContainerCommand(containerID, command)
if cmd.Dir != "/var/lib/docker/execdriver/native/"+containerID {
t.Errorf("unexpected command CWD: %s", cmd.Dir)
}
if !reflect.DeepEqual(cmd.Args, []string{"/usr/sbin/nsinit", "exec", "ls"}) {
t.Errorf("unexpectd command args: %s", cmd.Args)
}
}
var parseImageNameTests = []struct {
imageName string
name string