func (e *etcd) SaveTcpRouteMapping(tcpMapping models.TcpRouteMapping) error { key := generateTcpRouteMappingKey(tcpMapping) retries := 0 for retries <= maxRetries { response, err := e.keysAPI.Get(context.Background(), key, readOpts()) // Update if response != nil && err == nil { var existingTcpRouteMapping models.TcpRouteMapping err = json.Unmarshal([]byte(response.Node.Value), &existingTcpRouteMapping) if err != nil { return err } tcpMapping.ModificationTag = existingTcpRouteMapping.ModificationTag tcpMapping.ModificationTag.Increment() tcpRouteJSON, _ := json.Marshal(tcpMapping) _, err = e.keysAPI.Set(ctx(), key, string(tcpRouteJSON), updateOptsWithTTL(int(tcpMapping.TTL), response.Node.ModifiedIndex)) } else if cerr, ok := err.(client.Error); ok && cerr.Code == client.ErrorCodeKeyNotFound { //create // Delete came in between a read and update if retries > 0 { return ErrorConflict } var tag models.ModificationTag tag, err = models.NewModificationTag() if err != nil { return err } tcpMapping.ModificationTag = tag tcpRouteMappingJSON, _ := json.Marshal(tcpMapping) _, err = e.keysAPI.Set(ctx(), key, string(tcpRouteMappingJSON), createOpts(int(tcpMapping.TTL))) } // return when create or update is successful if err == nil { return nil } // only retry on a compare and swap error if cerr, ok := err.(client.Error); ok && cerr.Code == client.ErrorCodeTestFailed { retries++ } else { return err } } // number of retries exceeded return ErrorConflict }
Expect(err).NotTo(HaveOccurred()) Expect(fakeKeysAPI.GetCallCount()).To(Equal(1)) Expect(fakeKeysAPI.SetCallCount()).To(Equal(1)) _, _, json, opts := fakeKeysAPI.SetArgsForCall(0) Expect(json).To(ContainSubstring("\"index\":0")) Expect(json).To(ContainSubstring(`"router_group_guid":"router-group-guid-001"`)) Expect(json).To(ContainSubstring(`"port":52000`)) Expect(json).To(ContainSubstring(`"backend_port":60000`)) Expect(json).To(ContainSubstring(`"backend_ip":"1.2.3.4"`)) Expect(json).To(ContainSubstring(`"ttl":50`)) Expect(opts.TTL).To(Equal(50 * time.Second)) }) Context("when an entry already exists", func() { BeforeEach(func() { tcpMapping.ModificationTag = models.ModificationTag{Guid: "guid", Index: 5} tcpRouteJson, err := json.Marshal(&tcpMapping) Expect(err).ToNot(HaveOccurred()) fakeResp := &client.Response{Node: &client.Node{Value: string(tcpRouteJson)}} fakeKeysAPI.GetReturns(fakeResp, nil) }) It("Updates the route and increments the tag index", func() { newTcpMapping := models.NewTcpRouteMapping("router-group-guid-001", 52000, "1.2.3.4", 60000, 50) err := fakeEtcd.SaveTcpRouteMapping(newTcpMapping) Expect(err).NotTo(HaveOccurred()) Expect(fakeKeysAPI.GetCallCount()).To(Equal(1)) Expect(fakeKeysAPI.SetCallCount()).To(Equal(1)) _, _, json, _ := fakeKeysAPI.SetArgsForCall(0) Expect(json).To(ContainSubstring("\"index\":6")) })