github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/actor/v2action/route.go (about) 1 package v2action 2 3 import ( 4 "fmt" 5 "path" 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/ccv2" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 12 "code.cloudfoundry.org/cli/types" 13 log "github.com/sirupsen/logrus" 14 ) 15 16 type Routes []Route 17 18 // Summary converts routes into a comma separated string. 19 func (rs Routes) Summary() string { 20 formattedRoutes := []string{} 21 for _, route := range rs { 22 formattedRoutes = append(formattedRoutes, route.String()) 23 } 24 return strings.Join(formattedRoutes, ", ") 25 } 26 27 // Route represents a CLI Route. 28 type Route struct { 29 Domain Domain 30 GUID string 31 Host string 32 Path string 33 Port types.NullInt 34 SpaceGUID string 35 } 36 37 func (r Route) RandomTCPPort() bool { 38 return r.Domain.IsTCP() && !r.Port.IsSet 39 } 40 41 // Validate will return an error if there are invalid HTTP or TCP settings for 42 // it's given domain. 43 func (r Route) Validate() error { 44 if r.Domain.IsHTTP() { 45 if r.Port.IsSet { 46 return actionerror.InvalidHTTPRouteSettings{Domain: r.Domain.Name} 47 } 48 if r.Domain.IsShared() && r.Host == "" { 49 return actionerror.NoHostnameAndSharedDomainError{} 50 } 51 } else { // Is TCP Domain 52 if r.Host != "" || r.Path != "" { 53 return actionerror.InvalidTCPRouteSettings{Domain: r.Domain.Name} 54 } 55 } 56 return nil 57 } 58 59 // ValidateWithRandomPort will return an error if a random port is requested 60 // for an HTTP route, or if the route is TCP, a random port wasn't requested, 61 // and the route has no port set. 62 func (r Route) ValidateWithRandomPort(randomPort bool) error { 63 if r.Domain.IsHTTP() && randomPort { 64 return actionerror.InvalidHTTPRouteSettings{Domain: r.Domain.Name} 65 } 66 67 if r.Domain.IsTCP() && !r.Port.IsSet && !randomPort { 68 return actionerror.TCPRouteOptionsNotProvidedError{} 69 } 70 return r.Validate() 71 } 72 73 // String formats the route in a human readable format. 74 func (r Route) String() string { 75 routeString := r.Domain.Name 76 77 if r.Port.IsSet { 78 routeString = fmt.Sprintf("%s:%d", routeString, r.Port.Value) 79 } else if r.RandomTCPPort() { 80 routeString = fmt.Sprintf("%s:????", routeString) 81 } 82 83 if r.Host != "" { 84 routeString = fmt.Sprintf("%s.%s", r.Host, routeString) 85 } 86 87 if r.Path != "" { 88 routeString = path.Join(routeString, r.Path) 89 } 90 91 return routeString 92 } 93 94 func (actor Actor) MapRouteToApplication(routeGUID string, appGUID string) (Warnings, error) { 95 _, warnings, err := actor.CloudControllerClient.UpdateRouteApplication(routeGUID, appGUID) 96 if _, ok := err.(ccerror.InvalidRelationError); ok { 97 return Warnings(warnings), actionerror.RouteInDifferentSpaceError{} 98 } 99 return Warnings(warnings), err 100 } 101 102 func (actor Actor) UnmapRouteFromApplication(routeGUID string, appGUID string) (Warnings, error) { 103 warnings, err := actor.CloudControllerClient.DeleteRouteApplication(routeGUID, appGUID) 104 return Warnings(warnings), err 105 } 106 107 func (actor Actor) CreateRoute(route Route, generatePort bool) (Route, Warnings, error) { 108 if route.Path != "" && !strings.HasPrefix(route.Path, "/") { 109 route.Path = fmt.Sprintf("/%s", route.Path) 110 } 111 returnedRoute, warnings, err := actor.CloudControllerClient.CreateRoute(ActorToCCRoute(route), generatePort) 112 return CCToActorRoute(returnedRoute, route.Domain), Warnings(warnings), err 113 } 114 115 func (actor Actor) CreateRouteWithExistenceCheck(orgGUID string, spaceName string, route Route, generatePort bool) (Route, Warnings, error) { 116 space, warnings, spaceErr := actor.GetSpaceByOrganizationAndName(orgGUID, spaceName) 117 if spaceErr != nil { 118 return Route{}, Warnings(warnings), spaceErr 119 } 120 route.SpaceGUID = space.GUID 121 122 if route.Domain.GUID == "" { 123 domains, orgDomainWarnings, getDomainErr := actor.GetDomainsByNameAndOrganization([]string{route.Domain.Name}, orgGUID) 124 warnings = append(warnings, orgDomainWarnings...) 125 if getDomainErr != nil { 126 return Route{}, warnings, getDomainErr 127 } else if len(domains) == 0 { 128 return Route{}, warnings, actionerror.DomainNotFoundError{Name: route.Domain.Name} 129 } 130 route.Domain.GUID = domains[0].GUID 131 route.Domain.RouterGroupType = domains[0].RouterGroupType 132 } 133 134 validateErr := route.ValidateWithRandomPort(generatePort) 135 if validateErr != nil { 136 return Route{}, Warnings(warnings), validateErr 137 } 138 139 if !generatePort { 140 foundRoute, spaceRouteWarnings, findErr := actor.FindRouteBoundToSpaceWithSettings(route) 141 warnings = append(warnings, spaceRouteWarnings...) 142 routeAlreadyExists := true 143 if _, ok := findErr.(actionerror.RouteNotFoundError); ok { 144 routeAlreadyExists = false 145 } else if findErr != nil { 146 return Route{}, Warnings(warnings), findErr 147 } 148 149 if routeAlreadyExists { 150 return Route{}, Warnings(warnings), actionerror.RouteAlreadyExistsError{Route: foundRoute.String()} 151 } 152 } 153 154 createdRoute, createRouteWarnings, createErr := actor.CreateRoute(route, generatePort) 155 warnings = append(warnings, createRouteWarnings...) 156 if createErr != nil { 157 return Route{}, Warnings(warnings), createErr 158 } 159 160 return createdRoute, Warnings(warnings), nil 161 } 162 163 // GetOrphanedRoutesBySpace returns a list of orphaned routes associated with 164 // the provided Space GUID. 165 func (actor Actor) GetOrphanedRoutesBySpace(spaceGUID string) ([]Route, Warnings, error) { 166 var ( 167 orphanedRoutes []Route 168 allWarnings Warnings 169 ) 170 171 routes, warnings, err := actor.GetSpaceRoutes(spaceGUID) 172 allWarnings = append(allWarnings, warnings...) 173 if err != nil { 174 return nil, allWarnings, err 175 } 176 177 for _, route := range routes { 178 apps, warnings, err := actor.GetRouteApplications(route.GUID) 179 allWarnings = append(allWarnings, warnings...) 180 if err != nil { 181 return nil, allWarnings, err 182 } 183 184 if len(apps) == 0 { 185 orphanedRoutes = append(orphanedRoutes, route) 186 } 187 } 188 189 if len(orphanedRoutes) == 0 { 190 return nil, allWarnings, actionerror.OrphanedRoutesNotFoundError{} 191 } 192 193 return orphanedRoutes, allWarnings, nil 194 } 195 196 // GetApplicationRoutes returns a list of routes associated with the provided 197 // Application GUID. 198 func (actor Actor) GetApplicationRoutes(applicationGUID string) (Routes, Warnings, error) { 199 var allWarnings Warnings 200 ccv2Routes, warnings, err := actor.CloudControllerClient.GetApplicationRoutes(applicationGUID) 201 allWarnings = append(allWarnings, warnings...) 202 if err != nil { 203 return nil, allWarnings, err 204 } 205 206 routes, domainWarnings, err := actor.applyDomain(ccv2Routes) 207 208 return routes, append(allWarnings, domainWarnings...), err 209 } 210 211 // GetSpaceRoutes returns a list of routes associated with the provided Space 212 // GUID. 213 func (actor Actor) GetSpaceRoutes(spaceGUID string) ([]Route, Warnings, error) { 214 var allWarnings Warnings 215 ccv2Routes, warnings, err := actor.CloudControllerClient.GetSpaceRoutes(spaceGUID) 216 allWarnings = append(allWarnings, warnings...) 217 if err != nil { 218 return nil, allWarnings, err 219 } 220 221 routes, domainWarnings, err := actor.applyDomain(ccv2Routes) 222 223 return routes, append(allWarnings, domainWarnings...), err 224 } 225 226 // DeleteUnmappedRoutes deletes the unmapped routes associated with the provided Space GUID. 227 func (actor Actor) DeleteUnmappedRoutes(spaceGUID string) (Warnings, error) { 228 warnings, err := actor.CloudControllerClient.DeleteSpaceUnmappedRoutes(spaceGUID) 229 return Warnings(warnings), err 230 } 231 232 // DeleteRoute deletes the Route associated with the provided Route GUID. 233 func (actor Actor) DeleteRoute(routeGUID string) (Warnings, error) { 234 warnings, err := actor.CloudControllerClient.DeleteRoute(routeGUID) 235 return Warnings(warnings), err 236 } 237 238 func (actor Actor) CheckRoute(route Route) (bool, Warnings, error) { 239 exists, warnings, err := actor.CloudControllerClient.CheckRoute(ActorToCCRoute(route)) 240 return exists, Warnings(warnings), err 241 } 242 243 // FindRouteBoundToSpaceWithSettings finds the route with the given host, 244 // domain and space. If it is unable to find the route, it will check if it 245 // exists anywhere in the system. When the route exists in another space, 246 // RouteInDifferentSpaceError is returned. Use this when you know the space 247 // GUID. 248 func (actor Actor) FindRouteBoundToSpaceWithSettings(route Route) (Route, Warnings, error) { 249 existingRoute, warnings, err := actor.GetRouteByComponents(route) 250 if routeNotFoundErr, ok := err.(actionerror.RouteNotFoundError); ok { 251 // This check only works for API versions 2.55 or higher. It will return 252 // false for anything below that. 253 log.Infof("checking route existence for: %s\n", route) 254 exists, checkRouteWarnings, chkErr := actor.CheckRoute(route) 255 if chkErr != nil { 256 log.Errorln("check route:", err) 257 return Route{}, append(Warnings(warnings), checkRouteWarnings...), chkErr 258 } 259 260 // This will happen if the route exists in a space to which the user does 261 // not have access. 262 if exists { 263 log.Errorf("unable to find route %s in current space", route.String()) 264 return Route{}, append(Warnings(warnings), checkRouteWarnings...), actionerror.RouteInDifferentSpaceError{Route: route.String()} 265 } 266 267 log.Warnf("negative existence check for route %s - returning partial route", route.String()) 268 log.Debugf("partialRoute: %#v", route) 269 return Route{}, append(Warnings(warnings), checkRouteWarnings...), routeNotFoundErr 270 } else if err != nil { 271 log.Errorln("finding route:", err) 272 return Route{}, Warnings(warnings), err 273 } 274 275 if existingRoute.SpaceGUID != route.SpaceGUID { 276 log.WithFields(log.Fields{ 277 "targeted_space_guid": route.SpaceGUID, 278 "existing_space_guid": existingRoute.SpaceGUID, 279 }).Errorf("route exists in different space the user has access to") 280 return Route{}, Warnings(warnings), actionerror.RouteInDifferentSpaceError{Route: route.String()} 281 } 282 283 log.Debugf("found route: %#v", existingRoute) 284 return existingRoute, Warnings(warnings), err 285 } 286 287 // GetRouteByComponents returns the route with the matching host, domain, path, 288 // and port. Use this when you don't know the space GUID. 289 // TCP routes require a port to be uniquely identified 290 // HTTP routes using shared domains require a hostname or path to be uniquely identified 291 func (actor Actor) GetRouteByComponents(route Route) (Route, Warnings, error) { 292 // TODO: validation should probably be done separately (?) 293 if route.Domain.IsTCP() && !route.Port.IsSet { 294 return Route{}, nil, actionerror.PortNotProvidedForQueryError{} 295 } 296 297 if route.Domain.IsShared() && route.Domain.IsHTTP() && route.Host == "" { 298 return Route{}, nil, actionerror.NoHostnameAndSharedDomainError{} 299 } 300 301 queries := []ccv2.Filter{ 302 { 303 Type: constant.DomainGUIDFilter, 304 Operator: constant.EqualOperator, 305 Values: []string{route.Domain.GUID}, 306 }, { 307 Type: constant.HostFilter, 308 Operator: constant.EqualOperator, 309 Values: []string{route.Host}, 310 }, { 311 Type: constant.PathFilter, 312 Operator: constant.EqualOperator, 313 Values: []string{route.Path}, 314 }, 315 } 316 317 if route.Port.IsSet { 318 queries = append(queries, ccv2.Filter{ 319 Type: constant.PortFilter, 320 Operator: constant.EqualOperator, 321 Values: []string{fmt.Sprint(route.Port.Value)}, 322 }) 323 } 324 325 ccv2Routes, warnings, err := actor.CloudControllerClient.GetRoutes(queries...) 326 if err != nil { 327 return Route{}, Warnings(warnings), err 328 } 329 330 if len(ccv2Routes) == 0 { 331 return Route{}, Warnings(warnings), actionerror.RouteNotFoundError{ 332 Host: route.Host, 333 DomainGUID: route.Domain.GUID, 334 Path: route.Path, 335 Port: route.Port.Value, 336 } 337 } 338 339 return CCToActorRoute(ccv2Routes[0], route.Domain), Warnings(warnings), err 340 } 341 342 func ActorToCCRoute(route Route) ccv2.Route { 343 return ccv2.Route{ 344 DomainGUID: route.Domain.GUID, 345 GUID: route.GUID, 346 Host: route.Host, 347 Path: route.Path, 348 Port: route.Port, 349 SpaceGUID: route.SpaceGUID, 350 } 351 } 352 353 func CCToActorRoute(ccv2Route ccv2.Route, domain Domain) Route { 354 return Route{ 355 Domain: domain, 356 GUID: ccv2Route.GUID, 357 Host: ccv2Route.Host, 358 Path: ccv2Route.Path, 359 Port: ccv2Route.Port, 360 SpaceGUID: ccv2Route.SpaceGUID, 361 } 362 } 363 364 func (actor Actor) applyDomain(ccv2Routes []ccv2.Route) (Routes, Warnings, error) { 365 var routes Routes 366 var allWarnings Warnings 367 368 for _, ccv2Route := range ccv2Routes { 369 domain, warnings, err := actor.GetDomain(ccv2Route.DomainGUID) 370 allWarnings = append(allWarnings, warnings...) 371 if err != nil { 372 return nil, allWarnings, err 373 } 374 routes = append(routes, CCToActorRoute(ccv2Route, domain)) 375 } 376 377 return routes, allWarnings, nil 378 }