github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+incompatible/actor/v2action/route.go (about)

     1  package v2action
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
    10  	"code.cloudfoundry.org/cli/types"
    11  	log "github.com/sirupsen/logrus"
    12  )
    13  
    14  // OrphanedRoutesNotFoundError is an error wrapper that represents the case
    15  // when no orphaned routes are found.
    16  type OrphanedRoutesNotFoundError struct{}
    17  
    18  // Error method to display the error message.
    19  func (OrphanedRoutesNotFoundError) Error() string {
    20  	return "No orphaned routes were found."
    21  }
    22  
    23  // RouteInDifferentSpaceError is returned when the route exists in a different
    24  // space than the one requesting it.
    25  type RouteInDifferentSpaceError struct {
    26  	Route string
    27  }
    28  
    29  func (RouteInDifferentSpaceError) Error() string {
    30  	return "route registered to another space"
    31  }
    32  
    33  // RouteNotFoundError is returned when a route cannot be found
    34  type RouteNotFoundError struct {
    35  	Host       string
    36  	DomainGUID string
    37  }
    38  
    39  func (e RouteNotFoundError) Error() string {
    40  	return fmt.Sprintf("Route with host %s and domain guid %s not found", e.Host, e.DomainGUID)
    41  }
    42  
    43  // RouteAlreadyExistsError is returned when a route already exists
    44  type RouteAlreadyExistsError struct {
    45  	Route Route
    46  }
    47  
    48  func (e RouteAlreadyExistsError) Error() string {
    49  	return fmt.Sprintf("Route %s already exists", e.Route)
    50  }
    51  
    52  type Routes []Route
    53  
    54  func (rs Routes) Summary() string {
    55  	formattedRoutes := []string{}
    56  	for _, route := range rs {
    57  		formattedRoutes = append(formattedRoutes, route.String())
    58  	}
    59  	return strings.Join(formattedRoutes, ", ")
    60  }
    61  
    62  // Route represents a CLI Route.
    63  type Route struct {
    64  	Domain    Domain
    65  	GUID      string
    66  	Host      string
    67  	Path      string
    68  	Port      types.NullInt
    69  	SpaceGUID string
    70  }
    71  
    72  // String formats the route in a human readable format.
    73  func (r Route) String() string {
    74  	routeString := r.Domain.Name
    75  
    76  	if r.Port.IsSet {
    77  		routeString = fmt.Sprintf("%s:%d", routeString, r.Port.Value)
    78  	}
    79  
    80  	if r.Host != "" {
    81  		routeString = fmt.Sprintf("%s.%s", r.Host, routeString)
    82  	}
    83  
    84  	if r.Path != "" {
    85  		routeString = path.Join(routeString, r.Path)
    86  	}
    87  
    88  	return routeString
    89  }
    90  
    91  func (actor Actor) BindRouteToApplication(routeGUID string, appGUID string) (Warnings, error) {
    92  	_, warnings, err := actor.CloudControllerClient.BindRouteToApplication(routeGUID, appGUID)
    93  	if _, ok := err.(ccerror.InvalidRelationError); ok {
    94  		return Warnings(warnings), RouteInDifferentSpaceError{}
    95  	}
    96  	return Warnings(warnings), err
    97  }
    98  
    99  func (actor Actor) CreateRoute(route Route, generatePort bool) (Route, Warnings, error) {
   100  	if route.Path != "" && !strings.HasPrefix(route.Path, "/") {
   101  		route.Path = fmt.Sprintf("/%s", route.Path)
   102  	}
   103  	returnedRoute, warnings, err := actor.CloudControllerClient.CreateRoute(ActorToCCRoute(route), generatePort)
   104  	return CCToActorRoute(returnedRoute, route.Domain), Warnings(warnings), err
   105  }
   106  
   107  func (actor Actor) CreateRouteWithExistenceCheck(orgGUID string, spaceName string, route Route, generatePort bool) (Route, Warnings, error) {
   108  	space, warnings, spaceErr := actor.GetSpaceByOrganizationAndName(orgGUID, spaceName)
   109  	if spaceErr != nil {
   110  		return Route{}, Warnings(warnings), spaceErr
   111  	}
   112  	route.SpaceGUID = space.GUID
   113  
   114  	if route.Domain.GUID == "" {
   115  		domains, orgDomainWarnings, getDomainErr := actor.GetDomainsByNameAndOrganization([]string{route.Domain.Name}, orgGUID)
   116  		warnings = append(warnings, orgDomainWarnings...)
   117  		if getDomainErr != nil {
   118  			return Route{}, warnings, getDomainErr
   119  		} else if len(domains) == 0 {
   120  			return Route{}, warnings, DomainNotFoundError{Name: route.Domain.Name}
   121  		}
   122  		route.Domain.GUID = domains[0].GUID
   123  	}
   124  
   125  	foundRoute, spaceRouteWarnings, findErr := actor.FindRouteBoundToSpaceWithSettings(route)
   126  	warnings = append(warnings, spaceRouteWarnings...)
   127  	routeAlreadyExists := true
   128  	if _, ok := findErr.(RouteNotFoundError); ok {
   129  		routeAlreadyExists = false
   130  	} else if findErr != nil {
   131  		return Route{}, Warnings(warnings), findErr
   132  	}
   133  
   134  	if routeAlreadyExists {
   135  		return Route{}, Warnings(warnings), RouteAlreadyExistsError{Route: foundRoute}
   136  	}
   137  
   138  	createdRoute, createRouteWarnings, createErr := actor.CreateRoute(route, generatePort)
   139  	warnings = append(warnings, createRouteWarnings...)
   140  	if createErr != nil {
   141  		return Route{}, Warnings(warnings), createErr
   142  	}
   143  
   144  	return createdRoute, Warnings(warnings), nil
   145  }
   146  
   147  // GetOrphanedRoutesBySpace returns a list of orphaned routes associated with
   148  // the provided Space GUID.
   149  func (actor Actor) GetOrphanedRoutesBySpace(spaceGUID string) ([]Route, Warnings, error) {
   150  	var (
   151  		orphanedRoutes []Route
   152  		allWarnings    Warnings
   153  	)
   154  
   155  	routes, warnings, err := actor.GetSpaceRoutes(spaceGUID)
   156  	allWarnings = append(allWarnings, warnings...)
   157  	if err != nil {
   158  		return nil, allWarnings, err
   159  	}
   160  
   161  	for _, route := range routes {
   162  		apps, warnings, err := actor.GetRouteApplications(route.GUID)
   163  		allWarnings = append(allWarnings, warnings...)
   164  		if err != nil {
   165  			return nil, allWarnings, err
   166  		}
   167  
   168  		if len(apps) == 0 {
   169  			orphanedRoutes = append(orphanedRoutes, route)
   170  		}
   171  	}
   172  
   173  	if len(orphanedRoutes) == 0 {
   174  		return nil, allWarnings, OrphanedRoutesNotFoundError{}
   175  	}
   176  
   177  	return orphanedRoutes, allWarnings, nil
   178  }
   179  
   180  // GetApplicationRoutes returns a list of routes associated with the provided
   181  // Application GUID.
   182  func (actor Actor) GetApplicationRoutes(applicationGUID string) (Routes, Warnings, error) {
   183  	var allWarnings Warnings
   184  	ccv2Routes, warnings, err := actor.CloudControllerClient.GetApplicationRoutes(applicationGUID)
   185  	allWarnings = append(allWarnings, warnings...)
   186  	if err != nil {
   187  		return nil, allWarnings, err
   188  	}
   189  
   190  	routes, domainWarnings, err := actor.applyDomain(ccv2Routes)
   191  
   192  	return routes, append(allWarnings, domainWarnings...), err
   193  }
   194  
   195  // GetSpaceRoutes returns a list of routes associated with the provided Space
   196  // GUID.
   197  func (actor Actor) GetSpaceRoutes(spaceGUID string) ([]Route, Warnings, error) {
   198  	var allWarnings Warnings
   199  	ccv2Routes, warnings, err := actor.CloudControllerClient.GetSpaceRoutes(spaceGUID)
   200  	allWarnings = append(allWarnings, warnings...)
   201  	if err != nil {
   202  		return nil, allWarnings, err
   203  	}
   204  
   205  	routes, domainWarnings, err := actor.applyDomain(ccv2Routes)
   206  
   207  	return routes, append(allWarnings, domainWarnings...), err
   208  }
   209  
   210  // DeleteRoute deletes the Route associated with the provided Route GUID.
   211  func (actor Actor) DeleteRoute(routeGUID string) (Warnings, error) {
   212  	warnings, err := actor.CloudControllerClient.DeleteRoute(routeGUID)
   213  	return Warnings(warnings), err
   214  }
   215  
   216  func (actor Actor) CheckRoute(route Route) (bool, Warnings, error) {
   217  	exists, warnings, err := actor.CloudControllerClient.CheckRoute(ActorToCCRoute(route))
   218  	return exists, Warnings(warnings), err
   219  }
   220  
   221  // FindRouteBoundToSpaceWithSettings finds the route with the given host,
   222  // domain and space.  If it is unable to find the route, it will check if it
   223  // exists anywhere in the system. When the route exists in another space,
   224  // RouteInDifferentSpaceError is returned.
   225  func (actor Actor) FindRouteBoundToSpaceWithSettings(route Route) (Route, Warnings, error) {
   226  	// TODO: Use a more generic search mechanism to support path, port, and no host
   227  	existingRoute, warnings, err := actor.GetRouteByHostAndDomain(route.Host, route.Domain.GUID)
   228  	if routeNotFoundErr, ok := err.(RouteNotFoundError); ok {
   229  		// This check only works for API versions 2.55 or higher. It will return
   230  		// false for anything below that.
   231  		log.Infoln("checking route existence for: %s", route)
   232  		exists, checkRouteWarnings, chkErr := actor.CheckRoute(route)
   233  		if chkErr != nil {
   234  			log.Errorln("check route:", err)
   235  			return Route{}, append(Warnings(warnings), checkRouteWarnings...), chkErr
   236  		}
   237  
   238  		// This will happen if the route exists in a space to which the user does
   239  		// not have access.
   240  		if exists {
   241  			log.Errorf("unable to find route %s in current space", route.String())
   242  			return Route{}, append(Warnings(warnings), checkRouteWarnings...), RouteInDifferentSpaceError{Route: route.String()}
   243  		}
   244  
   245  		log.Warnf("negative existence check for route %s - returning partial route", route.String())
   246  		log.Debugf("partialRoute: %#v", route)
   247  		return Route{}, append(Warnings(warnings), checkRouteWarnings...), routeNotFoundErr
   248  	} else if err != nil {
   249  		log.Errorln("finding route:", err)
   250  		return Route{}, Warnings(warnings), err
   251  	}
   252  
   253  	if existingRoute.SpaceGUID != route.SpaceGUID {
   254  		log.WithFields(log.Fields{
   255  			"targeted_space_guid": route.SpaceGUID,
   256  			"existing_space_guid": existingRoute.SpaceGUID,
   257  		}).Errorf("route exists in different space the user has access to")
   258  		return Route{}, Warnings(warnings), RouteInDifferentSpaceError{Route: route.String()}
   259  	}
   260  
   261  	log.Debugf("found route: %#v", existingRoute)
   262  	return existingRoute, Warnings(warnings), err
   263  }
   264  
   265  // GetRouteByHostAndDomain returns the HTTP route with the matching host and
   266  // the associate domain GUID.
   267  func (actor Actor) GetRouteByHostAndDomain(host string, domainGUID string) (Route, Warnings, error) {
   268  	ccv2Routes, warnings, err := actor.CloudControllerClient.GetRoutes(
   269  		ccv2.Query{
   270  			Filter:   ccv2.HostFilter,
   271  			Operator: ccv2.EqualOperator,
   272  			Values:   []string{host},
   273  		},
   274  		ccv2.Query{
   275  			Filter:   ccv2.DomainGUIDFilter,
   276  			Operator: ccv2.EqualOperator,
   277  			Values:   []string{domainGUID},
   278  		},
   279  	)
   280  	if err != nil {
   281  		return Route{}, Warnings(warnings), err
   282  	}
   283  
   284  	if len(ccv2Routes) == 0 {
   285  		return Route{}, Warnings(warnings), RouteNotFoundError{Host: host, DomainGUID: domainGUID}
   286  	}
   287  
   288  	routes, domainWarnings, err := actor.applyDomain(ccv2Routes)
   289  	if err != nil {
   290  		return Route{}, append(Warnings(warnings), domainWarnings...), err
   291  	}
   292  
   293  	return routes[0], append(Warnings(warnings), domainWarnings...), err
   294  }
   295  
   296  func ActorToCCRoute(route Route) ccv2.Route {
   297  	return ccv2.Route{
   298  		DomainGUID: route.Domain.GUID,
   299  		GUID:       route.GUID,
   300  		Host:       route.Host,
   301  		Path:       route.Path,
   302  		Port:       route.Port,
   303  		SpaceGUID:  route.SpaceGUID,
   304  	}
   305  }
   306  
   307  func CCToActorRoute(ccv2Route ccv2.Route, domain Domain) Route {
   308  	return Route{
   309  		Domain:    domain,
   310  		GUID:      ccv2Route.GUID,
   311  		Host:      ccv2Route.Host,
   312  		Path:      ccv2Route.Path,
   313  		Port:      ccv2Route.Port,
   314  		SpaceGUID: ccv2Route.SpaceGUID,
   315  	}
   316  }
   317  
   318  func (actor Actor) applyDomain(ccv2Routes []ccv2.Route) (Routes, Warnings, error) {
   319  	var routes Routes
   320  	var allWarnings Warnings
   321  
   322  	for _, ccv2Route := range ccv2Routes {
   323  		domain, warnings, err := actor.GetDomain(ccv2Route.DomainGUID)
   324  		allWarnings = append(allWarnings, warnings...)
   325  		if err != nil {
   326  			return nil, allWarnings, err
   327  		}
   328  		routes = append(routes, CCToActorRoute(ccv2Route, domain))
   329  	}
   330  
   331  	return routes, allWarnings, nil
   332  }