def convert_project_argument_to_uuid(auth, argument_value: str, organization_id: str = None) -> str: # uuid provided (no conversion required) if is_valid_uuid4(argument_value): return argument_value # get available context options or use provided data (e.g. from previous query) graph_ql = GraphQL(authentication=auth) data = graph_ql.query( """ query($organization_id: UUID) { allProjects(organizationId: $organization_id) { results { title id } } } """, query_variables={ "organization_id": organization_id, }, ) results = data["allProjects"]["results"] return __select_result(argument_value, results, exception_message="project")
def organization_list(ctx) -> Union[None, str]: # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query(""" query { allOrganizations { results { id title } } } """) organization_list = data["allOrganizations"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() selection = console.list( message="Please select an organization", choices=[organization["title"] for organization in organization_list], identifiers=[organization["id"] for organization in organization_list], message_no_choices="No organizations available!", ) if selection is None: return None # get identifier if available organization_argument = get_identifier_or_pass(selection) organization_id = convert_organization_argument_to_uuid( ctx.auth, argument_value=organization_argument) return organization_id
def get_deck(ctx, deck_id: str): # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID) { deck(id: $id) { id title environment { id type valuesPath namespace } project { id title organization { title } } } } """, query_variables={"id": deck_id}, ) deck = data["deck"] except Exception as e: console.debug(e) console.exit_generic_error() return deck
def info(ctx, project=None, organization=None, **kwargs): """ Displays the id, title and optional description of the selected project. """ _ = ctx.auth.refresh() # context organization_id, project_id, _ = ctx.context.get_context_ids_from_arguments( organization_argument=organization, project_argument=project) # argument if not project_id: project_id = console.project_list(ctx, organization_id=organization_id) if not project_id: return None # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID!) { project(id: $id) { id title description specRepository specRepositoryBranch organization { title } } } """, query_variables={"id": project_id}, ) project_selected = data["project"] except Exception as e: console.debug(e) console.exit_generic_error() # console if project_selected: project_selected["organization"] = project_selected.pop( "organization").get("title", "-") project_selected["repository"] = project_selected.pop("specRepository") project_selected["repository branch"] = project_selected.pop( "specRepositoryBranch") console.table( data={ "key": [k for k in project_selected.keys()], "value": [v for v in project_selected.values()], }, headers=["Key", "Value"], ) else: console.error("Project does not exist.")
def get_deck_from_arguments(ctx, organization_id: str, project_id: str, deck_id: str): # context organization_id, project_id, deck_id = ctx.context.get_context_ids_from_arguments( organization_argument=organization_id, project_argument=project_id, deck_argument=deck_id) # argument if not deck_id: deck_id = console.deck_list(ctx, organization_id=organization_id, project_id=project_id) if not deck_id: exit(1) # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID) { deck(id: $id) { id title environment { namespace } project { id } } } """, query_variables={"id": deck_id}, ) deck = data["deck"] project_id = deck["project"]["id"] except Exception as e: console.debug(e) console.exit_generic_error() # cluster data cluster_list = ctx.cluster_manager.get_cluster_list(ready=True) if project_id not in [cluster.id for cluster in cluster_list]: console.info( f"The project cluster for '{project_id}' is not up or does not exist yet.", _exit=True) cluster_data = ctx.cluster_manager.get(id=project_id) if not cluster_data: console.error("The cluster could not be found.", _exit=True) return cluster_data, deck
def prune(ctx, **kwargs): """ Remove unused clusters. """ # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query(""" query { allProjects { results { id } } } """) projects = data["allProjects"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() # cluster cluster_list = ctx.cluster_manager.get_cluster_list() # select clusters to prune prune_clusters = [] for cluster_data in cluster_list: if cluster_data.id not in [project["id"] for project in projects]: prune_clusters.append(cluster_data) for cluster_data in prune_clusters: console.info( f"It seems like the project for cluster '{cluster_data.name}' has been deleted." ) # confirm question confirmed = console.confirm( question="Do want to remove the cluster? [N/y]: ") if not confirmed: console.info("No action taken.") continue # delete try: cluster = ctx.cluster_manager.select(cluster_data=cluster_data) success = cluster.delete() if success: console.success("The project was deleted successfully.") ctx.cluster_manager.delete(cluster.id) except Exception as e: console.debug(e) console.error("The cluster could not be deleted.")
def project_list(ctx, organization_id: str = None, filter: List[str] = None, excludes: List[str] = None) -> Union[None, str]: # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($organization_id: UUID) { allProjects(organizationId: $organization_id) { results { title id organization { id title } } } } """, query_variables={ "organization_id": organization_id, }, ) project_list = data["allProjects"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() selection = console.list( message="Please select a project", choices=[project["title"] for project in project_list], identifiers=[project["id"] for project in project_list], filter=filter, excludes=excludes, help_texts=[ project["organization"]["title"] for project in project_list ], message_no_choices="No projects available!", ) if selection is None: return None # get identifier if available project_argument = get_identifier_or_pass(selection) project_id = convert_project_argument_to_uuid( ctx.auth, argument_value=project_argument, organization_id=organization_id) return project_id
def deck_list(ctx, organization_id: str = None, project_id: str = None) -> Union[None, str]: # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($organization_id: UUID, $project_id: UUID) { allDecks(organizationId: $organization_id, projectId: $project_id) { results { title id project { id title organization { id } } } } } """, query_variables={ "organization_id": organization_id, "project_id": project_id, }, ) deck_list = data["allDecks"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() selection = console.list( message="Please select a deck", choices=[deck["title"] for deck in deck_list], identifiers=[deck["id"] for deck in deck_list], help_texts=[deck["project"]["title"] for deck in deck_list], message_no_choices="No decks available!", ) if selection is None: return None # get identifier if available deck_argument = get_identifier_or_pass(selection) deck_id = convert_deck_argument_to_uuid(ctx.auth, argument_value=deck_argument, organization_id=organization_id, project_id=project_id) return deck_id
def info(ctx, organization=None, project=None, deck=None, **kwargs): """ Display further information of the selected deck. """ # context organization_id, project_id, deck_id = ctx.context.get_context_ids_from_arguments( organization_argument=organization, project_argument=project, deck_argument=deck) # argument if not deck_id: deck_id = console.deck_list(ctx, organization_id=organization_id, project_id=project_id) if not deck_id: return None # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID) { deck(id: $id) { id title description namespace type } } """, query_variables={"id": deck_id}, ) deck_selected = data["deck"] except Exception as e: console.debug(e) console.exit_generic_error() # console if deck_selected: console.table( data={ "key": [k for k in deck_selected.keys()], "value": [v for v in deck_selected.values()], }, headers=["Key", "Value"], ) else: console.error("Deck does not exist.")
def info(ctx, organization, **kwargs): """ Display further information of the selected organization. """ _ = ctx.auth.refresh() # context organization_id, _, _ = ctx.context.get_context_ids_from_arguments(organization_argument=organization) # argument if not organization_id: organization_id = console.organization_list(ctx) if not organization_id: return None # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID!) { organization(id: $id) { id title description } } """, query_variables={"id": organization_id}, ) organization_selected = data["organization"] except Exception as e: console.debug(e) console.exit_generic_error() # console if organization_selected: console.table( data={ "key": [k for k in organization_selected.keys()], "value": [v for v in organization_selected.values()], }, headers=["Key", "Value"], ) else: console.error("Organization does not exist.")
def list(ctx, organization, **kwargs): """ Display a table of all available project names alongside with the ids. """ _ = ctx.auth.refresh() # context organization_id, _, _ = ctx.context.get_context_ids_from_arguments( organization_argument=organization) # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($organization_id: UUID) { allProjects(organizationId: $organization_id) { results { title id description } } } """, query_variables={"organization_id": organization_id}, ) project_list = data["allProjects"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() # console if len(project_list) < 1: console.info( "No projects available. Please go to https://app.unikube.io and create a project.", _exit=True) console.table( data={ "id": [p["id"] for p in project_list], "title": [p["title"] for p in project_list], "description": [p["description"] for p in project_list], }, headers=["id", "name", "description"], )
def convert_organization_argument_to_uuid(auth, argument_value: str) -> str: # uuid provided (no conversion required) if is_valid_uuid4(argument_value): return argument_value # get available context options or use provided data (e.g. from previous query) graph_ql = GraphQL(authentication=auth) data = graph_ql.query(""" query { allOrganizations { results { title id } } } """) results = data["allOrganizations"]["results"] return __select_result(argument_value, results, exception_message="organization")
def organization_id_2_display_name(ctx, id: str = None) -> str: if not id: return "-" try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID!) { organization(id: $id) { title } } """, query_variables={ "id": id, }, ) title = data["organization"]["title"] except Exception as e: console.debug(e) title = "-" return f"{title} ({id})"
def list(ctx, organization=None, project=None, **kwargs): """ List all decks. """ # context organization_id, project_id, _ = ctx.context.get_context_ids_from_arguments( organization_argument=organization, project_argument=project) # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($organization_id: UUID, $project_id: UUID) { allDecks(organizationId: $organization_id, projectId: $project_id) { results { id title project { title organization { title } } } } } """, query_variables={ "organization_id": organization_id, "project_id": project_id, }, ) deck_list = data["allDecks"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() if not deck_list: console.warning( "No decks available. Please go to https://app.unikube.io and create a project.", _exit=True) # format list to table table_data = [] for deck in deck_list: data = {} if not organization_id: data["organization"] = deck["project"]["organization"]["title"] if not project_id: data["project"] = deck["project"]["title"] data["id"] = deck["id"] data["title"] = deck["title"] table_data.append(data) # console console.table(data=table_data)
def switch(ctx, app, organization, project, deck, deployment, unikubefile: str = None, no_build: bool = False, **kwargs): """ Switch a running deployment with a local Docker container. """ cluster_data, deck = get_deck_from_arguments(ctx, organization, project, deck) # get cluster cluster = get_cluster_or_exit(ctx, cluster_data.id) # unikube file input try: unikube_file = unikube_file_selector.get(path_unikube_file=unikubefile) unikube_file_app = unikube_file.get_app(name=app) except Exception as e: console.debug(e) console.error("Invalid 'app' argument.", _exit=True) # 2: Get a deployment # 2.1.a Check the deployment identifier if not deployment and unikube_file_app: # 1.1.b check the unikubefile deployment = unikube_file_app.get_deployment() if not deployment: console.error( "Please specify the 'deployment' key of your app in your unikube.yaml.", _exit=True) else: console.error( "Please specify the deployment either using the '--deployment' option or in the unikube.yaml. " "Run 'unikube app switch' in a directory containing the unikube.yaml file.", _exit=True, ) # 2.2 Fetch available "deployment:", deployments # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID) { deck(id: $id) { deployments(level: "local") { id title description ports isSwitchable } environment { id type valuesPath namespace } } } """, query_variables={ "id": deck["id"], }, ) except Exception as e: console.debug(e) console.exit_generic_error() target_deployment = None for _deployment in data["deck"]["deployments"]: if _deployment["title"] == deployment: target_deployment = _deployment # 2.3 Check and select deployment data if target_deployment is None: console.error( f"The deployment '{deployment}' you specified could not be found.", _exit=True, ) ports = target_deployment["ports"].split(",") deployment = target_deployment["title"] namespace = deck["environment"][0]["namespace"] console.info("Please wait while unikube prepares the switch.") with click_spinner.spinner(beep=False, disable=False, force=False, stream=sys.stdout): # check telepresence provider_data = cluster.storage.get() telepresence = Telepresence(provider_data) available_deployments = telepresence.list(namespace, flat=True) if deployment not in available_deployments: console.error( "The given deployment cannot be switched. " f"You may have to run 'unikube deck install {deck}' first.", _exit=True, ) is_swapped = telepresence.is_swapped(deployment, namespace) k8s = KubeAPI(provider_data, deck) # service account token, service cert service_account_tokens = k8s.get_serviceaccount_tokens(deployment) # 3: Build an new Docker image # 3.1 Grab the docker file context, dockerfile, target = unikube_file_app.get_docker_build() if not target: target = "" console.debug(f"{context}, {dockerfile}, {target}") # 3.2 Set an image name image_name = settings.TELEPRESENCE_DOCKER_IMAGE_FORMAT.format( project=cluster_data.name.replace(" ", "").lower(), deck=deck["title"], name=deployment) docker = Docker() if is_swapped: console.warning( "It seems this app is already switched in another process. ") if click.confirm("Do you want to kill it and switch here?"): telepresence.leave(deployment, namespace, silent=True) if docker.check_running(image_name): docker.kill(name=image_name) else: sys.exit(0) # 3.3 Build image if not docker.image_exists(image_name) or not no_build: if no_build: console.warning( f"Ignoring --no-build since the required image '{image_name}' does not exist" ) console.info( f"Building a Docker image for {dockerfile} with context {context}") with click_spinner.spinner(beep=False, disable=False, force=False, stream=sys.stdout): status, msg = docker.build(image_name, context, dockerfile, target) if not status: console.debug(msg) console.error("Failed to build Docker image.", _exit=True) console.info(f"Docker image successfully built: {image_name}") # 4. Start the Telepresence session # 4.1 Set the right intercept port port = unikube_file_app.get_port() if port is None: port = str(ports[0]) if len(ports) > 1: console.warning( f"No port specified although there are multiple ports available: {ports}. " f"Defaulting to port {port} which might not be correct.") if port not in ports: console.error( f"The specified port {port} is not in the rage of available options: {ports}", _exit=True) if not _is_local_port_free(port): console.error( f"The local port {port} is busy. Please stop the application running on " f"this port and try again.", _exit=True, ) # 4.2 See if there are volume mounts mounts = unikube_file_app.get_mounts() console.debug(f"Volumes requested: {mounts}") # mount service tokens if service_account_tokens: tmp_sa_token = tempfile.NamedTemporaryFile(delete=True) tmp_sa_cert = tempfile.NamedTemporaryFile(delete=True) tmp_sa_token.write(service_account_tokens[0].encode()) tmp_sa_cert.write(service_account_tokens[1].encode()) tmp_sa_token.flush() tmp_sa_cert.flush() mounts.append((tmp_sa_token.name, settings.SERVICE_TOKEN_FILENAME)) mounts.append((tmp_sa_cert.name, settings.SERVICE_CERT_FILENAME)) else: tmp_sa_token = None tmp_sa_cert = None # 4.3 See if there special env variables envs = unikube_file_app.get_environment() console.debug(f"Envs requested: {envs}") # 4.4 See if there is a run command to be executed command = unikube_file_app.get_command(port=port) console.debug(f"Run command: {command}") console.info( "Starting your container, this may take a while to become effective") telepresence.swap(deployment, image_name, command, namespace, envs, mounts, port) if docker.check_running(image_name): docker.kill(name=image_name) if tmp_sa_token: tmp_sa_token.close() tmp_sa_cert.close()
def ps(ctx, **kwargs): """ Displays the current process state. """ # cluster cluster_list = ctx.cluster_manager.get_cluster_list(ready=True) cluster_id_list = [cluster.id for cluster in cluster_list] # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query { allProjects { results { title id description } } } """, ) project_list = data["allProjects"]["results"] except Exception as e: console.debug(e) console.exit_generic_error() cluster_data = [] for project in project_list: if project["id"] in cluster_id_list: cluster_data.append(project) console.info("Project:") console.table( data={ "id": [cluster["id"] for cluster in cluster_data], "title": [cluster["title"] for cluster in cluster_data], "description": [cluster["description"] for cluster in cluster_data], }, headers=["cluster: id", "name", "description"], ) console.echo("") # switch intercept_count = 0 if cluster_data: cluster = get_cluster_or_exit(ctx, cluster_data[0]["id"]) provider_data = cluster.storage.get() telepresence = Telepresence(provider_data) intercept_count = telepresence.intercept_count() if intercept_count == 0 or not intercept_count: console.info("No app switched!") else: console.info(f"Apps switched: #{intercept_count}") console.echo("") # context local_storage_user = get_local_storage_user() user_data = local_storage_user.get() show_context(ctx=ctx, context=user_data.context)
def up(ctx, project=None, organization=None, ingress=None, provider=None, workers=None, **kwargs): """ This command starts or resumes a Kubernetes cluster for the specified project. As it is a selection command, the project can be specified and/or filtered in several ways: * as a positional argument, id or project title can be specified, or from a set context * as an interactive selection from available projects * via ``-o`` or ``--organization`` option, specifying an organisation to which a project belongs """ _ = ctx.auth.refresh() if not Docker().daemon_active(): console.error( "Docker is not running. Please start Docker before starting a project.", _exit=True) # context organization_id, project_id, _ = ctx.context.get_context_ids_from_arguments( organization_argument=organization, project_argument=project) # cluster information cluster_list = ctx.cluster_manager.get_cluster_list(ready=True) cluster_id_list = [item.id for item in cluster_list] # argument if not project_id: project_id = console.project_list(ctx, organization_id=organization_id, excludes=cluster_id_list) if not project_id: return None if project_id in cluster_id_list: console.info( f"Project '{project_id_2_display_name(ctx=ctx, id=project_id)}' is already up.", _exit=True) # GraphQL try: graph_ql = GraphQL(authentication=ctx.auth) data = graph_ql.query( """ query($id: UUID) { project(id: $id) { title id organization { id } clusterSettings { id port } organization { title } } } """, query_variables={ "id": project_id, }, ) project_selected = data["project"] except Exception as e: console.debug(e) console.exit_generic_error() if not project_selected: console.info( f"The project '{project_id_2_display_name(ctx=ctx, id=project_id)}' could not be found.", _exit=True) try: cluster_provider_type = K8sProviderType[provider] except KeyError: console.error( f"The provider '{provider}' is not supported. Please use " f"one of: {','.join(opt.name for opt in K8sProviderType)}", _exit=True, ) check_running_cluster(ctx, cluster_provider_type, project_selected) # get project id if ingress is None: ingress = project_selected["clusterSettings"]["port"] if not_available_ports := check_ports([ingress]): console.error( "Following ports are currently busy, however needed to spin up the cluster: {}" .format(", ".join([str(port) for port in not_available_ports])), _exit=True, )