def test_destroy(self): LoadBalancer.create('fake', 'my-lb', {'LB_ID': 'xxx'}) db_lb = LoadBalancer.find('my-lb') self.assertEqual(db_lb.id, 'xxx') db_lb.destroy() db_lb = LoadBalancer.find('my-lb') self.assertIsNone(db_lb)
def test_destroy_ignores_manager_exception(self, log): LoadBalancer.create('fake', 'my-lb', {'LB_ID': 'explode'}) db_lb = LoadBalancer.find('my-lb') self.assertEqual(db_lb.id, 'explode') db_lb.destroy() self.assertEqual(log.call_args, call("Error trying to destroy load balancer name: 'my-lb' " "id: 'explode' in 'fake': failure to destroy")) db_lb = LoadBalancer.find('my-lb') self.assertIsNone(db_lb)
def test_destroy_ignores_manager_exception(self, log): LoadBalancer.create('fake', 'my-lb', {'LB_ID': 'explode'}) db_lb = LoadBalancer.find('my-lb') self.assertEqual(db_lb.id, 'explode') db_lb.destroy() self.assertEqual( log.call_args, call("Error trying to destroy load balancer name: 'my-lb' " "id: 'explode' in 'fake': failure to destroy")) db_lb = LoadBalancer.find('my-lb') self.assertIsNone(db_lb)
def update_certificate(self, name, cert, key): self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.update_binding_certificate(name, cert, key) self.consul_manager.set_certificate(name, cert, key)
def activate_ssl(self, name, domain, plugin='default'): lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() # Check if DNS is registered for rpaas ip if not self._check_dns(name, domain): raise SslError('rpaas IP is not registered for this DNS name') # Key and CSR generated to request a certificate key = self._generate_key() csr = self._generate_csr(key, domain) # load plugin if get it as an arg if plugin.isalpha() and plugin in rpaas.ssl_plugins.__all__ and \ plugin not in ['default', '__init__']: try: self.storage.store_task(name) task = tasks.DownloadCertTask().delay(self.config, name, plugin, csr, key, domain) self.storage.update_task(name, task.task_id) return '' except Exception: raise SslError('rpaas IP is not registered for this DNS name') else: # default p_ssl = rpaas.ssl_plugins.default.Default(domain) cert = p_ssl.download_crt(key=key) self.update_certificate(name, cert, key) return ''
def delete_block(self, name, block_name): self.task_manager.ensure_ready(name) block_name = block_name.strip() lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.consul_manager.remove_block(name, block_name)
def activate_ssl(self, name, domain, plugin='default'): lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() if not self._check_dns(name, domain): raise SslError('rpaas IP is not registered for this DNS name') key = ssl.generate_key() csr = ssl.generate_csr(key, domain) if plugin == 'le': try: self.task_manager.create(name) task = tasks.DownloadCertTask().delay(self.config, name, plugin, csr, key, domain) self.task_manager.update(name, task.task_id) return '' except Exception: raise SslError('rpaas IP is not registered for this DNS name') else: p_ssl = ssl_plugins.default.Default(domain) cert = p_ssl.download_crt(key=key) self.update_certificate(name, cert, key) return ''
def add_block(self, name, block_name, content): self._ensure_ready(name) block_name = block_name.strip() lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.consul_manager.write_block(name, block_name, content)
def remove_upstream(self, name, upstream_name, servers): self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.consul_manager.remove_server_upstream(name, upstream_name, servers)
def generate_crt(config, name, plugin, csr, key, domain): lb = LoadBalancer.find(name, config) if lb is None: raise storage.InstanceNotFoundError() strg = storage.MongoDBStorage(config) consul_mngr = consul_manager.ConsulManager(config) crt = None plugin_class = ssl_plugins.get(plugin) if not plugin_class: raise Exception("Invalid plugin {}".format(plugin)) plugin_obj = plugin_class(domain, os.environ.get('RPAAS_PLUGIN_LE_EMAIL', 'admin@'+domain), name, consul_manager=consul_mngr) # Upload csr and get an Id plugin_id = plugin_obj.upload_csr(csr) crt = plugin_obj.download_crt(id=str(plugin_id)) # Download the certificate and update nginx with it if crt: try: js_crt = json.loads(crt) cert = js_crt['crt'] cert = cert+js_crt['chain'] if 'chain' in js_crt else cert key = js_crt['key'] if 'key' in js_crt else key except: cert = crt consul_mngr.set_certificate(name, cert, key) strg.store_le_certificate(name, domain) else: raise Exception('Could not download certificate')
def info(self, name): addr = self._get_address(name) routes_data = [] binding_data = self.storage.find_binding(name) if binding_data: paths = binding_data.get('paths') or [] for path_data in paths: routes_data.append("path = {}".format(path_data['path'])) dst = path_data.get('destination') content = path_data.get('content') if dst: routes_data.append("destination = {}".format(dst)) if content: routes_data.append("content = {}".format(content)) lb = LoadBalancer.find(name) host_count = 0 if lb: host_count = len(lb.hosts) return [ { "label": "Address", "value": addr, }, { "label": "Instances", "value": str(host_count), }, { "label": "Routes", "value": "\n".join(routes_data), }, ]
def new_instance(self, name, team=None, plan_name=None): plan = None if plan_name: plan = self.storage.find_plan(plan_name) used, quota = self.storage.find_team_quota(team) if len(used) >= quota: raise QuotaExceededError(len(used), quota) if not self.storage.increment_quota(team, used, name): raise Exception("concurrent operations updating team quota") lb = LoadBalancer.find(name) if lb is not None: raise storage.DuplicateError(name) self.task_manager.create(name) config = copy.deepcopy(self.config) metadata = {} if plan: config.update(plan.config) metadata["plan_name"] = plan_name metadata[ "consul_token"] = consul_token = self.consul_manager.generate_token( name) self.consul_manager.write_healthcheck(name) self.storage.store_instance_metadata(name, **metadata) self._add_tags(name, config, consul_token) task = tasks.NewInstanceTask().delay(config, name) self.task_manager.update(name, task.task_id)
def delete_route(self, name, path): self.task_manager.ensure_ready(name) path = path.strip() if path == "/": raise RouteError( "You cannot remove a route for / location, unbind the app.") lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() routes = self.list_routes(name) destination_count = 0 if not routes: raise storage.InstanceNotFoundError() destination = [ p['destination'] for p in routes['paths'] if p['path'] == path ] if len(destination) > 0: destination = destination.pop() else: destination = None for p in routes['paths']: if destination and p['destination'] == destination: destination_count += 1 if destination_count == 0: raise storage.InstanceNotFoundError() if destination_count < 2: self.consul_manager.remove_server_upstream(name, destination, destination) self.storage.delete_binding_path(name, path) self.consul_manager.remove_location(name, path)
def add_lua(self, name, lua_module_name, lua_module_type, content): self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: storage.InstanceNotFoundError() self.consul_manager.write_lua(name, lua_module_name, lua_module_type, content)
def new_instance(self, name, team=None, plan_name=None, flavor_name=None): plan = None flavor = None if plan_name: plan = self.storage.find_plan(plan_name) if flavor_name: flavor = self.storage.find_flavor(flavor_name) used, quota = self.storage.find_team_quota(team) if len(used) >= quota: raise QuotaExceededError(len(used), quota) if not self.storage.increment_quota(team, used, name): raise Exception("concurrent operations updating team quota") lb = LoadBalancer.find(name) if lb is not None: raise storage.DuplicateError(name) self.task_manager.create(name) config = copy.deepcopy(self.config) metadata = {} if plan: config.update(plan.config) metadata["plan_name"] = plan_name if flavor: config.update(flavor.config) metadata["flavor_name"] = flavor_name metadata["consul_token"] = consul_token = self.consul_manager.generate_token(name) self.consul_manager.write_healthcheck(name) self.storage.store_instance_metadata(name, **metadata) self._add_tags(name, config, consul_token) task = tasks.NewInstanceTask().delay(config, name) self.task_manager.update(name, task.task_id)
def update_certificate(self, name, cert, key): self._ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.update_binding_certificate(name, cert, key) for host in lb.hosts: self.nginx_manager.update_certificate(host.dns_name, cert, key)
def swap(self, src_instance, dst_instance): self.task_manager.ensure_ready(src_instance) self.task_manager.ensure_ready(dst_instance) for instance in [src_instance, dst_instance]: lb = LoadBalancer.find(instance) if lb is None: raise storage.InstanceNotFoundError(instance) self.consul_manager.swap_instances(src_instance, dst_instance)
def add_route(self, name, path, destination, content): self._ensure_ready(name) path = path.strip() lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.replace_binding_path(name, path, destination, content) for host in lb.hosts: self.nginx_manager.update_binding(host.dns_name, path, destination, content)
def add_route(self, name, path, destination, content): self.task_manager.ensure_ready(name) path = path.strip() lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.replace_binding_path(name, path, destination, content) self.consul_manager.write_location(name, path, destination=destination, content=content)
def run(self, config, name): self.init_config(config) lb = LoadBalancer.find(name, self.config) if lb is None: raise storage.InstanceNotFoundError() for host in lb.hosts: host.destroy() lb.destroy() self.hc.destroy(name)
def unbind(self, name, app_host): self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() binding_data = self.storage.find_binding(name) if not binding_data: return self.storage.remove_root_binding(name) self.consul_manager.remove_location(name, "/")
def delete_route(self, name, path): self.task_manager.ensure_ready(name) path = path.strip() if path == "/": raise RouteError("You cannot remove a route for / location, unbind the app.") lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.delete_binding_path(name, path) self.consul_manager.remove_location(name, path)
def update_certificate(self, name, cert, key): self._ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() # if not self._verify_crt(cert, key): # raise SslError('Invalid certificate') self.storage.update_binding_certificate(name, cert, key) for host in lb.hosts: self.nginx_manager.update_certificate(host.dns_name, cert, key)
def update_instance(self, name, plan_name): if not self.storage.find_plan(plan_name): raise storage.PlanNotFoundError() self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() metadata = self.storage.find_instance_metadata(name) metadata['plan_name'] = plan_name self.storage.store_instance_metadata(name, **metadata)
def test_remove_host(self): h1 = Host('x', 'x.me.com') h2 = Host('y', 'y.me.com') lb = LoadBalancer.create('fake', 'my-lb', {'LB_ID': 'explode'}) lb.add_host(h1) lb.add_host(h2) lb.remove_host(h1) self.assertItemsEqual(lb.hosts, [h2]) db_lb = LoadBalancer.find('my-lb') self.assertItemsEqual([h.to_json() for h in db_lb.hosts], [h2.to_json()])
def unbind(self, name, app_host): self._ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() binding_data = self.storage.find_binding(name) if not binding_data: return self.storage.remove_root_binding(name) for host in lb.hosts: self.nginx_manager.delete_binding(host.dns_name, '/')
def run(self, config, name): self.init_config(config) lb = LoadBalancer.find(name, self.config) if lb is None: raise storage.InstanceNotFoundError() for host in lb.hosts: self._delete_host(name, host, lb) lb.destroy() for cert in self.storage.find_le_certificates({'name': name}): self.storage.remove_le_certificate(name, cert['domain']) self.hc.destroy(name)
def _get_address(self, name): task = self.storage.find_task(name) if task: result = tasks.NewInstanceTask().AsyncResult(task['task_id']) if result.status in ['FAILURE', 'REVOKED']: return FAILURE return PENDING lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() return lb.address
def restore_instance(self, name): self.task_manager.ensure_ready(name) self.task_manager.create(name) config = copy.deepcopy(self.config) metadata = self.storage.find_instance_metadata(name) if metadata and "plan_name" in metadata: plan = self.storage.find_plan(metadata["plan_name"]) config.update(plan.config or {}) healthcheck_timeout = int(config.get("RPAAS_HEALTHCHECK_TIMEOUT", 600)) tags = [] extra_tags = config.get("INSTANCE_EXTRA_TAGS", "") if extra_tags: tags.append(extra_tags) config["HOST_TAGS"] = ",".join(tags) try: self.task_manager.update(name, uuid()) lb = LoadBalancer.find(name, config) if lb is None: raise storage.InstanceNotFoundError() length = len(lb.hosts) for idx, host in enumerate(lb.hosts): yield "Restoring host ({}/{}) {} ".format( idx + 1, length, host.id) restore_host_job = JobWaiting(host.restore, 0, reset_template=True, reset_tags=True) restore_host_job.start() while restore_host_job.is_alive(): yield "." time.sleep(1) if isinstance(restore_host_job.result, Exception): raise restore_host_job.result host.start() nginx_waiting = self.nginx_manager.wait_healthcheck restore_delay = int(config.get("RPAAS_RESTORE_DELAY", 30)) nginx_healthcheck_job = JobWaiting(nginx_waiting, restore_delay, host=host.dns_name, timeout=healthcheck_timeout, manage_healthcheck=False) nginx_healthcheck_job.start() while nginx_healthcheck_job.is_alive(): yield "." time.sleep(1) if isinstance(nginx_healthcheck_job.result, Exception): raise nginx_healthcheck_job.result yield ": successfully restored\n" except storage.InstanceNotFoundError: yield "instance {} not found\n".format(name) except Exception as e: yield ": failed to restore - {}\n".format(repr(e.message)) finally: self.task_manager.remove(name)
def _get_address(self, name): task = self.storage.find_task(name) if task.count() >= 1: result = tasks.NewInstanceTask().AsyncResult(task[0]["task_id"]) if result.status in ["FAILURE", "REVOKED"]: return FAILURE return PENDING lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() return lb.address
def delete_route(self, name, path): self._ensure_ready(name) path = path.strip() if path == '/': raise RouteError("You cannot remove a route for / location, unbind the app.") lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.delete_binding_path(name, path) for host in lb.hosts: self.nginx_manager.delete_binding(host.dns_name, path)
def purge_location(self, name, path): self.task_manager.ensure_ready(name) path = path.strip() lb = LoadBalancer.find(name) purged_hosts = 0 if lb is None: raise storage.InstanceNotFoundError() for host in lb.hosts: if self.nginx_manager.purge_location(host.dns_name, path): purged_hosts += 1 return purged_hosts
def test_add_host(self): h1 = Host('x', 'x.me.com') h2 = Host('y', 'y.me.com') conf = {'LB_ID': 'explode'} lb = LoadBalancer.create('fake', 'my-lb', conf) lb.add_host(h1) lb.add_host(h2) self.assertItemsEqual(lb.hosts, [h1, h2]) db_lb = LoadBalancer.find('my-lb', conf) self.assertEqual(db_lb.hosts[0].config, conf) self.assertEqual(db_lb.hosts[1].config, conf) self.assertItemsEqual([h.to_json() for h in db_lb.hosts], [h1.to_json(), h2.to_json()])
def delete_route(self, name, path): self._ensure_ready(name) path = path.strip() if path == '/': raise RouteError( "You cannot remove a route for / location, unbind the app.") lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() self.storage.delete_binding_path(name, path) for host in lb.hosts: self.nginx_manager.delete_binding(host.dns_name, path)
def unbind(self, name): self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() binding_data = self.storage.find_binding(name) if not binding_data: return bound_host = binding_data.get("app_host") self.storage.remove_root_binding(name) self.consul_manager.write_location(name, "/", content=nginx.NGINX_LOCATION_INSTANCE_NOT_BOUND) self.consul_manager.remove_server_upstream(name, "rpaas_default_upstream", bound_host)
def new_instance(self, name, team=None): used, quota = self.storage.find_team_quota(team) if len(used) >= quota: raise QuotaExceededError(len(used), quota) if not self.storage.increment_quota(team, used, name): raise Exception("concurrent operations updating team quota") lb = LoadBalancer.find(name) if lb is not None: raise storage.DuplicateError(name) self.storage.store_task(name) task = tasks.NewInstanceTask().delay(self.config, name) self.storage.update_task(name, task.task_id)
def run(self, config, name): self.init_config(config) lb = LoadBalancer.find(name, self.config) if lb is None: raise storage.InstanceNotFoundError() for host in lb.hosts: self._delete_host(name, host, lb) self.consul_manager.destroy_instance(name) lb.destroy() for cert in self.storage.find_le_certificates({'name': name}): self.storage.remove_le_certificate(name, cert['domain']) self.hc.destroy(name)
def add_upstream(self, name, upstream_name, servers, acl=False): self.task_manager.ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() if acl: for host in lb.hosts: if not isinstance(servers, list): servers = [servers] for server in servers: dst_host, _ = host_from_destination(server) self.acl_manager.add_acl(name, host.dns_name, dst_host) self.consul_manager.add_server_upstream(name, upstream_name, servers)
def list_blocks(self, name): self._ensure_ready(name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() blocks = self.consul_manager.list_blocks(name) block_list = [] if blocks[1]: block_list = [ {'block_name': block['Key'].split('/')[-2], 'content': block['Value']} for block in blocks[1] ] return block_list
def restore_machine_instance(self, name, machine, cancel_task=False): task_name = "restore_{}".format(machine) if cancel_task: self.task_manager.remove(task_name) return self.task_manager.ensure_ready(task_name) lb = LoadBalancer.find(name) if lb is None: raise storage.InstanceNotFoundError() machine_data = self.storage.find_host_id(machine) if machine_data is None: raise InstanceMachineNotFoundError() self.task_manager.create({"_id": task_name, "host": machine, "instance": name, "created": datetime.datetime.utcnow()})