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 }