def join(connection, worker, skip_verify, disable_low_memory_guard): """ Join the node to a cluster CONNECTION: the cluster connection endpoint in format <master>:<port>/<token> """ connection_parts = connection.split("/") verify = not skip_verify if is_low_memory_guard_enabled() and disable_low_memory_guard: os.remove( os.path.expandvars("$SNAP_DATA/var/lock/low-memory-guard.lock")) if is_low_memory_guard_enabled() and not worker: print(""" This node does not have enough RAM to host the Kubernetes control plane services and join the database quorum. You may consider joining this node as a worker instead: microk8s join {connection} --worker If you would still like to join the cluster as a control plane node, use: microk8s join {connection} --disable-low-memory-guard """.format(connection=connection)) sys.exit(1) if is_node_running_dqlite(): join_dqlite(connection_parts, verify, worker) else: join_etcd(connection_parts, verify) sys.exit(0)
def reset(node, force): """ Remove a node from the cluster """ if is_node_running_dqlite(): remove_dqlite_node(node, force) else: remove_node(node) sys.exit(0)
def leave(): """ The node will depart from the cluster it is in. """ if is_node_running_dqlite(): if is_node_dqlite_worker(): reset_current_dqlite_worker_installation() else: reset_current_dqlite_installation() else: reset_current_etcd_installation() sys.exit(0)
def join(connection, worker, skip_verify): """ Join the node to a cluster CONNECTION: the cluster connection endpoint in format <master>:<port>/<token> """ connection_parts = connection.split("/") verify = not skip_verify if is_node_running_dqlite(): join_dqlite(connection_parts, verify, worker) else: join_etcd(connection_parts, verify) sys.exit(0)
def print_pretty(token, check): default_ip, all_ips, port = get_network_info() print("From the node you wish to join to this cluster, run the following:") print(f"microk8s join {default_ip}:{port}/{token}/{check}\n") if is_node_running_dqlite(): print( "Use the '--worker' flag to join a node as a worker not running the control plane, eg:" ) print(f"microk8s join {default_ip}:{port}/{token}/{check} --worker\n") print( "If the node you are adding is not reachable through the default interface you can use one of the following:" ) for ip in all_ips: print(f"microk8s join {ip}:{port}/{token}/{check}")
def do_op(remote_op): """ Perform an operation on a remote node :param remote_op: the operation json string """ if is_node_running_dqlite(): try: hostname = socket.gethostname() with open(callback_token_file, "r+") as fp: token = fp.read() subprocess.check_output( "{}/microk8s-status.wrapper --wait-ready --timeout=60".format( snap_path).split()) nodes_info = subprocess.check_output( "{}/microk8s-kubectl.wrapper get no -o json".format( snap_path).split()) info = json.loads(nodes_info.decode()) for node_info in info["items"]: node_ip = get_internal_ip_from_get_node(node_info) if is_same_server(hostname, node_ip): continue print("Configuring node {}".format(node_ip)) # TODO: make port configurable node_ep = "{}:{}".format(node_ip, "25000") remote_op["callback"] = token.rstrip() # TODO: handle ssl verification res = requests.post( "https://{}/{}/configure".format(node_ep, CLUSTER_API), json=remote_op, verify=False, ) if res.status_code != 200: print("Failed to perform a {} on node {} {}".format( remote_op["action_str"], node_ep, res.status_code)) except subprocess.CalledProcessError as e: print("Could not query for nodes") raise SystemExit(e) except requests.exceptions.RequestException as e: print("Failed to reach node.") raise SystemExit(e) else: with open(callback_tokens_file, "r+") as fp: for _, line in enumerate(fp): parts = line.split() node_ep = parts[0] host = node_ep.split(":")[0] print("Applying to node {}.".format(host)) try: # Make sure this node exists subprocess.check_call( "{}/microk8s-kubectl.wrapper get no {}".format( snap_path, host).split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) token = parts[1] remote_op["callback"] = token # TODO: handle ssl verification res = requests.post( "https://{}/{}/configure".format(node_ep, CLUSTER_API), json=remote_op, verify=False, ) if res.status_code != 200: print("Failed to perform a {} on node {}".format( remote_op["action_str"], node_ep)) except subprocess.CalledProcessError: print("Node {} not present".format(host))
"name": addon, state: "true" }], } do_op(remote_op) def usage(): print("usage: dist_refresh_opt [OPERATION] [SERVICE] (ARGUMENT) (value)") print( "OPERATION is one of restart, update_argument, remove_argument, set_addon" ) if __name__ == "__main__": if is_node_running_dqlite() and not os.path.isfile(callback_token_file): # print("Single node cluster.") exit(0) if not is_node_running_dqlite() and not os.path.isfile( callback_tokens_file): print("No callback tokens file.") exit(1) try: opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) except getopt.GetoptError as err: # print help information and exit: print(err) # will print something like "option -a not recognized" usage() sys.exit(2)
if o in ("-h", "--help"): usage() sys.exit(1) elif o in ("-f", "--force"): force = True else: print("Unhandled option") sys.exit(1) if len(args) <= 0: print("Please provide a connection string.") usage() sys.exit(4) elif args[0] == "reset": if len(args) > 1: if is_node_running_dqlite(): remove_dqlite_node(args[1], force) else: remove_node(args[1]) else: if is_node_running_dqlite(): reset_current_dqlite_installation() else: reset_current_etcd_installation() else: connection_parts = args[0].split("/") if is_node_running_dqlite(): join_dqlite(connection_parts) else: join_etcd(connection_parts)