github.com/cloudfoundry/cli@v7.1.0+incompatible/actor/v7action/route.go (about)

     1  package v7action
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    12  	"code.cloudfoundry.org/cli/resources"
    13  	"code.cloudfoundry.org/cli/util/sorting"
    14  )
    15  
    16  type RouteSummary struct {
    17  	resources.Route
    18  	AppNames   []string
    19  	DomainName string
    20  	SpaceName  string
    21  }
    22  
    23  func (actor Actor) CreateRoute(spaceGUID, domainName, hostname, path string, port int) (resources.Route, Warnings, error) {
    24  	allWarnings := Warnings{}
    25  	domain, warnings, err := actor.GetDomainByName(domainName)
    26  	allWarnings = append(allWarnings, warnings...)
    27  
    28  	if err != nil {
    29  		return resources.Route{}, allWarnings, err
    30  	}
    31  
    32  	route, apiWarnings, err := actor.CloudControllerClient.CreateRoute(resources.Route{
    33  		SpaceGUID:  spaceGUID,
    34  		DomainGUID: domain.GUID,
    35  		Host:       hostname,
    36  		Path:       path,
    37  		Port:       port,
    38  	})
    39  
    40  	actorWarnings := Warnings(apiWarnings)
    41  	allWarnings = append(allWarnings, actorWarnings...)
    42  
    43  	if _, ok := err.(ccerror.RouteNotUniqueError); ok {
    44  		return resources.Route{}, allWarnings, actionerror.RouteAlreadyExistsError{Err: err}
    45  	}
    46  
    47  	return route, allWarnings, err
    48  }
    49  
    50  func (actor Actor) GetRouteDestinations(routeGUID string) ([]resources.RouteDestination, Warnings, error) {
    51  	destinations, warnings, err := actor.CloudControllerClient.GetRouteDestinations(routeGUID)
    52  
    53  	var actorDestinations []resources.RouteDestination
    54  	for _, dst := range destinations {
    55  		actorDestinations = append(actorDestinations, resources.RouteDestination{
    56  			GUID: dst.GUID,
    57  			App:  resources.RouteDestinationApp(dst.App),
    58  		})
    59  	}
    60  
    61  	return actorDestinations, Warnings(warnings), err
    62  }
    63  
    64  func (actor Actor) GetRouteDestinationByAppGUID(route resources.Route, appGUID string) (resources.RouteDestination, error) {
    65  	for _, destination := range route.Destinations {
    66  		if destination.App.GUID == appGUID && destination.App.Process.Type == constant.ProcessTypeWeb {
    67  			return destination, nil
    68  		}
    69  	}
    70  
    71  	return resources.RouteDestination{}, actionerror.RouteDestinationNotFoundError{
    72  		AppGUID:     appGUID,
    73  		ProcessType: constant.ProcessTypeWeb,
    74  		RouteGUID:   route.GUID,
    75  	}
    76  }
    77  
    78  func (actor Actor) GetRoutesBySpace(spaceGUID string, labelSelector string) ([]resources.Route, Warnings, error) {
    79  	allWarnings := Warnings{}
    80  	queries := []ccv3.Query{
    81  		ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}},
    82  	}
    83  	if len(labelSelector) > 0 {
    84  		queries = append(queries, ccv3.Query{Key: ccv3.LabelSelectorFilter, Values: []string{labelSelector}})
    85  	}
    86  
    87  	routes, warnings, err := actor.CloudControllerClient.GetRoutes(queries...)
    88  	allWarnings = append(allWarnings, warnings...)
    89  	if err != nil {
    90  		return nil, allWarnings, err
    91  	}
    92  
    93  	return routes, allWarnings, nil
    94  }
    95  
    96  func (actor Actor) parseRoutePath(routePath string) (string, string, string, resources.Domain, Warnings, error) {
    97  	var (
    98  		warnings Warnings
    99  		host     string
   100  		path     string
   101  		port     string
   102  	)
   103  
   104  	routeParts := strings.SplitN(routePath, "/", 2)
   105  	domainAndPort := routeParts[0]
   106  	if len(routeParts) > 1 {
   107  		path = "/" + routeParts[1]
   108  	}
   109  
   110  	domainAndPortParts := strings.SplitN(domainAndPort, ":", 2)
   111  	domainName := domainAndPortParts[0]
   112  	if len(domainAndPortParts) > 1 {
   113  		port = domainAndPortParts[1]
   114  	}
   115  
   116  	domain, allWarnings, err := actor.GetDomainByName(domainName)
   117  
   118  	if err != nil {
   119  		_, domainNotFound := err.(actionerror.DomainNotFoundError)
   120  		domainParts := strings.SplitN(domainName, ".", 2)
   121  		needToCheckForHost := domainNotFound && len(domainParts) > 1
   122  
   123  		if !needToCheckForHost {
   124  			return "", "", "", resources.Domain{}, allWarnings, err
   125  		}
   126  
   127  		host = domainParts[0]
   128  		domainName = domainParts[1]
   129  		domain, warnings, err = actor.GetDomainByName(domainName)
   130  		allWarnings = append(allWarnings, warnings...)
   131  		if err != nil {
   132  			return "", "", "", resources.Domain{}, allWarnings, err
   133  		}
   134  	}
   135  
   136  	return host, path, port, domain, allWarnings, err
   137  }
   138  
   139  func (actor Actor) GetRoute(routePath string, spaceGUID string) (resources.Route, Warnings, error) {
   140  	filters := []ccv3.Query{
   141  		{
   142  			Key:    ccv3.SpaceGUIDFilter,
   143  			Values: []string{spaceGUID},
   144  		},
   145  	}
   146  
   147  	host, path, port, domain, allWarnings, err := actor.parseRoutePath(routePath)
   148  	if err != nil {
   149  		return resources.Route{}, allWarnings, err
   150  	}
   151  
   152  	filters = append(filters, ccv3.Query{Key: ccv3.DomainGUIDFilter,
   153  		Values: []string{domain.GUID},
   154  	})
   155  	filters = append(filters, ccv3.Query{Key: ccv3.HostsFilter,
   156  		Values: []string{host},
   157  	})
   158  	filters = append(filters, ccv3.Query{Key: ccv3.PathsFilter,
   159  		Values: []string{path},
   160  	})
   161  
   162  	if domain.IsTCP() {
   163  		filters = append(filters, ccv3.Query{Key: ccv3.PortsFilter,
   164  			Values: []string{port},
   165  		})
   166  	}
   167  
   168  	routes, warnings, err := actor.CloudControllerClient.GetRoutes(filters...)
   169  	allWarnings = append(allWarnings, warnings...)
   170  	if err != nil {
   171  		return resources.Route{}, allWarnings, err
   172  	}
   173  	if len(routes) == 0 {
   174  		return resources.Route{}, allWarnings, actionerror.RouteNotFoundError{
   175  			Host:       host,
   176  			DomainName: domain.Name,
   177  			Path:       path,
   178  		}
   179  	}
   180  
   181  	return routes[0], allWarnings, nil
   182  }
   183  
   184  func (actor Actor) GetRoutesByOrg(orgGUID string, labelSelector string) ([]resources.Route, Warnings, error) {
   185  	allWarnings := Warnings{}
   186  	queries := []ccv3.Query{
   187  		ccv3.Query{Key: ccv3.OrganizationGUIDFilter, Values: []string{orgGUID}},
   188  	}
   189  	if len(labelSelector) > 0 {
   190  		queries = append(queries, ccv3.Query{Key: ccv3.LabelSelectorFilter, Values: []string{labelSelector}})
   191  	}
   192  
   193  	routes, warnings, err := actor.CloudControllerClient.GetRoutes(queries...)
   194  	allWarnings = append(allWarnings, warnings...)
   195  	if err != nil {
   196  		return nil, allWarnings, err
   197  	}
   198  
   199  	return routes, allWarnings, nil
   200  }
   201  
   202  func (actor Actor) GetRouteSummaries(routes []resources.Route) ([]RouteSummary, Warnings, error) {
   203  	var allWarnings Warnings
   204  	var routeSummaries []RouteSummary
   205  
   206  	destinationAppGUIDsByRouteGUID := make(map[string][]string)
   207  	destinationAppGUIDs := make(map[string]bool)
   208  	var uniqueAppGUIDs []string
   209  
   210  	spaceGUIDs := make(map[string]struct{})
   211  	var uniqueSpaceGUIDs []string
   212  
   213  	for _, route := range routes {
   214  		if _, seen := spaceGUIDs[route.SpaceGUID]; !seen {
   215  			spaceGUIDs[route.SpaceGUID] = struct{}{}
   216  			uniqueSpaceGUIDs = append(uniqueSpaceGUIDs, route.SpaceGUID)
   217  		}
   218  
   219  		for _, destination := range route.Destinations {
   220  			appGUID := destination.App.GUID
   221  
   222  			if _, ok := destinationAppGUIDs[appGUID]; !ok {
   223  				destinationAppGUIDs[appGUID] = true
   224  				uniqueAppGUIDs = append(uniqueAppGUIDs, appGUID)
   225  			}
   226  
   227  			destinationAppGUIDsByRouteGUID[route.GUID] = append(destinationAppGUIDsByRouteGUID[route.GUID], appGUID)
   228  		}
   229  	}
   230  
   231  	spaces, _, ccv3Warnings, err := actor.CloudControllerClient.GetSpaces(ccv3.Query{
   232  		Key:    ccv3.GUIDFilter,
   233  		Values: uniqueSpaceGUIDs,
   234  	})
   235  	allWarnings = append(allWarnings, ccv3Warnings...)
   236  	if err != nil {
   237  		return nil, allWarnings, err
   238  	}
   239  
   240  	spaceNamesByGUID := make(map[string]string)
   241  	for _, space := range spaces {
   242  		spaceNamesByGUID[space.GUID] = space.Name
   243  	}
   244  
   245  	apps, warnings, err := actor.GetApplicationsByGUIDs(uniqueAppGUIDs)
   246  	allWarnings = append(allWarnings, warnings...)
   247  	if err != nil {
   248  		return nil, allWarnings, err
   249  	}
   250  
   251  	appNamesByGUID := make(map[string]string)
   252  	for _, app := range apps {
   253  		appNamesByGUID[app.GUID] = app.Name
   254  	}
   255  
   256  	for _, route := range routes {
   257  		var appNames []string
   258  
   259  		appGUIDs := destinationAppGUIDsByRouteGUID[route.GUID]
   260  		for _, appGUID := range appGUIDs {
   261  			appNames = append(appNames, appNamesByGUID[appGUID])
   262  		}
   263  
   264  		routeSummaries = append(routeSummaries, RouteSummary{
   265  			Route:      route,
   266  			AppNames:   appNames,
   267  			SpaceName:  spaceNamesByGUID[route.SpaceGUID],
   268  			DomainName: getDomainName(route.URL, route.Host, route.Path, route.Port),
   269  		})
   270  	}
   271  
   272  	sort.Slice(routeSummaries, func(i, j int) bool {
   273  		return sorting.LessIgnoreCase(routeSummaries[i].SpaceName, routeSummaries[j].SpaceName)
   274  	})
   275  
   276  	return routeSummaries, allWarnings, nil
   277  }
   278  
   279  func (actor Actor) DeleteOrphanedRoutes(spaceGUID string) (Warnings, error) {
   280  	var allWarnings Warnings
   281  
   282  	jobURL, warnings, err := actor.CloudControllerClient.DeleteOrphanedRoutes(spaceGUID)
   283  	allWarnings = append(allWarnings, warnings...)
   284  	if err != nil {
   285  		return allWarnings, err
   286  	}
   287  
   288  	warnings, err = actor.CloudControllerClient.PollJob(jobURL)
   289  	allWarnings = append(allWarnings, warnings...)
   290  
   291  	return allWarnings, err
   292  }
   293  
   294  func (actor Actor) DeleteRoute(domainName, hostname, path string, port int) (Warnings, error) {
   295  	allWarnings := Warnings{}
   296  	domain, warnings, err := actor.GetDomainByName(domainName)
   297  	allWarnings = append(allWarnings, warnings...)
   298  
   299  	if _, ok := err.(actionerror.DomainNotFoundError); ok {
   300  		allWarnings = append(allWarnings, err.Error())
   301  		return allWarnings, nil
   302  	}
   303  
   304  	if err != nil {
   305  		return allWarnings, err
   306  	}
   307  
   308  	route, actorWarnings, err := actor.GetRouteByAttributes(domain, hostname, path, port)
   309  
   310  	allWarnings = append(allWarnings, actorWarnings...)
   311  
   312  	if err != nil {
   313  		return allWarnings, err
   314  	}
   315  
   316  	jobURL, apiWarnings, err := actor.CloudControllerClient.DeleteRoute(route.GUID)
   317  	actorWarnings = Warnings(apiWarnings)
   318  	allWarnings = append(allWarnings, actorWarnings...)
   319  
   320  	if err != nil {
   321  		return allWarnings, err
   322  	}
   323  
   324  	pollJobWarnings, err := actor.CloudControllerClient.PollJob(jobURL)
   325  	allWarnings = append(allWarnings, Warnings(pollJobWarnings)...)
   326  
   327  	return allWarnings, err
   328  }
   329  
   330  func (actor Actor) GetRouteByAttributes(domain resources.Domain, hostname string, path string, port int) (resources.Route, Warnings, error) {
   331  	var (
   332  		routes     []resources.Route
   333  		ccWarnings ccv3.Warnings
   334  		err        error
   335  	)
   336  
   337  	queries := []ccv3.Query{
   338  		{Key: ccv3.DomainGUIDFilter, Values: []string{domain.GUID}},
   339  		{Key: ccv3.HostsFilter, Values: []string{hostname}},
   340  		{Key: ccv3.PathsFilter, Values: []string{path}},
   341  	}
   342  
   343  	if domain.IsTCP() {
   344  		queries = append(queries, ccv3.Query{Key: ccv3.PortsFilter, Values: []string{strconv.Itoa(port)}})
   345  	}
   346  
   347  	routes, ccWarnings, err = actor.CloudControllerClient.GetRoutes(queries...)
   348  	if err != nil {
   349  		return resources.Route{}, Warnings(ccWarnings), err
   350  	}
   351  
   352  	if len(routes) < 1 {
   353  		return resources.Route{}, Warnings(ccWarnings), actionerror.RouteNotFoundError{
   354  			DomainName: domain.Name,
   355  			DomainGUID: domain.GUID,
   356  			Host:       hostname,
   357  			Path:       path,
   358  			Port:       port,
   359  		}
   360  	}
   361  
   362  	return routes[0], Warnings(ccWarnings), nil
   363  }
   364  
   365  func (actor Actor) MapRoute(routeGUID string, appGUID string) (Warnings, error) {
   366  	warnings, err := actor.CloudControllerClient.MapRoute(routeGUID, appGUID)
   367  	return Warnings(warnings), err
   368  }
   369  
   370  func (actor Actor) UnmapRoute(routeGUID string, destinationGUID string) (Warnings, error) {
   371  	warnings, err := actor.CloudControllerClient.UnmapRoute(routeGUID, destinationGUID)
   372  	return Warnings(warnings), err
   373  }
   374  
   375  func (actor Actor) GetApplicationRoutes(appGUID string) ([]resources.Route, Warnings, error) {
   376  	allWarnings := Warnings{}
   377  
   378  	routes, warnings, err := actor.CloudControllerClient.GetApplicationRoutes(appGUID)
   379  	allWarnings = append(allWarnings, warnings...)
   380  	if err != nil {
   381  		return nil, allWarnings, err
   382  	}
   383  
   384  	if len(routes) == 0 {
   385  		return nil, allWarnings, err
   386  	}
   387  
   388  	return routes, allWarnings, nil
   389  }
   390  
   391  func getDomainName(fullURL, host, path string, port int) string {
   392  	domainWithoutHost := strings.TrimPrefix(fullURL, host+".")
   393  	domainWithoutPath := strings.TrimSuffix(domainWithoutHost, path)
   394  
   395  	if port > 0 {
   396  		portString := strconv.Itoa(port)
   397  		domainWithoutPort := strings.TrimSuffix(domainWithoutPath, ":"+portString)
   398  		return domainWithoutPort
   399  	}
   400  
   401  	return domainWithoutPath
   402  }