diff --git a/cmd/cloudcfg/cloudcfg.go b/cmd/cloudcfg/cloudcfg.go index f17bf611a3..e694583f28 100644 --- a/cmd/cloudcfg/cloudcfg.go +++ b/cmd/cloudcfg/cloudcfg.go @@ -46,6 +46,8 @@ var ( json = flag.Bool("json", false, "If true, print raw JSON for responses") 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() { @@ -96,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 { @@ -118,6 +115,18 @@ func main() { } } + if *proxy { + 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/hooks/boilerplate.sh b/hooks/boilerplate.sh index 33c31cbf5b..17d049b9cf 100755 --- a/hooks/boilerplate.sh +++ b/hooks/boilerplate.sh @@ -6,6 +6,7 @@ LINES=$(cat "$(dirname $0)/boilerplate.txt" | wc -l) DIFFER=$(head -$LINES "${FILE}" | diff -q - "$(dirname $0)/boilerplate.txt") if [[ -z "${DIFFER}" ]]; then + echo "${DIFFER}" echo "1" exit 0 fi diff --git a/pkg/api/types.go b/pkg/api/types.go index a9d15a3f47..f0e43638bb 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 a8ad1da0bb..30bd13ccb8 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 cafaaddaa4..b4ca4b807a 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 b016fccca3..e8e57d4128 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 new file mode 100644 index 0000000000..f0ae0bbda7 --- /dev/null +++ b/pkg/cloudcfg/proxy_server.go @@ -0,0 +1,90 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloudcfg + +import ( + "fmt" + "io/ioutil" + "net/http" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/client" +) + +type ProxyServer struct { + Host string + 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) { + result := s.Client.Verb(r.Method).Path(r.URL.Path).Do() + if result.Error() != nil { + s.doError(w, result.Error()) + return + } + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-type", "application/json") + data, err := result.Raw() + if err != nil { + 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) +}