diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 4bb60df0852..71ae0c83f49 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -1056,7 +1056,7 @@ func TestRunInContainerNoSuchPod(t *testing.T) { podNamespace := "etcd" containerName := "containerFoo" output, err := kubelet.RunInContainer( - podName+"."+podNamespace, + GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}), containerName, []string{"ls"}) if output != nil { @@ -1086,7 +1086,7 @@ func TestRunInContainer(t *testing.T) { cmd := []string{"ls"} _, err := kubelet.RunInContainer( - podName+"."+podNamespace, + GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}), containerName, cmd) if fakeCommandRunner.ID != containerID { diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index c0def80ee25..b1920e1cec6 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -65,6 +65,7 @@ type HostInterface interface { GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) GetMachineInfo() (*info.MachineInfo, error) GetPodInfo(name string) (api.PodInfo, error) + RunInContainer(name, container string, cmd []string) ([]byte, error) ServeLogs(w http.ResponseWriter, req *http.Request) } @@ -88,6 +89,7 @@ func (s *Server) InstallDefaultHandlers() { s.mux.HandleFunc("/stats/", s.handleStats) s.mux.HandleFunc("/logs/", s.handleLogs) s.mux.HandleFunc("/spec/", s.handleSpec) + s.mux.HandleFunc("/run/", s.handleRun) } // error serializes an error object into an HTTP response @@ -204,6 +206,31 @@ func (s *Server) handleSpec(w http.ResponseWriter, req *http.Request) { } +// handleRun handles requests to run a command inside a container +func (s *Server) handleRun(w http.ResponseWriter, req *http.Request) { + u, err := url.ParseRequestURI(req.RequestURI) + if err != nil { + s.error(w, err) + return + } + parts := strings.Split(u.Path, "/") + if len(parts) != 4 { + http.Error(w, "Unexpected path for command running", http.StatusBadRequest) + return + } + podID := parts[2] + container := parts[3] + podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"}) + command := strings.Split(u.Query().Get("cmd"), " ") + data, err := s.host.RunInContainer(podFullName, container, command) + if err != nil { + s.error(w, err) + return + } + w.Header().Add("Content-type", "text/plain") + w.Write(data) +} + // ServeHTTP responds to HTTP requests on the Kubelet func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { defer httplog.NewLogged(req, &w).StacktraceWhen( diff --git a/pkg/kubelet/server_test.go b/pkg/kubelet/server_test.go index b6662a86ccc..2745679d8a4 100644 --- a/pkg/kubelet/server_test.go +++ b/pkg/kubelet/server_test.go @@ -40,6 +40,7 @@ type fakeKubelet struct { rootInfoFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error) machineInfoFunc func() (*info.MachineInfo, error) logFunc func(w http.ResponseWriter, req *http.Request) + runFunc func(podFullName, containerName string, cmd []string) ([]byte, error) } func (fk *fakeKubelet) GetPodInfo(name string) (api.PodInfo, error) { @@ -62,6 +63,10 @@ func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) { fk.logFunc(w, req) } +func (fk *fakeKubelet) RunInContainer(podFullName, containerName string, cmd []string) ([]byte, error) { + return fk.runFunc(podFullName, containerName, cmd) +} + type serverTestFramework struct { updateChan chan interface{} updateReader *channelReader @@ -288,3 +293,42 @@ func TestServeLogs(t *testing.T) { t.Errorf("Received wrong data: %s", result) } } + +func TestServeRunInContainer(t *testing.T) { + fw := newServerTest() + output := "foo bar" + podName := "foo" + expectedPodName := podName + ".etcd" + expectedContainerName := "baz" + expectedCommand := "ls -a" + fw.fakeKubelet.runFunc = func(podFullName, containerName string, cmd []string) ([]byte, error) { + if podFullName != expectedPodName { + t.Errorf("expected %s, got %s", expectedPodName, podFullName) + } + if containerName != expectedContainerName { + t.Errorf("expected %s, got %s", expectedContainerName, containerName) + } + if strings.Join(cmd, " ") != expectedCommand { + t.Errorf("expected: %s, got %v", expectedCommand, cmd) + } + + return []byte(output), nil + } + + resp, err := http.Get(fw.testHTTPServer.URL + "/run/" + podName + "/" + expectedContainerName + "?cmd=ls%20-a") + + if err != nil { + t.Fatalf("Got error GETing: %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + // copying the response body did not work + t.Errorf("Cannot copy resp: %#v", err) + } + result := string(body) + if result != output { + t.Errorf("expected %s, got %s", output, result) + } +}