diff --git a/cli/src/pcluster/api/util.py b/cli/src/pcluster/api/util.py index 9921ac13e0..0f014cced1 100644 --- a/cli/src/pcluster/api/util.py +++ b/cli/src/pcluster/api/util.py @@ -178,6 +178,9 @@ def _assert_node_executable(): def _assert_node_version(): + node_version_string = None + + # Try to get version from node executable try: # A nosec comment is appended to the following line in order to disable the B607 and B603 checks. # It is a false positive since the PATH search is wanted and the input of the check_output is static. @@ -187,10 +190,17 @@ def _assert_node_version(): # untrusted source node_version_string = subprocess.check_output( # nosec B607 B603 ["node", "--version"], stderr=subprocess.STDOUT, shell=False, encoding="utf-8" - ) + ).strip() LOGGER.debug("Found Node.js version (%s)", node_version_string) - except Exception: - LOGGER.debug("Unable to determine current Node.js version from node") + except subprocess.CalledProcessError as e: + LOGGER.debug("Node executable failed with exit code %s: %s", e.returncode, e.output) + except FileNotFoundError: + LOGGER.debug("Node executable not found") + except Exception as e: + LOGGER.debug("Unable to determine current Node.js version from node: %s", e) + + # If node command failed, try nvm + if not node_version_string: try: # A nosec comment is appended to the following line in order to disable the B607 and B603 checks. # It is a false positive since the PATH search is wanted and the input of the check_output is static. @@ -200,24 +210,29 @@ def _assert_node_version(): # untrusted source node_version_string = subprocess.check_output( # nosec B607 B603 ["nvm", "current"], stderr=subprocess.STDOUT, shell=False, encoding="utf-8" - ) + ).strip() LOGGER.debug("Found Node.js version '%s' in use", node_version_string) - except Exception: - message = "Unable to check Node.js version" - LOGGER.critical(message) - raise Exception(message) - # `nvm current` will return `none` if no versions of Node.js are currently installed. - if node_version_string == "none": - message = ( - "Node.js does not appear to be installed. Please use the Node Version Manager (nvm) to install a" - " version of Node.js compatible with this platform." - ) - else: - message = ( - f"Unable to invoke Node.js for the installed version {node_version_string}. This version may not be" - " compatible with this platform. Please use the Node Version Manager (nvm) to install and use a" - " compatible version of Node.js compatible with this platform." - ) + except FileNotFoundError: + LOGGER.debug("NVM not found") + except Exception as e: + LOGGER.debug("Unable to determine Node.js version from nvm: %s", e) + + # Handle case where we couldn't get version info + if not node_version_string: + message = ( + "Unable to check Node.js version. Node.js is required by the AWS CDK library used by ParallelCluster. " + "Please ensure Node.js is properly installed and accessible. " + "See installation instructions: https://docs.aws.amazon.com/parallelcluster/latest/ug/install-v3.html" + ) + LOGGER.critical(message) + raise Exception(message) + + # Handle nvm returning 'none' + if node_version_string == "none": + message = ( + "Node.js does not appear to be installed. Please use the Node Version Manager (nvm) to install a" + " version of Node.js compatible with this platform." + ) LOGGER.critical(message) raise Exception(message) diff --git a/tests/integration-tests/conftest.py b/tests/integration-tests/conftest.py index 101e7da1b2..26f0aece6d 100644 --- a/tests/integration-tests/conftest.py +++ b/tests/integration-tests/conftest.py @@ -399,8 +399,7 @@ def _setup_custom_logger(log_file): @pytest.fixture(scope="class") -@pytest.mark.usefixtures("setup_credentials") -def clusters_factory(request, region): +def clusters_factory(request, region, setup_credentials): """ Define a fixture to manage the creation and destruction of clusters. @@ -512,8 +511,7 @@ def api_client(region, api_server_factory, api_uri): @pytest.fixture(scope="class") -@pytest.mark.usefixtures("setup_credentials") -def images_factory(request): +def images_factory(request, setup_credentials): """ Define a fixture to manage the creation and destruction of images. @@ -924,8 +922,7 @@ def cfn_stacks_factory(request): @pytest.fixture() -@pytest.mark.usefixtures("setup_credentials") -def parameterized_cfn_stacks_factory(request): +def parameterized_cfn_stacks_factory(request, setup_credentials): """Define a fixture that returns a parameterized stack factory and manages the stack creation and deletion.""" factory = CfnStacksFactory(request.config.getoption("credential")) @@ -1020,8 +1017,7 @@ def register_cli_credentials(initialize_cli_creds): @pytest.fixture(scope="class") -@pytest.mark.usefixtures("clusters_factory", "images_factory") -def create_roles_stack(request, region): +def create_roles_stack(request, region, clusters_factory, images_factory): """Define a fixture that returns a stack factory for IAM roles.""" logging.info("Creating IAM roles stack") factory = CfnStacksFactory(request.config.getoption("credential")) diff --git a/tests/integration-tests/requirements.txt b/tests/integration-tests/requirements.txt index a25cab35cc..111c3f81c4 100644 --- a/tests/integration-tests/requirements.txt +++ b/tests/integration-tests/requirements.txt @@ -6,7 +6,7 @@ cfn_flip click decorator #https://github.com/fabric/fabric/issues/2204 -fabric==2.6.0 +fabric>=3.2.2 filelock jinja2 jsonpickle @@ -30,3 +30,4 @@ retrying troposphere untangle xmltodict +invoke>=2.2.0 \ No newline at end of file