def delete_management_cluster(cmd, yes=False): # pylint: disable=unused-argument exit_if_no_management_cluster() msg = 'Do you want to delete Cluster API components from the current cluster?' if not yes and not prompt_y_n(msg, default="n"): return command = ["clusterctl", "delete", "--all", "--include-crd", "--include-namespace"] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't delete components from management cluster") from err namespaces = [ "capi-kubeadm-bootstrap-system", "capi-kubeadm-control-plane-system", "capi-system", "capi-webhook-system", "capz-system", "cert-manager", ] command = ["kubectl", "delete", "namespace", "--ignore-not-found"] + namespaces try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't delete namespaces from management cluster") from err
def delete_management_cluster(cmd): # pylint: disable=unused-argument # TODO: add user confirmation command = [ "clusterctl", "delete", "--all", "--include-crd", "--include-namespace" ] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(err) namespaces = [ "capi-kubeadm-bootstrap-system", "capi-kubeadm-control-plane-system", "capi-system", "capi-webhook-system", "capz-system", "cert-manager", ] command = ["kubectl", "delete", "namespace", "--ignore-not-found" ] + namespaces try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(err)
def init_environment(cmd, prompt=True): check_prereqs(cmd, install=True) # Create a management cluster if needed try: find_management_cluster_retry(cmd) except ResourceNotFoundError as err: if str(err) == "No Cluster API installation found": _install_capz_components(cmd) except subprocess.CalledProcessError: if prompt: choices = ["kind - a local Docker container-based cluster", "AKS - a managed cluster in the Azure cloud", "exit - don't create a management cluster"] prompt = """ No Kubernetes cluster was found using the default configuration. Cluster API needs a "management cluster" to run its components. Learn more from the Cluster API Book: https://cluster-api.sigs.k8s.io/user/concepts.html Where do you want to create a management cluster? """ choice_index = prompt_choice_list(prompt, choices) else: choice_index = 0 cluster_name = "capi-manager" if choice_index == 0: check_kind(cmd, install=not prompt) begin_msg = 'Creating local management cluster "{}" with kind'.format(cluster_name) end_msg = '✓ Created local management cluster "{}"'.format(cluster_name) with Spinner(cmd, begin_msg, end_msg): command = ["kind", "create", "cluster", "--name", cluster_name] try: # if --verbose, don't capture stderr stderr = None if is_verbose() else subprocess.STDOUT output = subprocess.check_output(command, universal_newlines=True, stderr=stderr) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't create kind management cluster") from err elif choice_index == 1: with Spinner(cmd, "Creating Azure resource group", "✓ Created Azure resource group"): command = ["az", "group", "create", "-l", "southcentralus", "--name", cluster_name] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't create Azure resource group") from err with Spinner(cmd, "Creating Azure management cluster with AKS", "✓ Created AKS management cluster"): command = ["az", "aks", "create", "-g", cluster_name, "--name", cluster_name] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't create AKS management cluster") from err else: return _install_capz_components(cmd)
def find_kubectl_resource_names(resource_type, error_msg, kubeconfig=None): command = ["kubectl", "get", resource_type, "--output", "name"] command += add_kubeconfig_to_command(kubeconfig) try: return run_shell_command(command).splitlines() except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(error_msg) from err
def show_management_cluster(_cmd, yes=False): # TODO: check to see if a management cluster is specified in the config config = get_default_cli().config # Config can also be set by the AZURE_CAPI_KUBECONFIG environment variable. kubeconfig = config.get("capi", "kubeconfig", fallback=os.environ.get("KUBECONFIG")) if not kubeconfig: raise InvalidArgumentValueError("no kubeconfig") # make a $HOME/.azure/capi directory for storing cluster configurations path = os.path.join(get_config_dir(), "capi") if not os.path.exists(path): os.makedirs(path) # TODO: if not command = [ "kubectl", "config", "get-contexts", "--no-headers", "--output", "name" ] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) contexts = output.splitlines() logger.info(contexts) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(err) msg = path + "ok" if not yes and prompt_y_n(msg, default="n"): logger.info("yes")
def data_collection_rules_create(client, resource_group_name, data_collection_rule_name, rule_file, location=None, tags=None, description=None): from azure.cli.core.util import get_file_json from azure.cli.core.azclierror import FileOperationError, UnclassifiedUserFault body = {} body['location'] = location body['tags'] = tags body['description'] = description try: json_data = get_file_json(rule_file) except FileNotFoundError: raise FileOperationError("No such file: " + str(rule_file)) except IsADirectoryError: raise FileOperationError("Is a directory: " + str(rule_file)) except PermissionError: raise FileOperationError("Permission denied: " + str(rule_file)) except OSError as e: raise UnclassifiedUserFault(e) for key_prop in json_data: if key_prop == 'properties': data = json_data['properties'] else: data = json_data for key in data: if key == 'dataSources': body['data_sources'] = {} for key_ds in data['dataSources']: if key_ds == 'performanceCounters': body['data_sources']['performance_counters'] = data[ 'dataSources']['performanceCounters'] if key_ds == 'windowsEventLogs': body['data_sources']['windows_event_logs'] = data[ 'dataSources']['windowsEventLogs'] if key_ds == 'syslog': body['data_sources']['syslog'] = data['dataSources'][ 'syslog'] if key_ds == 'extensions': body['data_sources']['extensions'] = data['dataSources'][ 'extensions'] if key == 'destinations': body['destinations'] = {} for key_de in data['destinations']: if key_de == 'logAnalytics': body['destinations']['log_analytics'] = data[ 'destinations']['logAnalytics'] if key_de == 'azureMonitorMetrics': body['destinations']['azure_monitor_metrics'] = data[ 'destinations']['azureMonitorMetrics'] if key == 'dataFlows': body['data_flows'] = data['dataFlows'] return _data_collection_rules_create( client, resource_group_name=resource_group_name, data_collection_rule_name=data_collection_rule_name, body=body)
def init_environment(cmd, prompt=True, management_cluster_name=None, resource_group_name=None, location=None): check_prereqs(cmd, install=True) # Create a management cluster if needed use_new_cluster = False pre_prompt = None try: find_management_cluster_retry(cmd) cluster_name = find_cluster_in_current_context() if prompt and not prompt_y_n( f"Do you want to use {cluster_name} as the management cluster?" ): use_new_cluster = True else: return True except ResourceNotFoundError as err: error_msg = err.error_msg if management_cluster_components_missing_matching_expressions( error_msg): choices = [ "Create a new management cluster", "Use default kuberenetes cluster found and install CAPI required components", "Exit" ] msg = "The default kubernetes cluster found is missing required components for a management cluster.\ \nDo you want to:" index_choice = 0 if prompt: index_choice = prompt_choice_list(msg, choices) if index_choice == 0: use_new_cluster = True elif index_choice != 1: return False else: raise UnclassifiedUserFault(err) from err except subprocess.CalledProcessError: pre_prompt = """ No Kubernetes cluster was found using the default configuration. Cluster API needs a "management cluster" to run its components. Learn more from the Cluster API Book: https://cluster-api.sigs.k8s.io/user/concepts.html """ use_new_cluster = True if use_new_cluster and not create_new_management_cluster( cmd, management_cluster_name, resource_group_name, location, pre_prompt_text=pre_prompt, prompt=prompt): return False _create_azure_identity_secret(cmd) _install_capi_provider_components(cmd) return True
def find_nodes(kubeconfig): command = ["kubectl", "get", "nodes", "--output", "name", "--kubeconfig", kubeconfig] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) return output.splitlines() except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't get nodes of workload cluster") from err
def list_workload_clusters(cmd): cmd = ["kubectl", "get", "clusters", "-o", "json"] try: output = subprocess.check_output(cmd, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(cmd), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(err) return json.loads(output)
def list_workload_clusters(cmd): # pylint: disable=unused-argument exit_if_no_management_cluster() command = ["kubectl", "get", "clusters", "-o", "json"] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't list workload clusters") from err return json.loads(output)
def try_command_with_spinner(cmd, command, spinner_begin_msg, spinner_end_msg, error_msg, include_error_stdout=False): with Spinner(cmd, spinner_begin_msg, spinner_end_msg): try: run_shell_command(command) except (subprocess.CalledProcessError, FileNotFoundError) as err: if include_error_stdout: error_msg += f"\n{err.stdout}" raise UnclassifiedUserFault(error_msg) from err
def _run_command(bicep_installation_path, args): process = subprocess.run([rf"{bicep_installation_path}"] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: process.check_returncode() return process.stdout.decode("utf-8") except subprocess.CalledProcessError: raise UnclassifiedUserFault(process.stderr.decode("utf-8"))
def _install_capz_components(): os.environ["EXP_MACHINE_POOL"] = "true" os.environ["EXP_CLUSTER_RESOURCE_SET"] = "true" command = ["clusterctl", "init", "--infrastructure", "azure"] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault( "Can't locate a Kubernetes cluster") from err
def show_workload_cluster(cmd, capi_name): # pylint: disable=unused-argument exit_if_no_management_cluster() # TODO: --output=table could print the output of `clusterctl describe` directly. command = ["kubectl", "get", "cluster", capi_name, "--output", "json"] try: output = subprocess.check_output(command, stderr=subprocess.STDOUT, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't get the workload cluster {}".format(capi_name)) from err return json.loads(output)
def create_management_cluster(cmd): # TODO: add user confirmation check_preqreqs(cmd) command = ["clusterctl", "init", "--infrastructure", "azure"] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault( "Can't locate a Kubernetes cluster") from err
def delete_workload_cluster(cmd, capi_name, yes=False): exit_if_no_management_cluster() msg = 'Do you want to delete this Kubernetes cluster "{}"?'.format(capi_name) if not yes and not prompt_y_n(msg, default="n"): return cmd = ["kubectl", "delete", "cluster", capi_name] try: output = subprocess.check_output(cmd, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(cmd), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't delete workload cluster") from err
def create_management_cluster(cmd, yes=False): check_prereqs(cmd) msg = 'Do you want to initialize Cluster API on the current cluster?' if not yes and not prompt_y_n(msg, default="n"): return command = ["clusterctl", "init", "--infrastructure", "azure"] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Can't locate a Kubernetes cluster") from err
def _install_capz_components(cmd): os.environ["EXP_MACHINE_POOL"] = "true" os.environ["EXP_CLUSTER_RESOURCE_SET"] = "true" with Spinner(cmd, "Initializing management cluster", "✓ Initialized management cluster"): command = ["clusterctl", "init", "--infrastructure", "azure"] try: # if --verbose, don't capture stderr stderr = None if is_verbose() else subprocess.STDOUT output = subprocess.check_output(command, universal_newlines=True, stderr=stderr) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Can't locate a Kubernetes cluster") from err
def show_workload_cluster(cmd, name): # pylint: disable=unused-argument # TODO: --output=table should print the output of `clusterctl describe` directly. # command = ["clusterctl", "describe", "cluster", name] command = ["kubectl", "get", "cluster", name, "--output", "json"] try: output = subprocess.check_output(command, stderr=subprocess.STDOUT, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(err) return json.loads(output)
def get_kubeconfig(capi_name): cmd = ["clusterctl", "get", "kubeconfig", capi_name] # if --verbose, don't capture stderr stderr = None if is_verbose() else subprocess.STDOUT try: output = subprocess.check_output(cmd, universal_newlines=True, stderr=stderr) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't get kubeconfig") from err filename = capi_name + ".kubeconfig" with open(filename, "w") as kubeconfig_file: kubeconfig_file.write(output) return "Wrote kubeconfig file to {} ".format(filename)
def datamigration_register_ir(auth_key, ir_path=None): validate_os_env() if not is_user_admin(): raise UnclassifiedUserFault( "Failed: You do not have Administrator rights to run this command. Please re-run this command as an Administrator!" ) validate_input(auth_key) if ir_path is not None: install_gateway(ir_path) register_ir(auth_key)
def update_management_cluster(cmd): # Check for local prerequisites check_preqreqs(cmd) cmd = [ "clusterctl", "upgrade", "apply", "--management-group", "capi-system/cluster-api", "--contract", "v1alpha3", ] try: output = subprocess.check_output(cmd, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(cmd), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault(err)
def _run_command(bicep_installation_path, args): process = subprocess.run([rf"{bicep_installation_path}"] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: process.check_returncode() command_warnings = process.stderr.decode("utf-8") if command_warnings: _logger.warning(command_warnings) return process.stdout.decode("utf-8") except subprocess.CalledProcessError: stderr_output = process.stderr.decode("utf-8") errors = [] for line in stderr_output.splitlines(): if re.match(_bicep_diagnostic_warning_pattern, line): _logger.warning(line) else: errors.append(line) error_msg = os.linesep.join(errors) raise UnclassifiedUserFault(error_msg)
def update_management_cluster(cmd, yes=False): exit_if_no_management_cluster() msg = 'Do you want to update Cluster API components on the current cluster?' if not yes and not prompt_y_n(msg, default="n"): return # Check for clusterctl tool check_prereqs(cmd, install=yes) command = [ "clusterctl", "upgrade", "apply", "--management-group", "capi-system/cluster-api", "--contract", "v1alpha3", ] try: output = subprocess.check_output(command, universal_newlines=True) logger.info("%s returned:\n%s", " ".join(command), output) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault("Couldn't upgrade management cluster") from err
def create_aks_management_cluster(cmd, cluster_name, resource_group_name=None, location=None, yes=False): if not resource_group_name: msg = "Please name the resource group for the management cluster" resource_group_name = get_user_prompt_or_default(msg, cluster_name, skip_prompt=yes) if not location: default_location = "southcentralus" msg = f"Please provide a location for {resource_group_name} resource group" location = get_user_prompt_or_default(msg, default_location, skip_prompt=yes) if not create_resource_group(cmd, resource_group_name, location, yes): return False command = [ "az", "aks", "create", "-g", resource_group_name, "--name", cluster_name, "--generate-ssh-keys", "--network-plugin", "azure", "--network-policy", "calico" ] try_command_with_spinner(cmd, command, "Creating Azure management cluster with AKS", "✓ Created AKS management cluster", "Couldn't create AKS management cluster") os.environ[MANAGEMENT_RG_NAME] = resource_group_name with Spinner(cmd, "Obtaining AKS credentials", "✓ Obtained AKS credentials"): command = [ "az", "aks", "get-credentials", "-g", resource_group_name, "--name", cluster_name ] try: subprocess.check_call(command, universal_newlines=True) except subprocess.CalledProcessError as err: raise UnclassifiedUserFault( "Couldn't get credentials for AKS management cluster") from err return True
def pivot_cluster(cmd, target_cluster_kubeconfig): logger.warning("Starting Pivot Process") begin_msg = "Installing Cluster API components in target management cluster" end_msg = "✓ Installed Cluster API components in target management cluster" with Spinner(cmd, begin_msg, end_msg): set_azure_identity_secret_env_vars() _create_azure_identity_secret(cmd, target_cluster_kubeconfig) _install_capi_provider_components(cmd, target_cluster_kubeconfig) with Spinner(cmd, "Waiting for workload cluster machines to be ready", "✓ Workload cluster machines are ready"): wait_for_machines() command = [ "clusterctl", "move", "--to-kubeconfig", target_cluster_kubeconfig ] begin_msg = "Moving cluster objects into target cluster" end_msg = "✓ Moved cluster objects into target cluster" error_msg = "Could not complete clusterctl move action" try_command_with_spinner(cmd, command, begin_msg, end_msg, error_msg, True) cluster_name = find_cluster_in_current_context() if has_kind_prefix(cluster_name): delete_kind_cluster_from_current_context(cmd) else: resource_group = os.environ.get(MANAGEMENT_RG_NAME, None) if not resource_group: raise UnclassifiedUserFault( "Could not delete AKS management cluster, resource group missing" ) delete_aks_cluster(cmd, cluster_name, resource_group) # Merge workload cluster kubeconfig and default kubeconfig. # To preverse any previous existing contexts merge_kubeconfig(target_cluster_kubeconfig) logger.warning("Completed Pivot Process") return True
def create_management_cluster(cmd, cluster_name=None, resource_group_name=None, location=None, yes=False): check_prereqs(cmd) existing_cluster = find_cluster_in_current_context() found_cluster = False if existing_cluster: msg = f'Do you want to initialize Cluster API on the current cluster {existing_cluster}?' if yes or prompt_y_n(msg, default="n"): try: _find_default_cluster() found_cluster = True except subprocess.CalledProcessError as err: raise UnclassifiedUserFault( "Can't locate a Kubernetes cluster") from err if not found_cluster and not create_new_management_cluster( cmd, cluster_name, resource_group_name, location, prompt=not yes): return set_azure_identity_secret_env_vars() _create_azure_identity_secret(cmd) _install_capi_provider_components(cmd)
def map_azure_error_to_cli_error(azure_error): error_message = getattr(azure_error, "message", str(azure_error)) if isinstance(azure_error, HttpResponseError): status_code = getattr(azure_error, "status_code", None) if status_code: status_code = int(status_code) if status_code == 400: return BadRequestError(error_message) if status_code == 401: return UnauthorizedError(error_message) if status_code == 403: return ForbiddenError(error_message) if status_code == 404: return ResourceNotFoundError(error_message) if 400 <= status_code < 500: return UnclassifiedUserFault(error_message) if 500 <= status_code < 600: return AzureInternalError(error_message) return ServiceError(error_message) if isinstance(azure_error, ServiceRequestError): return ClientRequestError(error_message) if isinstance(azure_error, ServiceResponseError): return AzureResponseError(error_message) return ServiceError(error_message)
'auto-upgrade', 'all', True) prompt = az_cli.config.getboolean('auto-upgrade', 'prompt', True) cmd = ['az', 'upgrade', '--all', str(update_all)] if prompt: from knack.prompting import verify_is_a_tty, NoTTYException # pylint: disable=ungrouped-imports az_upgrade_run = True try: verify_is_a_tty() except NoTTYException: az_upgrade_run = False err_msg = "Unable to prompt for auto upgrade as no tty available. " \ "Run 'az config set auto-upgrade.prompt=no' to allow auto upgrade with no prompt." logger.warning(err_msg) telemetry.set_exception( UnclassifiedUserFault(err_msg), fault_type='auto-upgrade-failed') else: upgrade_exit_code = subprocess.call( cmd, shell=platform.system() == 'Windows') else: import os devnull = open(os.devnull, 'w') cmd.append('-y') upgrade_exit_code = subprocess.call( cmd, shell=platform.system() == 'Windows', stdout=devnull) if az_upgrade_run and upgrade_exit_code != 0: err_msg = "Auto upgrade failed with exit code {}".format( exit_code)
def get_github_access_token(cmd, scope_list=None): # pylint: disable=unused-argument if scope_list: for scope in scope_list: if scope not in GITHUB_OAUTH_SCOPES: raise ValidationError( "Requested github oauth scope is invalid") scope_list = ' '.join(scope_list) authorize_url = 'https://github.com/login/device/code' authorize_url_data = { 'scope': scope_list, 'client_id': GITHUB_OAUTH_CLIENT_ID } import requests import time from urllib.parse import parse_qs try: response = requests.post(authorize_url, data=authorize_url_data) parsed_response = parse_qs(response.content.decode('ascii')) device_code = parsed_response['device_code'][0] user_code = parsed_response['user_code'][0] verification_uri = parsed_response['verification_uri'][0] interval = int(parsed_response['interval'][0]) expires_in_seconds = int(parsed_response['expires_in'][0]) logger.warning( 'Please navigate to %s and enter the user code %s to activate and ' 'retrieve your github personal access token', verification_uri, user_code) timeout = time.time() + expires_in_seconds logger.warning("Waiting up to '%s' minutes for activation", str(expires_in_seconds // 60)) confirmation_url = 'https://github.com/login/oauth/access_token' confirmation_url_data = { 'client_id': GITHUB_OAUTH_CLIENT_ID, 'device_code': device_code, 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code' } pending = True while pending: time.sleep(interval) if time.time() > timeout: raise UnclassifiedUserFault( 'Activation did not happen in time. Please try again') confirmation_response = requests.post(confirmation_url, data=confirmation_url_data) parsed_confirmation_response = parse_qs( confirmation_response.content.decode('ascii')) if 'error' in parsed_confirmation_response and parsed_confirmation_response[ 'error'][0]: if parsed_confirmation_response['error'][0] == 'slow_down': interval += 5 # if slow_down error is received, 5 seconds is added to minimum polling interval elif parsed_confirmation_response['error'][ 0] != 'authorization_pending': pending = False if 'access_token' in parsed_confirmation_response and parsed_confirmation_response[ 'access_token'][0]: return parsed_confirmation_response['access_token'][0] except Exception as e: raise CLIInternalError( 'Error: {}. Please try again, or retrieve personal access token from the Github website' .format(e)) raise UnclassifiedUserFault( 'Activation did not happen in time. Please try again')