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  }