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 }
func (e *etcd) DeleteTcpRouteMapping(tcpMapping models.TcpRouteMapping) error { key := generateTcpRouteMappingKey(tcpMapping) deleteOpt := &client.DeleteOptions{} _, err := e.keysAPI.Delete(context.Background(), key, deleteOpt) if err != nil { cerr, ok := err.(client.Error) if ok && cerr.Code == client.ErrorCodeKeyNotFound { err = DBError{Type: KeyNotFound, Message: "The specified route (" + tcpMapping.String() + ") could not be found."} } } return err }
func validateTcpRouteMapping(tcpRouteMapping models.TcpRouteMapping, checkTTL bool, maxTTL uint16) *routing_api.Error { if tcpRouteMapping.TcpRoute.RouterGroupGuid == "" { err := routing_api.NewError(routing_api.TcpRouteMappingInvalidError, "Each tcp mapping requires a non empty router group guid. RouteMapping=["+tcpRouteMapping.String()+"]") return &err } if tcpRouteMapping.TcpRoute.ExternalPort <= 0 { err := routing_api.NewError(routing_api.TcpRouteMappingInvalidError, "Each tcp mapping requires a positive external port. RouteMapping=["+tcpRouteMapping.String()+"]") return &err } if tcpRouteMapping.HostIP == "" { err := routing_api.NewError(routing_api.TcpRouteMappingInvalidError, "Each tcp mapping requires a non empty backend ip. RouteMapping=["+tcpRouteMapping.String()+"]") return &err } if tcpRouteMapping.HostPort <= 0 { err := routing_api.NewError(routing_api.TcpRouteMappingInvalidError, "Each tcp mapping requires a positive backend port. RouteMapping=["+tcpRouteMapping.String()+"]") return &err } if checkTTL && tcpRouteMapping.TTL > maxTTL { err := routing_api.NewError(routing_api.TcpRouteMappingInvalidError, "Each tcp mapping requires TTL to be less than or equal to "+strconv.Itoa(int(maxTTL))+". RouteMapping=["+tcpRouteMapping.String()+"]") return &err } if checkTTL && tcpRouteMapping.TTL <= 0 { err := routing_api.NewError(routing_api.TcpRouteMappingInvalidError, "Each tcp route mapping requires a ttl greater than 0") return &err } return nil }
Expect(err.Error()).To(Equal("Each route request requires a port greater than 0")) }) It("returns an error if any request does not have an IP", func() { routes[1].IP = "" err := validator.ValidateDelete(routes) Expect(err.Type).To(Equal(routing_api.RouteInvalidError)) Expect(err.Error()).To(Equal("Each route request requires an IP")) }) }) }) Describe("ValidateCreateTcpRouteMapping", func() { var ( tcpMapping models.TcpRouteMapping routerGroups models.RouterGroups ) BeforeEach(func() { routerGroups = models.RouterGroups{ { Guid: DefaultRouterGroupGuid, Name: "default-tcp", Type: "tcp", ReservablePorts: "1024-65535", }, } tcpMapping = models.NewTcpRouteMapping(DefaultRouterGroupGuid, 52000, "1.2.3.4", 60000, 60) }) Context("when valid tcp mapping is passed", func() {
BeforeEach(func() { fakeKeysAPI.DeleteReturns(nil, errors.New("some network error")) }) It("returns network error", func() { Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("some network error")) Expect(fakeKeysAPI.DeleteCallCount()).To(Equal(1)) }) }) }) }) Describe("Tcp Mappings", func() { var ( tcpMapping models.TcpRouteMapping ) BeforeEach(func() { tcpMapping = models.NewTcpRouteMapping("router-group-guid-001", 52000, "1.2.3.4", 60000, 50) }) Describe("SaveTcpRouteMapping", func() { Context("when there's no existing entry", func() { BeforeEach(func() { keyNotFoundError := client.Error{Code: client.ErrorCodeKeyNotFound} fakeKeysAPI.GetReturns(nil, keyNotFoundError) }) It("Creates a mapping if none exist", func() { err := fakeEtcd.SaveTcpRouteMapping(tcpMapping)