From 1101c00014574d2680a7053643a411f6927c5884 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Fri, 1 Aug 2014 14:14:33 -0700 Subject: [PATCH] Make updates atomic from the client side. --- cmd/kubecfg/kubecfg.go | 31 ++++++++++++++++++++++++++++++- pkg/client/client.go | 18 ++++++------------ pkg/client/client_test.go | 11 ++++++----- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/cmd/kubecfg/kubecfg.go b/cmd/kubecfg/kubecfg.go index 3aeed28e04..9b6a9dc2d2 100644 --- a/cmd/kubecfg/kubecfg.go +++ b/cmd/kubecfg/kubecfg.go @@ -29,6 +29,7 @@ import ( "text/template" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" kube_client "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -202,6 +203,7 @@ func executeAPIRequest(method string, s *kube_client.Client) bool { validStorage := checkStorage(storage) verb := "" setBody := false + var version uint64 switch method { case "get": verb = "GET" @@ -225,6 +227,15 @@ func executeAPIRequest(method string, s *kube_client.Client) bool { glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>", method, prettyWireStorage()) } case "update": + obj, err := s.Verb("GET").Path(path).Do().Get() + if err != nil { + glog.Fatalf("error obtaining resource version for update: %v", err) + } + jsonBase, err := api.FindJSONBase(obj) + if err != nil { + glog.Fatalf("error finding json base for update: %v", err) + } + version = jsonBase.ResourceVersion() verb = "PUT" setBody = true if !validStorage || !hasSuffix { @@ -238,7 +249,25 @@ func executeAPIRequest(method string, s *kube_client.Client) bool { Path(path). ParseSelector(*selector) if setBody { - r.Body(readConfig(storage)) + if version != 0 { + data := readConfig(storage) + obj, err := api.Decode(data) + if err != nil { + glog.Fatalf("error setting resource version: %v", err) + } + jsonBase, err := api.FindJSONBase(obj) + if err != nil { + glog.Fatalf("error setting resource version: %v", err) + } + jsonBase.SetResourceVersion(version) + data, err = api.Encode(obj) + if err != nil { + glog.Fatalf("error setting resource version: %v", err) + } + r.Body(data) + } else { + r.Body(readConfig(storage)) + } } result := r.Do() obj, err := result.Get() diff --git a/pkg/client/client.go b/pkg/client/client.go index 76b76e33e8..3a41f8613d 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -192,12 +192,10 @@ func (c *Client) CreatePod(pod api.Pod) (result api.Pod, err error) { // UpdatePod takes the representation of a pod to update. Returns the server's representation of the pod, and an error, if it occurs func (c *Client) UpdatePod(pod api.Pod) (result api.Pod, err error) { - var prev api.Pod - err = c.Get().Path("pod").Path(pod.ID).Do().Into(&prev) - if err != nil { + if pod.ResourceVersion == 0 { + err = fmt.Errorf("invalid update object, missing resource version: %v", pod) return } - pod.ResourceVersion = prev.ResourceVersion err = c.Put().Path("pods").Path(pod.ID).Body(pod).Do().Into(&result) return } @@ -222,12 +220,10 @@ func (c *Client) CreateReplicationController(controller api.ReplicationControlle // UpdateReplicationController updates an existing replication controller func (c *Client) UpdateReplicationController(controller api.ReplicationController) (result api.ReplicationController, err error) { - var prev api.ReplicationController - err = c.Get().Path("replicationControllers").Path(controller.ID).Do().Into(&prev) - if err != nil { + if controller.ResourceVersion == 0 { + err = fmt.Errorf("invalid update object, missing resource version: %v", controller) return } - controller.ResourceVersion = prev.ResourceVersion err = c.Put().Path("replicationControllers").Path(controller.ID).Body(controller).Do().Into(&result) return } @@ -251,12 +247,10 @@ func (c *Client) CreateService(svc api.Service) (result api.Service, err error) // UpdateService updates an existing service. func (c *Client) UpdateService(svc api.Service) (result api.Service, err error) { - var prev api.Service - err = c.Get().Path("services").Path(svc.ID).Do().Into(&prev) - if err != nil { + if svc.ResourceVersion == 0 { + err = fmt.Errorf("invalid update object, missing resource version: %v", svc) return } - svc.ResourceVersion = prev.ResourceVersion err = c.Put().Path("services").Path(svc.ID).Body(svc).Do().Into(&result) return } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 5ed763b8b8..0a10d904af 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -154,7 +154,7 @@ func TestCreatePod(t *testing.T) { func TestUpdatePod(t *testing.T) { requestPod := api.Pod{ - JSONBase: api.JSONBase{ID: "foo"}, + JSONBase: api.JSONBase{ID: "foo", ResourceVersion: 1}, CurrentState: api.PodState{ Status: "Foobar", }, @@ -219,7 +219,7 @@ func TestGetController(t *testing.T) { func TestUpdateController(t *testing.T) { requestController := api.ReplicationController{ - JSONBase: api.JSONBase{ID: "foo"}, + JSONBase: api.JSONBase{ID: "foo", ResourceVersion: 1}, } c := &testClient{ Request: testRequest{Method: "PUT", Path: "/replicationControllers/foo"}, @@ -388,11 +388,12 @@ func TestCreateService(t *testing.T) { } func TestUpdateService(t *testing.T) { + svc := api.Service{JSONBase: api.JSONBase{ID: "service-1", ResourceVersion: 1}} c := &testClient{ - Request: testRequest{Method: "PUT", Path: "/services/service-1", Body: &api.Service{JSONBase: api.JSONBase{ID: "service-1"}}}, - Response: Response{StatusCode: 200, Body: &api.Service{JSONBase: api.JSONBase{ID: "service-1"}}}, + Request: testRequest{Method: "PUT", Path: "/services/service-1", Body: &svc}, + Response: Response{StatusCode: 200, Body: &svc}, } - response, err := c.Setup().UpdateService(api.Service{JSONBase: api.JSONBase{ID: "service-1"}}) + response, err := c.Setup().UpdateService(svc) c.Validate(t, &response, err) }