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