github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/api/routes.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/url" 8 "strings" 9 10 "code.cloudfoundry.org/cli/cf/api/resources" 11 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 12 "code.cloudfoundry.org/cli/cf/errors" 13 "code.cloudfoundry.org/cli/cf/models" 14 "code.cloudfoundry.org/cli/cf/net" 15 "github.com/google/go-querystring/query" 16 ) 17 18 //go:generate counterfeiter . RouteRepository 19 20 type RouteRepository interface { 21 ListRoutes(cb func(models.Route) bool) (apiErr error) 22 ListAllRoutes(cb func(models.Route) bool) (apiErr error) 23 Find(host string, domain models.DomainFields, path string, port int) (route models.Route, apiErr error) 24 Create(host string, domain models.DomainFields, path string, port int, useRandomPort bool) (createdRoute models.Route, apiErr error) 25 CheckIfExists(host string, domain models.DomainFields, path string) (found bool, apiErr error) 26 CreateInSpace(host, path, domainGUID, spaceGUID string, port int, randomPort bool) (createdRoute models.Route, apiErr error) 27 Bind(routeGUID, appGUID string) (apiErr error) 28 Unbind(routeGUID, appGUID string) (apiErr error) 29 Delete(routeGUID string) (apiErr error) 30 } 31 32 type CloudControllerRouteRepository struct { 33 config coreconfig.Reader 34 gateway net.Gateway 35 } 36 37 func NewCloudControllerRouteRepository(config coreconfig.Reader, gateway net.Gateway) (repo CloudControllerRouteRepository) { 38 repo.config = config 39 repo.gateway = gateway 40 return 41 } 42 43 func (repo CloudControllerRouteRepository) ListRoutes(cb func(models.Route) bool) (apiErr error) { 44 return repo.gateway.ListPaginatedResources( 45 repo.config.APIEndpoint(), 46 fmt.Sprintf("/v2/spaces/%s/routes?inline-relations-depth=1", repo.config.SpaceFields().GUID), 47 resources.RouteResource{}, 48 func(resource interface{}) bool { 49 return cb(resource.(resources.RouteResource).ToModel()) 50 }) 51 } 52 53 func (repo CloudControllerRouteRepository) ListAllRoutes(cb func(models.Route) bool) (apiErr error) { 54 return repo.gateway.ListPaginatedResources( 55 repo.config.APIEndpoint(), 56 fmt.Sprintf("/v2/routes?q=organization_guid:%s&inline-relations-depth=1", repo.config.OrganizationFields().GUID), 57 resources.RouteResource{}, 58 func(resource interface{}) bool { 59 return cb(resource.(resources.RouteResource).ToModel()) 60 }) 61 } 62 63 func normalizedPath(path string) string { 64 if path != "" && !strings.HasPrefix(path, `/`) { 65 return `/` + path 66 } 67 68 return path 69 } 70 71 func queryStringForRouteSearch(host, guid, path string, port int) string { 72 args := []string{ 73 fmt.Sprintf("host:%s", host), 74 fmt.Sprintf("domain_guid:%s", guid), 75 } 76 77 if path != "" { 78 args = append(args, fmt.Sprintf("path:%s", normalizedPath(path))) 79 } 80 81 if port != 0 { 82 args = append(args, fmt.Sprintf("port:%d", port)) 83 } 84 85 return strings.Join(args, ";") 86 } 87 88 func (repo CloudControllerRouteRepository) Find(host string, domain models.DomainFields, path string, port int) (models.Route, error) { 89 var route models.Route 90 queryString := queryStringForRouteSearch(host, domain.GUID, path, port) 91 92 q := struct { 93 Query string `url:"q"` 94 InlineRelationsDepth int `url:"inline-relations-depth"` 95 }{queryString, 1} 96 97 opt, _ := query.Values(q) 98 99 found := false 100 apiErr := repo.gateway.ListPaginatedResources( 101 repo.config.APIEndpoint(), 102 fmt.Sprintf("/v2/routes?%s", opt.Encode()), 103 resources.RouteResource{}, 104 func(resource interface{}) bool { 105 keepSearching := true 106 route = resource.(resources.RouteResource).ToModel() 107 if doesNotMatchVersionSpecificAttributes(route, path, port) { 108 return keepSearching 109 } 110 111 found = true 112 return !keepSearching 113 }) 114 115 if apiErr == nil && !found { 116 apiErr = errors.NewModelNotFoundError("Route", host) 117 } 118 119 return route, apiErr 120 } 121 122 func doesNotMatchVersionSpecificAttributes(route models.Route, path string, port int) bool { 123 return normalizedPath(route.Path) != normalizedPath(path) || route.Port != port 124 } 125 126 func (repo CloudControllerRouteRepository) Create(host string, domain models.DomainFields, path string, port int, useRandomPort bool) (createdRoute models.Route, apiErr error) { 127 return repo.CreateInSpace(host, path, domain.GUID, repo.config.SpaceFields().GUID, port, useRandomPort) 128 } 129 130 func (repo CloudControllerRouteRepository) CheckIfExists(host string, domain models.DomainFields, path string) (bool, error) { 131 path = normalizedPath(path) 132 133 u, err := url.Parse(repo.config.APIEndpoint()) 134 if err != nil { 135 return false, err 136 } 137 138 u.Path = fmt.Sprintf("/v2/routes/reserved/domain/%s/host/%s", domain.GUID, host) 139 if path != "" { 140 q := u.Query() 141 q.Set("path", path) 142 u.RawQuery = q.Encode() 143 } 144 145 var rawResponse interface{} 146 err = repo.gateway.GetResource(u.String(), &rawResponse) 147 if err != nil { 148 if _, ok := err.(*errors.HTTPNotFoundError); ok { 149 return false, nil 150 } 151 return false, err 152 } 153 154 return true, nil 155 } 156 157 func (repo CloudControllerRouteRepository) CreateInSpace(host, path, domainGUID, spaceGUID string, port int, randomPort bool) (models.Route, error) { 158 path = normalizedPath(path) 159 160 body := struct { 161 Host string `json:"host,omitempty"` 162 Path string `json:"path,omitempty"` 163 Port int `json:"port,omitempty"` 164 DomainGUID string `json:"domain_guid"` 165 SpaceGUID string `json:"space_guid"` 166 }{host, path, port, domainGUID, spaceGUID} 167 168 data, err := json.Marshal(body) 169 if err != nil { 170 return models.Route{}, err 171 } 172 173 q := struct { 174 GeneratePort bool `url:"generate_port,omitempty"` 175 InlineRelationsDepth int `url:"inline-relations-depth"` 176 }{randomPort, 1} 177 178 opt, _ := query.Values(q) 179 uriFragment := "/v2/routes?" + opt.Encode() 180 181 resource := new(resources.RouteResource) 182 err = repo.gateway.CreateResource( 183 repo.config.APIEndpoint(), 184 uriFragment, 185 bytes.NewReader(data), 186 resource, 187 ) 188 if err != nil { 189 return models.Route{}, err 190 } 191 192 return resource.ToModel(), nil 193 } 194 195 func (repo CloudControllerRouteRepository) Bind(routeGUID, appGUID string) (apiErr error) { 196 path := fmt.Sprintf("/v2/apps/%s/routes/%s", appGUID, routeGUID) 197 return repo.gateway.UpdateResource(repo.config.APIEndpoint(), path, nil) 198 } 199 200 func (repo CloudControllerRouteRepository) Unbind(routeGUID, appGUID string) (apiErr error) { 201 path := fmt.Sprintf("/v2/apps/%s/routes/%s", appGUID, routeGUID) 202 return repo.gateway.DeleteResource(repo.config.APIEndpoint(), path) 203 } 204 205 func (repo CloudControllerRouteRepository) Delete(routeGUID string) (apiErr error) { 206 path := fmt.Sprintf("/v2/routes/%s", routeGUID) 207 return repo.gateway.DeleteResource(repo.config.APIEndpoint(), path) 208 }