def bootstrap_local_module(service_uri, deploy_path, app): """ Incorporates the routes of an existing app into this one :param service_uri: the path to the target application :param deploy_path: the path on which to make the target app discoverable :param app: flask.Flask application instance :return: None """ app.logger.debug( 'Attempting bootstrap_local_module [{0}]'.format(service_uri) ) module = import_module(service_uri) local_app = module.create_app() # Add the target app's config to the parent app's config. # Do not overwrite any config already present in the parent app for k, v in local_app.config.iteritems(): if k not in app.config: app.config[k] = v for rule in local_app.url_map.iter_rules(): view = local_app.view_functions[rule.endpoint] route = os.path.join(deploy_path, rule.rule[1:]) # view_class is attached to a function view in the case of # class-based views, and that view.view_class is the element # that has the scopes and docstring attributes if hasattr(view, 'view_class'): attr_base = view.view_class else: attr_base = view # Decorate the view with ratelimit if hasattr(attr_base, 'rate_limit'): d = attr_base.rate_limit[0] view = ratelimit( limit=lambda default=d, **kwargs: limit_func(default), per=attr_base.rate_limit[1], scope_func=lambda: scope_func(), key_func=lambda: request.endpoint )(view) # Decorate the view with require_oauth if hasattr(attr_base, 'scopes'): view = oauth2.require_oauth(*attr_base.scopes)(view) # Add cache-control headers if app.config.get('API_PROXYVIEW_HEADERS'): view = headers(app.config['API_PROXYVIEW_HEADERS'])(view) # Let flask handle OPTIONS, which it will not do if we explicitly # add it to the url_map if 'OPTIONS' in rule.methods: rule.methods.remove('OPTIONS') app.add_url_rule(route, route, view, methods=rule.methods)
def bootstrap_remote_service(service_uri, deploy_path, app): """ Incorporates the routes of a remote app into this one by registering views that forward traffic to those remote endpoints :param service_uri: the http url of the target application :param deploy_path: the path on which to make the target app discoverable :param app: flask.Flask application instance :return: None """ app.logger.debug( 'Attempting bootstrap_remote_service [{0}]'.format(service_uri) ) if service_uri.startswith('consul://'): cs = ConsulService( service_uri, nameservers=[app.config.get("CONSUL_DNS", "172.17.42.1")] ) url = urljoin( cs.base_url, app.config.get('WEBSERVICES_PUBLISH_ENDPOINT', '/') ) print "base url", cs.base_url else: url = urljoin( service_uri, app.config.get('WEBSERVICES_PUBLISH_ENDPOINT', '/') ) try: r = requests.get(url, timeout=5) except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): app.logger.info('Could not discover {0}'.format(service_uri)) return # validate(r.json()) # TODO validate the incoming json # Start constructing the ProxyViews based on what we got when querying # the /resources route. # If any part of this procedure fails, log that we couldn't produce this # ProxyView, but otherwise continue. for resource, properties in r.json().iteritems(): if resource.startswith('/'): resource = resource[1:] route = os.path.join(deploy_path, resource) remote_route = urljoin(service_uri, resource) # Make an instance of the ProxyView. We need to instantiate the class # to save instance attributes, which will be necessary to re-construct # the location to the third party resource (ProxyView.endpoint) with app.app_context(): # app_context to allow config lookup via current_app in __init__ proxyview = ProxyView(remote_route, service_uri, deploy_path) for method in properties['methods']: if method not in proxyview.methods: app.logger.warning("Could not create a ProxyView for " "method {meth} for {ep}" .format(meth=method, ep=service_uri)) continue view = proxyview.dispatcher properties.setdefault('rate_limit', [1000, 86400]) properties.setdefault('scopes', []) # Decorate the view with ratelimit. d = properties['rate_limit'][0] view = ratelimit( limit=lambda default=d, **kwargs: limit_func(default), per=properties['rate_limit'][1], scope_func=lambda: scope_func(), key_func=lambda: request.endpoint, )(view) # Decorate with the advertised oauth2 scopes view = oauth2.require_oauth(*properties['scopes'])(view) # Add cache-control headers if app.config.get('API_PROXYVIEW_HEADERS'): view = headers(app.config['API_PROXYVIEW_HEADERS'])(view) # Either make a new route with this view, or append the new method # to an existing route if one exists with the same name try: rule = next(app.url_map.iter_rules(endpoint=route)) if method not in rule.methods: rule.methods.update([method]) except KeyError: app.add_url_rule(route, route, view, methods=[method])