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

     1  package pushaction
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/actor/v2action"
    10  	"code.cloudfoundry.org/cli/types"
    11  	log "github.com/sirupsen/logrus"
    12  )
    13  
    14  func (actor Actor) BindRoutes(config ApplicationConfig) (ApplicationConfig, bool, Warnings, error) {
    15  	log.Info("binding routes")
    16  
    17  	var boundRoutes bool
    18  	var allWarnings Warnings
    19  
    20  	for _, route := range config.DesiredRoutes {
    21  		if !actor.routeInListByGUID(route, config.CurrentRoutes) {
    22  			log.Debugf("binding route: %#v", route)
    23  			warnings, err := actor.bindRouteToApp(route, config.DesiredApplication.GUID)
    24  			allWarnings = append(allWarnings, warnings...)
    25  			if err != nil {
    26  				log.Errorln("binding route:", err)
    27  				return ApplicationConfig{}, false, allWarnings, err
    28  			}
    29  			boundRoutes = true
    30  		} else {
    31  			log.Debugf("route %s already bound to app", route)
    32  		}
    33  	}
    34  	log.Debug("binding routes complete")
    35  	config.CurrentRoutes = config.DesiredRoutes
    36  
    37  	return config, boundRoutes, allWarnings, nil
    38  }
    39  
    40  func (actor Actor) CalculateRoutes(routes []string, orgGUID string, spaceGUID string, existingRoutes []v2action.Route) ([]v2action.Route, Warnings, error) {
    41  	calculatedRoutes, unknownRoutes := actor.spitExistingRoutes(existingRoutes, routes)
    42  	possibleDomains, err := actor.generatePossibleDomains(unknownRoutes)
    43  	if err != nil {
    44  		log.Errorln("domain breakdown:", err)
    45  		return nil, nil, err
    46  	}
    47  
    48  	var allWarnings Warnings
    49  	foundDomains, warnings, err := actor.V2Actor.GetDomainsByNameAndOrganization(possibleDomains, orgGUID)
    50  	allWarnings = append(allWarnings, warnings...)
    51  	if err != nil {
    52  		log.Errorln("domain lookup:", err)
    53  		return nil, allWarnings, err
    54  	}
    55  	nameToFoundDomain := map[string]v2action.Domain{}
    56  	for _, foundDomain := range foundDomains {
    57  		log.WithField("domain", foundDomain.Name).Debug("found domain")
    58  		nameToFoundDomain[foundDomain.Name] = foundDomain
    59  	}
    60  
    61  	for _, route := range unknownRoutes {
    62  		log.WithField("route", route).Debug("generating route")
    63  
    64  		root, port, path, parseErr := actor.parseURL(route)
    65  		if parseErr != nil {
    66  			log.Errorln("parse route:", parseErr)
    67  			return nil, allWarnings, parseErr
    68  		}
    69  
    70  		host, domain, domainErr := actor.calculateRoute(root, nameToFoundDomain)
    71  		if _, ok := domainErr.(v2action.DomainNotFoundError); ok {
    72  			log.Error("no matching domains")
    73  			return nil, allWarnings, actionerror.NoMatchingDomainError{Route: route}
    74  		} else if domainErr != nil {
    75  			log.Errorln("matching domains:", domainErr)
    76  			return nil, allWarnings, domainErr
    77  		}
    78  
    79  		// TODO: redo once TCP routing has been completed
    80  		if port.IsSet && domain.IsHTTP() {
    81  			return nil, allWarnings, actionerror.InvalidHTTPRouteSettings{Domain: domain.Name}
    82  		}
    83  
    84  		calculatedRoute, routeWarnings, routeErr := actor.findOrReturnPartialRouteWithSettings(v2action.Route{
    85  			Host:      strings.Join(host, "."),
    86  			Domain:    domain,
    87  			Path:      path,
    88  			Port:      port,
    89  			SpaceGUID: spaceGUID,
    90  		})
    91  		allWarnings = append(allWarnings, routeWarnings...)
    92  		if routeErr != nil {
    93  			log.Errorln("route lookup:", routeErr)
    94  			return nil, allWarnings, routeErr
    95  		}
    96  
    97  		calculatedRoutes = append(calculatedRoutes, calculatedRoute)
    98  	}
    99  
   100  	return calculatedRoutes, allWarnings, nil
   101  }
   102  
   103  func (actor Actor) CreateAndBindApplicationRoutes(orgGUID string, spaceGUID string, app v2action.Application) (Warnings, error) {
   104  	var warnings Warnings
   105  	defaultRoute, domainWarnings, err := actor.getDefaultRoute(orgGUID, spaceGUID, app.Name)
   106  	warnings = append(warnings, domainWarnings...)
   107  	if err != nil {
   108  		return warnings, err
   109  	}
   110  
   111  	boundRoutes, appRouteWarnings, err := actor.V2Actor.GetApplicationRoutes(app.GUID)
   112  	warnings = append(warnings, appRouteWarnings...)
   113  	if err != nil {
   114  		return warnings, err
   115  	}
   116  
   117  	_, routeAlreadyBound := actor.routeInListBySettings(defaultRoute, boundRoutes)
   118  	if routeAlreadyBound {
   119  		return warnings, err
   120  	}
   121  
   122  	spaceRoute, spaceRouteWarnings, err := actor.V2Actor.FindRouteBoundToSpaceWithSettings(defaultRoute)
   123  	warnings = append(warnings, spaceRouteWarnings...)
   124  	routeAlreadyExists := true
   125  	if _, ok := err.(v2action.RouteNotFoundError); ok {
   126  		routeAlreadyExists = false
   127  	} else if err != nil {
   128  		return warnings, err
   129  	}
   130  
   131  	if !routeAlreadyExists {
   132  		var createRouteWarning v2action.Warnings
   133  		spaceRoute, createRouteWarning, err = actor.V2Actor.CreateRoute(defaultRoute, false)
   134  		warnings = append(warnings, createRouteWarning...)
   135  		if err != nil {
   136  			return warnings, err
   137  		}
   138  	}
   139  
   140  	bindWarnings, err := actor.V2Actor.BindRouteToApplication(spaceRoute.GUID, app.GUID)
   141  	warnings = append(warnings, bindWarnings...)
   142  	return warnings, err
   143  }
   144  
   145  func (actor Actor) CreateRoutes(config ApplicationConfig) (ApplicationConfig, bool, Warnings, error) {
   146  	log.Info("creating routes")
   147  
   148  	var routes []v2action.Route
   149  	var createdRoutes bool
   150  	var allWarnings Warnings
   151  
   152  	for _, route := range config.DesiredRoutes {
   153  		if route.GUID == "" {
   154  			log.WithField("route", route).Debug("creating route")
   155  
   156  			createdRoute, warnings, err := actor.V2Actor.CreateRoute(route, false)
   157  			allWarnings = append(allWarnings, warnings...)
   158  			if err != nil {
   159  				log.Errorln("creating route:", err)
   160  				return ApplicationConfig{}, true, allWarnings, err
   161  			}
   162  			routes = append(routes, createdRoute)
   163  
   164  			createdRoutes = true
   165  		} else {
   166  			log.WithField("route", route).Debug("already exists, skipping")
   167  			routes = append(routes, route)
   168  		}
   169  	}
   170  	config.DesiredRoutes = routes
   171  
   172  	return config, createdRoutes, allWarnings, nil
   173  }
   174  
   175  // GetRouteWithDefaultDomain returns a route with the host and the default org
   176  // domain. This may be a partial route (ie no GUID) if the route does not
   177  // exist.
   178  func (actor Actor) GetRouteWithDefaultDomain(host string, orgGUID string, spaceGUID string, knownRoutes []v2action.Route) (v2action.Route, Warnings, error) {
   179  	defaultDomain, warnings, err := actor.DefaultDomain(orgGUID)
   180  	if err != nil {
   181  		log.Errorln("could not find default domains:", err.Error())
   182  		return v2action.Route{}, warnings, err
   183  	}
   184  
   185  	defaultRoute := v2action.Route{
   186  		Domain:    defaultDomain,
   187  		Host:      strings.ToLower(host),
   188  		SpaceGUID: spaceGUID,
   189  	}
   190  
   191  	cachedRoute, found := actor.routeInListBySettings(defaultRoute, knownRoutes)
   192  	if !found {
   193  		route, routeWarnings, err := actor.V2Actor.FindRouteBoundToSpaceWithSettings(defaultRoute)
   194  		if _, ok := err.(v2action.RouteNotFoundError); ok {
   195  			return defaultRoute, append(warnings, routeWarnings...), nil
   196  		}
   197  		return route, append(warnings, routeWarnings...), err
   198  	}
   199  	return cachedRoute, warnings, nil
   200  }
   201  
   202  func (actor Actor) bindRouteToApp(route v2action.Route, appGUID string) (v2action.Warnings, error) {
   203  	warnings, err := actor.V2Actor.BindRouteToApplication(route.GUID, appGUID)
   204  	if _, ok := err.(v2action.RouteInDifferentSpaceError); ok {
   205  		return warnings, v2action.RouteInDifferentSpaceError{Route: route.String()}
   206  	}
   207  	return warnings, err
   208  }
   209  
   210  func (actor Actor) calculateRoute(route string, domainCache map[string]v2action.Domain) ([]string, v2action.Domain, error) {
   211  	host, domain := actor.splitHost(route)
   212  	if domain, ok := domainCache[route]; ok {
   213  		return nil, domain, nil
   214  	}
   215  
   216  	if host == "" {
   217  		return nil, v2action.Domain{}, v2action.DomainNotFoundError{Name: route}
   218  	}
   219  
   220  	hosts, foundDomain, err := actor.calculateRoute(domain, domainCache)
   221  	hosts = append([]string{host}, hosts...)
   222  
   223  	return hosts, foundDomain, err
   224  }
   225  
   226  func (actor Actor) findOrReturnPartialRouteWithSettings(route v2action.Route) (v2action.Route, Warnings, error) {
   227  	cachedRoute, warnings, err := actor.V2Actor.FindRouteBoundToSpaceWithSettings(route)
   228  	if _, ok := err.(v2action.RouteNotFoundError); ok {
   229  		return route, Warnings(warnings), nil
   230  	}
   231  	return cachedRoute, Warnings(warnings), err
   232  }
   233  
   234  func (actor Actor) generatePossibleDomains(routes []string) ([]string, error) {
   235  	var hostnames []string
   236  	for _, route := range routes {
   237  		host, _, _, err := actor.parseURL(route)
   238  		if err != nil {
   239  			return nil, err
   240  		}
   241  		hostnames = append(hostnames, host)
   242  	}
   243  
   244  	possibleDomains := map[string]interface{}{}
   245  	for _, route := range hostnames {
   246  		count := strings.Count(route, ".")
   247  		domains := strings.SplitN(route, ".", count)
   248  
   249  		for i := range domains {
   250  			domain := strings.Join(domains[i:], ".")
   251  			possibleDomains[domain] = nil
   252  		}
   253  	}
   254  
   255  	var domains []string
   256  	for domain := range possibleDomains {
   257  		domains = append(domains, domain)
   258  	}
   259  
   260  	log.Debugln("domain brakedown:", strings.Join(domains, ","))
   261  	return domains, nil
   262  }
   263  
   264  func (actor Actor) getDefaultRoute(orgGUID string, spaceGUID string, appName string) (v2action.Route, Warnings, error) {
   265  	defaultDomain, domainWarnings, err := actor.DefaultDomain(orgGUID)
   266  	if err != nil {
   267  		return v2action.Route{}, domainWarnings, err
   268  	}
   269  
   270  	return v2action.Route{
   271  		Host:      appName,
   272  		Domain:    defaultDomain,
   273  		SpaceGUID: spaceGUID,
   274  	}, domainWarnings, nil
   275  }
   276  
   277  func (Actor) parseURL(route string) (string, types.NullInt, string, error) {
   278  	if !(strings.HasPrefix(route, "http://") || strings.HasPrefix(route, "https://")) {
   279  		route = fmt.Sprintf("http://%s", route)
   280  	}
   281  	parsedURL, err := url.Parse(route)
   282  	if err != nil {
   283  		return "", types.NullInt{}, "", err
   284  	}
   285  
   286  	path := parsedURL.RequestURI()
   287  	if path == "/" {
   288  		path = ""
   289  	}
   290  
   291  	var port types.NullInt
   292  	err = port.ParseStringValue(parsedURL.Port())
   293  	return parsedURL.Hostname(), port, path, err
   294  }
   295  
   296  func (Actor) routeInListByGUID(route v2action.Route, routes []v2action.Route) bool {
   297  	for _, r := range routes {
   298  		if r.GUID == route.GUID {
   299  			return true
   300  		}
   301  	}
   302  
   303  	return false
   304  }
   305  
   306  func (Actor) routeInListByName(route string, routes []v2action.Route) (v2action.Route, bool) {
   307  	for _, r := range routes {
   308  		if r.String() == route {
   309  			return r, true
   310  		}
   311  	}
   312  
   313  	return v2action.Route{}, false
   314  }
   315  func (Actor) routeInListBySettings(route v2action.Route, routes []v2action.Route) (v2action.Route, bool) {
   316  	for _, r := range routes {
   317  		if r.Host == route.Host && r.Path == route.Path && r.Port == route.Port &&
   318  			r.SpaceGUID == route.SpaceGUID && r.Domain.GUID == route.Domain.GUID {
   319  			return r, true
   320  		}
   321  	}
   322  
   323  	return v2action.Route{}, false
   324  }
   325  
   326  func (actor Actor) spitExistingRoutes(existingRoutes []v2action.Route, routes []string) ([]v2action.Route, []string) {
   327  	var cachedRoutes []v2action.Route
   328  	for _, route := range existingRoutes {
   329  		cachedRoutes = append(cachedRoutes, route)
   330  	}
   331  
   332  	var unknownRoutes []string
   333  	for _, route := range routes {
   334  		if _, found := actor.routeInListByName(route, existingRoutes); !found {
   335  			log.WithField("route", route).Debug("unable to find route in cache")
   336  			unknownRoutes = append(unknownRoutes, route)
   337  		}
   338  	}
   339  	return cachedRoutes, unknownRoutes
   340  }
   341  
   342  func (Actor) splitHost(url string) (string, string) {
   343  	count := strings.Count(url, ".")
   344  	if count == 1 {
   345  		return "", url
   346  	}
   347  
   348  	split := strings.SplitN(url, ".", 2)
   349  	return split[0], split[1]
   350  }