diff --git a/cmd/cloudcfg/cloudcfg.go b/cmd/cloudcfg/cloudcfg.go index 10d5e3b17ae..bf9d89399a9 100644 --- a/cmd/cloudcfg/cloudcfg.go +++ b/cmd/cloudcfg/cloudcfg.go @@ -21,7 +21,6 @@ import ( "fmt" "io/ioutil" "log" - "net/http" "net/url" "os" "strconv" @@ -48,6 +47,7 @@ var ( yaml = flag.Bool("yaml", false, "If true, print raw YAML for responses") verbose = flag.Bool("verbose", false, "If true, print extra information") proxy = flag.Bool("proxy", false, "If true, run a proxy to the api server") + www = flag.String("www", "", "If -proxy is true, use this directory to serve static files") ) func usage() { @@ -98,11 +98,6 @@ func main() { os.Exit(0) } - if len(flag.Args()) < 1 { - usage() - os.Exit(1) - } - method := flag.Arg(0) secure := true parsedUrl, err := url.Parse(*httpServer) if err != nil { @@ -121,14 +116,17 @@ func main() { } if *proxy { - server := cloudcfg.ProxyServer{ - Host: *httpServer, - Client: &http.Client{}, - } - http.Handle("/api/", &server) - log.Fatal(http.ListenAndServe(":8001", nil)) + log.Println("Starting to serve on localhost:8001") + server := cloudcfg.NewProxyServer(*www, *httpServer, auth) + log.Fatal(server.Serve()) } + if len(flag.Args()) < 1 { + usage() + os.Exit(1) + } + method := flag.Arg(0) + matchFound := executeAPIRequest(method, auth) || executeControllerRequest(method, auth) if matchFound == false { log.Fatalf("Unknown command %s", method) diff --git a/pkg/api/types.go b/pkg/api/types.go index a9d15a3f47b..f0e43638bb0 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -197,7 +197,8 @@ type Status struct { // One of: "success", "failure", "working" (for operations not yet completed) // TODO: if "working", include an operation identifier so final status can be // checked. - Status string `json:"status,omitempty" yaml:"status,omitempty"` + Status string `json:"status,omitempty" yaml:"status,omitempty"` + Details string `json:"details,omitempty" yaml:"details,omitempty"` } // Values of Status.Status diff --git a/pkg/client/request.go b/pkg/client/request.go index a8ad1da0bbc..30bd13ccb83 100644 --- a/pkg/client/request.go +++ b/pkg/client/request.go @@ -147,7 +147,7 @@ func (r *Request) Body(obj interface{}) *Request { return r } -// Format and execute the request. +// Format and xecute the request. Returns the API object received, or an error. func (r *Request) Do() Result { if r.err != nil { return Result{err: r.err} diff --git a/pkg/cloudcfg/cloudcfg.go b/pkg/cloudcfg/cloudcfg.go index cafaaddaa4d..b4ca4b807a6 100644 --- a/pkg/cloudcfg/cloudcfg.go +++ b/pkg/cloudcfg/cloudcfg.go @@ -79,7 +79,9 @@ func Update(name string, client client.ClientInterface, updatePeriod time.Durati return err } for _, pod := range podList.Items { - _, err = client.UpdatePod(pod) + // We delete the pod here, the controller will recreate it. This will result in pulling + // a new Docker image. This isn't a full "update" but its what we support for now. + err = client.DeletePod(pod.ID) if err != nil { return err } diff --git a/pkg/cloudcfg/cloudcfg_test.go b/pkg/cloudcfg/cloudcfg_test.go index b016fccca3d..e8e57d41288 100644 --- a/pkg/cloudcfg/cloudcfg_test.go +++ b/pkg/cloudcfg/cloudcfg_test.go @@ -131,8 +131,9 @@ func TestUpdateWithPods(t *testing.T) { } validateAction(Action{action: "get-controller", value: "foo"}, client.actions[0], t) validateAction(Action{action: "list-pods"}, client.actions[1], t) - validateAction(Action{action: "update-pod", value: "pod-1"}, client.actions[2], t) - validateAction(Action{action: "update-pod", value: "pod-2"}, client.actions[3], t) + // Update deletes the pods, it relies on the replication controller to replace them. + validateAction(Action{action: "delete-pod", value: "pod-1"}, client.actions[2], t) + validateAction(Action{action: "delete-pod", value: "pod-2"}, client.actions[3], t) } func TestUpdateNoPods(t *testing.T) { diff --git a/pkg/cloudcfg/proxy_server.go b/pkg/cloudcfg/proxy_server.go index 77270edad73..f0ae0bbda7f 100644 --- a/pkg/cloudcfg/proxy_server.go +++ b/pkg/cloudcfg/proxy_server.go @@ -17,33 +17,74 @@ limitations under the License. package cloudcfg import ( + "fmt" "io/ioutil" - "log" "net/http" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/client" ) type ProxyServer struct { Host string - Client *http.Client + Auth *client.AuthInfo + Client *client.Client +} + +func NewProxyServer(filebase, host string, auth *client.AuthInfo) *ProxyServer { + server := &ProxyServer{ + Host: host, + Auth: auth, + Client: client.New(host, auth), + } + fileServer := &fileServer{ + prefix: "/static/", + base: filebase, + } + http.Handle("/api/", server) + http.Handle("/static/", fileServer) + return server +} + +// Starts the server, loops forever. +func (s *ProxyServer) Serve() error { + return http.ListenAndServe(":8001", nil) +} + +func (s *ProxyServer) doError(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusInternalServerError) + w.Header().Add("Content-type", "application/json") + data, _ := api.Encode(api.Status{ + Status: api.StatusFailure, + Details: fmt.Sprintf("internal error: %#v", err), + }) + w.Write(data) } func (s *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - req, err := http.NewRequest(r.Method, s.Host+r.URL.Path, r.Body) - if err != nil { - log.Printf("Error: %#v", err) + result := s.Client.Verb(r.Method).Path(r.URL.Path).Do() + if result.Error() != nil { + s.doError(w, result.Error()) return } - res, err := s.Client.Do(req) + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-type", "application/json") + data, err := result.Raw() if err != nil { - log.Printf("Error: %#v", err) - return - } - w.WriteHeader(res.StatusCode) - defer res.Body.Close() - data, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("Error: %#v", err) + s.doError(w, err) return } w.Write(data) } + +type fileServer struct { + prefix string + base string +} + +func (f *fileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + filename := r.URL.Path[len(f.prefix):] + bytes, _ := ioutil.ReadFile(f.base + filename) + w.WriteHeader(http.StatusOK) + w.Write(bytes) +}