package cfclient import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "github.com/pkg/errors" ) type RoutesResponse struct { Count int `json:"total_results"` Pages int `json:"total_pages"` NextUrl string `json:"next_url"` Resources []RoutesResource `json:"resources"` } type RoutesResource struct { Meta Meta `json:"metadata"` Entity Route `json:"entity"` } type RouteRequest struct { DomainGuid string `json:"domain_guid"` SpaceGuid string `json:"space_guid"` Host string `json:"host"` // required for http routes Path string `json:"path"` Port int `json:"port"` } type Route struct { Guid string `json:"guid"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` Host string `json:"host"` Path string `json:"path"` DomainGuid string `json:"domain_guid"` SpaceGuid string `json:"space_guid"` ServiceInstanceGuid string `json:"service_instance_guid"` Port int `json:"port"` c *Client } // CreateRoute creates a regular http route func (c *Client) CreateRoute(routeRequest RouteRequest) (Route, error) { routesResource, err := c.createRoute("/v2/routes", routeRequest) if nil != err { return Route{}, err } return c.mergeRouteResource(routesResource), nil } // CreateTcpRoute creates a TCP route func (c *Client) CreateTcpRoute(routeRequest RouteRequest) (Route, error) { routesResource, err := c.createRoute("/v2/routes?generate_port=true", routeRequest) if nil != err { return Route{}, err } return c.mergeRouteResource(routesResource), nil } // BindRoute associates the specified route with the application func (c *Client) BindRoute(routeGUID, appGUID string) error { resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/routes/%s/apps/%s", routeGUID, appGUID))) if err != nil { return errors.Wrapf(err, "Error binding route %s to app %s", routeGUID, appGUID) } if resp.StatusCode != http.StatusCreated { return fmt.Errorf("Error binding route %s to app %s, response code: %d", routeGUID, appGUID, resp.StatusCode) } return nil } func (c *Client) ListRoutesByQuery(query url.Values) ([]Route, error) { return c.fetchRoutes("/v2/routes?" + query.Encode()) } func (c *Client) fetchRoutes(requestUrl string) ([]Route, error) { var routes []Route for { routesResp, err := c.getRoutesResponse(requestUrl) if err != nil { return []Route{}, err } for _, route := range routesResp.Resources { route.Entity.Guid = route.Meta.Guid route.Entity.CreatedAt = route.Meta.CreatedAt route.Entity.UpdatedAt = route.Meta.UpdatedAt route.Entity.c = c routes = append(routes, route.Entity) } requestUrl = routesResp.NextUrl if requestUrl == "" { break } } return routes, nil } func (c *Client) ListRoutes() ([]Route, error) { return c.ListRoutesByQuery(nil) } func (c *Client) getRoutesResponse(requestUrl string) (RoutesResponse, error) { var routesResp RoutesResponse r := c.NewRequest("GET", requestUrl) resp, err := c.DoRequest(r) if err != nil { return RoutesResponse{}, errors.Wrap(err, "Error requesting routes") } resBody, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return RoutesResponse{}, errors.Wrap(err, "Error reading routes body") } err = json.Unmarshal(resBody, &routesResp) if err != nil { return RoutesResponse{}, errors.Wrap(err, "Error unmarshalling routes") } return routesResp, nil } func (c *Client) createRoute(requestUrl string, routeRequest RouteRequest) (RoutesResource, error) { var routeResp RoutesResource buf := bytes.NewBuffer(nil) err := json.NewEncoder(buf).Encode(routeRequest) if err != nil { return RoutesResource{}, errors.Wrap(err, "Error creating route - failed to serialize request body") } r := c.NewRequestWithBody("POST", requestUrl, buf) resp, err := c.DoRequest(r) if err != nil { return RoutesResource{}, errors.Wrap(err, "Error creating route") } resBody, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return RoutesResource{}, errors.Wrap(err, "Error creating route") } err = json.Unmarshal(resBody, &routeResp) if err != nil { return RoutesResource{}, errors.Wrap(err, "Error unmarshalling routes") } routeResp.Entity.c = c return routeResp, nil } func (c *Client) DeleteRoute(guid string) error { resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/routes/%s", guid))) if err != nil { return err } if resp.StatusCode != http.StatusNoContent { return errors.Wrapf(err, "Error deleting route %s, response code: %d", guid, resp.StatusCode) } return nil } func (c *Client) mergeRouteResource(rr RoutesResource) Route { rr.Entity.Guid = rr.Meta.Guid rr.Entity.CreatedAt = rr.Meta.CreatedAt rr.Entity.UpdatedAt = rr.Meta.UpdatedAt rr.Entity.c = c return rr.Entity }