def test_no_nodejs_error(self, mocker, test_datadir): """Test expected message is printed out if nodejs is not installed.""" mocker.patch("pcluster.api.util.shutil.which", return_value=None) with pytest.raises(APIOperationException) as exc_info: run([ "update-cluster", "-r", "eu-west-1", "-n", "name", "-c", str(test_datadir / "config.yaml") ]) assert_that( exc_info.value.data.get("message")).matches("Node.js is required")
def test_error(self, mocker): api_response = {"message": "error"}, 400 mocker.patch( "pcluster.api.controllers.cluster_operations_controller.list_clusters", return_value=api_response, autospec=True, ) with pytest.raises(APIOperationException) as exc_info: command = ["list-clusters", "--region", "eu-west-1"] run(command) assert_that(exc_info.value.data).is_equal_to(api_response[0])
def test_error(self, mocker, test_datadir): api_response = {"message": "error"}, 400 mocker.patch( "pcluster.api.controllers.cluster_operations_controller.update_cluster", return_value=api_response, autospec=True, ) path = str(test_datadir / "config.yaml") with pytest.raises(APIOperationException) as exc_info: command = [ "update-cluster", "-r", "eu-west-1", "-n", "name", "-c", path ] run(command) assert_that(exc_info.value.data).is_equal_to(api_response[0])
def test_error(self, mocker, test_datadir): api_response = {"message": "error"}, 400 mocker.patch( "pcluster.api.controllers.image_operations_controller.build_image", return_value=api_response, autospec=True) path = str(test_datadir / "config.yaml") with pytest.raises(APIOperationException) as exc_info: command = [ "build-image", "--region", "eu-west-1", "--image-configuration", path, "--image-id", "image-id" ] run(command) assert_that(exc_info.value.data).is_equal_to(api_response[0])
def test_error(self, mocker): api_response = {"message": "error"}, 400 mocker.patch( "pcluster.api.controllers.image_logs_controller.get_image_stack_events", return_value=api_response, autospec=True, ) with pytest.raises(APIOperationException) as exc_info: command = [ "get-image-stack-events", "--region", "eu-west-1", "--image-id", "image-id" ] run(command) assert_that(exc_info.value.data).is_equal_to(api_response[0])
def test_execute(self, mocker, test_datadir): response_dict = { "cluster": { "clusterName": "cluster", "cloudformationStackStatus": "CREATE_IN_PROGRESS", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/cluster/aa", "region": "eu-west-1", "version": "3.0.0", "clusterStatus": "CREATE_IN_PROGRESS", } } response = CreateClusterResponseContent().from_dict(response_dict) create_cluster_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.create_cluster", return_value=response, autospec=True, ) path = str(test_datadir / "config.yaml") out = run( ["create-cluster", "--cluster-name", "cluster", "--cluster-configuration", path, "--region", "eu-west-1"] ) assert_that(out).is_equal_to(response_dict) assert_that(create_cluster_mock.call_args).is_length(2) expected_args = { "suppress_validators": None, "validation_failure_level": None, "dryrun": None, "rollback_on_failure": None, "region": "eu-west-1", "create_cluster_request_content": {"clusterName": "cluster", "clusterConfiguration": ""}, } create_cluster_mock.assert_called_with(**expected_args)
def test_execute(self, mocker): response_dict = { "cluster": { "clusterName": "cluster", "cloudformationStackStatus": "DELETE_IN_PROGRESS", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/cluster/aa", "region": "eu-west-1", "version": "3.0.0", "clusterStatus": "DELETE_IN_PROGRESS", } } response = DeleteClusterResponseContent().from_dict(response_dict) delete_cluster_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.delete_cluster", return_value=response, autospec=True, ) out = run(["delete-cluster", "--cluster-name", "cluster"]) expected = wire_translate(response) assert_that(out).is_equal_to(expected) assert_that(delete_cluster_mock.call_args).is_length(2) args_expected = {"region": None, "cluster_name": "cluster"} delete_cluster_mock.assert_called_with(**args_expected)
def test_execute(self, mocker): response_dict = { "status": "START_REQUESTED", "lastStatusUpdatedTime": "2021-01-01 00:00:00.000000+00:00" } response = UpdateComputeFleetResponseContent().from_dict(response_dict) update_compute_fleet_status_mock = mocker.patch( "pcluster.api.controllers.cluster_compute_fleet_controller.update_compute_fleet", return_value=response, autospec=True, ) out = run([ "update-compute-fleet", "--cluster-name", "cluster", "--status", "START_REQUESTED", ]) assert_that(out).is_equal_to(wire_translate(response)) assert_that(update_compute_fleet_status_mock.call_args).is_length( 2) # this is due to the decorator on list_clusters expected_args = { "region": None, "cluster_name": "cluster", "update_compute_fleet_request_content": { "status": "START_REQUESTED" }, } update_compute_fleet_status_mock.assert_called_with(**expected_args)
def test_execute(self, mocker): response_dict = { "nextToken": "99/nexttoken", "events": [ { "eventId": "00000000-aaaa-1111-bbbb-000000000000", "physicalResourceId": "arn:aws:cloudformation:eu-west-1:000000000000:stack/image/aa", "resourceStatus": "UPDATE_COMPLETE", "stackId": "arn:aws:cloudformation:eu-west-1:000000000000:stack/image/aaa", "stackName": "image", "logicalResourceId": "image", "resourceType": "AWS::CloudFormation::Stack", "timestamp": "2021-01-01T00:00:00.000Z", } ], } response = GetImageStackEventsResponseContent().from_dict(response_dict) get_image_stack_events_mock = mocker.patch( "pcluster.api.controllers.image_logs_controller.get_image_stack_events", return_value=response, autospec=True, ) out = run(["get-image-stack-events", "--image-id", "image"]) assert_that(out).is_equal_to(response_dict) assert_that(get_image_stack_events_mock.call_args).is_length(2) # this is due to the decorator on list_clusters expected_args = {"region": None, "image_id": "image", "next_token": None} get_image_stack_events_mock.assert_called_with(**expected_args)
def test_execute(self, mocker): response_dict = { "imageConfiguration": { "url": "s3://parallelcluster-0000000000000000-v1-do-not-delete/parallelcluster/3.0.0/config.yaml" }, "imageId": "aws-parallelcluster-3-0-0-ubuntu-1804-lts-hvm-arm64-202101010000", "creationTime": "2021-01-01T00:00:00.000Z", "imageBuildStatus": "BUILD_COMPLETE", "region": "eu-west-2", "ec2AmiInfo": { "amiName": "aws-parallelcluster-3.0.0-ubuntu-1804-lts-hvm-x86_64-202101010000 2021-01-01T00-00-00.000Z", "amiId": "ami-FEED0DEAD0BEEF000", "description": "AWS ParallelCluster AMI for ubuntu1804", "state": "AVAILABLE", "tags": [ {"key": "parallelcluster:lustre_version", "value": "5.4.0.1051.33"}, {"key": "parallelcluster:bootstrap_file", "value": "aws-parallelcluster-cookbook-3.0.0"}, ], "architecture": "x86_64", }, "version": "3.0.0", } response = DescribeImageResponseContent().from_dict(response_dict) describe_image_mock = mocker.patch( "pcluster.api.controllers.image_operations_controller.describe_image", return_value=response, autospec=True ) out = run(["describe-image", "--image-id", "image"]) assert_that(out).is_equal_to(wire_translate(response)) assert_that(describe_image_mock.call_args).is_length(2) # this is due to the decorator on list_clusters expected_args = {"region": None, "image_id": "image"} describe_image_mock.assert_called_with(**expected_args)
def test_error(self, mocker): api_response = {"message": "error"}, 400 mocker.patch( "pcluster.api.controllers.cluster_compute_fleet_controller.update_compute_fleet", return_value=api_response, autospec=True, ) with pytest.raises(APIOperationException) as exc_info: command = [ "update-compute-fleet", "--region", "eu-west-1", "--cluster-name", "name", "--status", "START_REQUESTED", ] run(command) assert_that(exc_info.value.data).is_equal_to(api_response[0])
def test_execute(self, mocker, test_datadir): response_dict = { "cluster": { "clusterName": "cluster", "cloudformationStackStatus": "UPDATE_IN_PROGRESS", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/cluster/aa", "region": "eu-west-1", "version": "3.0.0", "clusterStatus": "UPDATE_IN_PROGRESS", }, "changeSet": [{ "parameter": "Scheduling.SlurmQueues[queue0].ComputeResources[queue0-i0].MaxCount", "requestedValue": "100", "currentValue": "20", }], } response = UpdateClusterResponseContent().from_dict(response_dict) update_cluster_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.update_cluster", return_value=response, autospec=True, ) path = str(test_datadir / "config.yaml") command = [ "update-cluster", "--cluster-name", "cluster", "--cluster-configuration", path, ] out = run(command) assert_that(out).is_equal_to(response_dict) assert_that(update_cluster_mock.call_args).is_length(2) expected_args = { "update_cluster_request_content": { "clusterConfiguration": "" }, "cluster_name": "cluster", "dryrun": None, "force_update": None, "region": None, "suppress_validators": None, "validation_failure_level": None, } update_cluster_mock.assert_called_with(**expected_args)
def test_execute(self, mocker): response_dict = {"lastStatusUpdatedTime": "2021-01-01 00:00:00.000000+00:00", "status": "RUNNING"} response = DescribeComputeFleetResponseContent().from_dict(response_dict) describe_clusters_mock = mocker.patch( "pcluster.api.controllers.cluster_compute_fleet_controller.describe_compute_fleet", return_value=response, autospec=True, ) out = run(["describe-compute-fleet", "--cluster-name", "cluster"]) assert_that(out).is_equal_to(wire_translate(response)) assert_that(describe_clusters_mock.call_args).is_length(2) # this is due to the decorator on list_clusters expected_args = {"region": None, "cluster_name": "cluster"} describe_clusters_mock.assert_called_with(**expected_args)
def test_execute(self, mocker): response_dict = { "creationTime": "2021-01-01 00:00:00.000000+00:00", "headnode": { "launchTime": "2021-01-01T00:00:00+00:00", "instanceId": "i-099aaaaa7000ccccc", "publicIpAddress": "18.118.18.18", "instanceType": "t2.micro", "state": "running", "privateIpAddress": "10.0.0.32", }, "version": "3.0.0", "clusterConfiguration": { "url": ("https://parallelcluster-v1-do-not-delete.s3.amazonaws.com/parallelcluster/3.0.0/clusters/cluster/" "configs/cluster-config.yaml") }, "tags": [{ "value": "3.0.0", "key": "parallelcluster:version" }], "cloudFormationStackStatus": "CREATE_COMPLETE", "clusterName": "cluster", "computeFleetStatus": "RUNNING", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/name/0", "lastUpdatedTime": "2021-01-01 00:00:00.000000+00:00", "region": "us-west-2", "clusterStatus": "CREATE_COMPLETE", } response = DescribeClusterResponseContent().from_dict(response_dict) describe_clusters_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.describe_cluster", return_value=response, autospec=True, ) out = run(["describe-cluster", "--cluster-name", "cluster"]) expected = wire_translate(response) assert_that(out).is_equal_to(expected) assert_that(describe_clusters_mock.call_args).is_length( 2) # this is due to the decorator on list_clusters expected_args = {"region": None, "cluster_name": "cluster"} describe_clusters_mock.assert_called_with(**expected_args)
def test_execute(self, mocker, test_datadir): response_dict = { "image": { "imageId": "image-id", "imageBuildStatus": "BUILD_IN_PROGRESS", "cloudformationStackStatus": "CREATE_IN_PROGRESS", "cloudformationStackArn": "arn:aws:cloudformation:eu-west-1:000000000000:stack/image-id/aaa", "region": "eu-west-1", "version": "3.0.0", } } response = BuildImageResponseContent().from_dict(response_dict) describe_clusters_mock = mocker.patch( "pcluster.api.controllers.image_operations_controller.build_image", return_value=response, autospec=True, ) path = str(test_datadir / "config.yaml") out = run([ "build-image", "--image-configuration", path, "--image-id", "image-id", "--region", "eu-west-1" ]) assert_that(out).is_equal_to(response_dict) assert_that(describe_clusters_mock.call_args).is_length( 2) # this is due to the decorator on list_clusters expected_args = { "suppress_validators": None, "validation_failure_level": None, "dryrun": None, "rollback_on_failure": None, "region": "eu-west-1", "build_image_request_content": { "imageId": "image-id", "imageConfiguration": "" }, } describe_clusters_mock.assert_called_with(**expected_args)
def test_execute(self, mocker, set_env, args): export_logs_mock = mocker.patch( "pcluster.cli.commands.cluster_logs.Cluster.export_logs", return_value=args.get("output_file", "https://u.r.l."), ) set_env("AWS_DEFAULT_REGION", "us-east-1") command = ["export-cluster-logs"] + self._build_cli_args({ **REQUIRED_ARGS, **args }) out = run(command) if args.get("output_file") is not None: expected = {"path": os.path.realpath(args.get("output_file"))} else: expected = {"url": "https://u.r.l."} assert_that(out).is_equal_to(expected) assert_that(export_logs_mock.call_args).is_length(2) # verify arguments expected_params = { "bucket": "bucketname", "bucket_prefix": None, "keep_s3_objects": False, "filters": None, "start_time": None, "end_time": None, } expected_params.update(args) expected_params.update({ "output_file": args.get("output_file") and os.path.realpath(args.get("output_file")), "start_time": args.get("start_time") and to_utc_datetime(args["start_time"]), "end_time": args.get("end_time") and to_utc_datetime(args["end_time"]), }) export_logs_mock.assert_called_with(**expected_params)
def test_execute(self, mocker, args, mocked_api_response, expected_cli_response): list_clusters_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.list_clusters", return_value=mocked_api_response, autospec=True, ) out = run(["list-clusters"] + self._build_cli_args(args)) assert_that(out).is_equal_to(expected_cli_response) assert_that(list_clusters_mock.call_args).is_length( 2) # this is due to the decorator on list_clusters if "cluster_status" in args: # Asserting the cluster_status list separately because the order is not preserved assert_that(list_clusters_mock.call_args[1].get( "cluster_status")).contains_only(*args.get("cluster_status")) args["cluster_status"] = ANY base_args = { "region": None, "next_token": None, "cluster_status": None } list_clusters_mock.assert_called_with(**{**base_args, **args})
def test_execute(self, mocker): response_dict = { "images": [ { "imageId": "aws-parallelcluster-3-0-0-amzn2-hvm-x86-64-202107121836", "imageBuildStatus": "BUILD_COMPLETE", "region": "us-east-2", "version": "3.0.0", }, { # "imageId": "dlami-aws-parallelcluster-3-0-0-amzn2-hvm-x86-64-202106181651", "imageId": "dlami-aws-parallelcluster-3-0-0-truncated", "imageBuildStatus": "BUILD_COMPLETE", "region": "us-east-2", "version": "3.0.0", }, ] } response = ListImagesResponseContent().from_dict(response_dict) list_images_mock = mocker.patch( "pcluster.api.controllers.image_operations_controller.list_images", return_value=response, autospec=True, ) out = run(["list-images", "--image-status", "AVAILABLE"]) assert_that(out).is_equal_to(response_dict) assert_that(list_images_mock.call_args).is_length( 2) # this is due to the decorator on list_clusters expected_args = { "region": None, "next_token": None, "image_status": "AVAILABLE" } list_images_mock.assert_called_with(**expected_args)
def test_execute_with_wait(self, mocker): response_dict = { "cluster": { "clusterName": "cluster", "cloudformationStackStatus": "DELETE_IN_PROGRESS", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/cluster/aa", "region": "eu-west-1", "version": "3.0.0", "clusterStatus": "DELETE_IN_PROGRESS", } } delete_response_dict = { "message": "Successfully deleted cluster 'cluster'." } delete_response = DeleteClusterResponseContent().from_dict( response_dict) delete_cluster_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.delete_cluster", return_value=delete_response, autospec=True, ) cf_waiter_mock = mocker.patch("botocore.waiter.Waiter.wait") mock_aws_api(mocker) command = ["delete-cluster", "--cluster-name", "cluster", "--wait"] out = run(command) assert_that(out).is_equal_to(delete_response_dict) assert_that(delete_cluster_mock.call_args).is_length(2) args_expected = {"region": None, "cluster_name": "cluster"} delete_cluster_mock.assert_called_with(**args_expected) assert_that(cf_waiter_mock.call_args[1]).is_equal_to( {"StackName": "cluster"})
def test_execute(self, mocker, set_env, test_datadir, args): mocked_result = [ LogStream( FAKE_ID, "logstream", { "events": [ { "timestamp": 1622802790248, "message": ("2021-06-04 10:33:10,248 [DEBUG] CloudFormation client initialized " "with endpoint https://cloudformation.eu-west-1.amazonaws.com" ), "ingestionTime": 1622802842382, }, { "timestamp": 1622802790248, "message": ("2021-06-04 10:33:10,248 [DEBUG] Describing resource HeadNodeLaunchTemplate in " "stack test22"), "ingestionTime": 1622802842382, }, { "timestamp": 1622802790390, "message": ("2021-06-04 10:33:10,390 [INFO] -----------------------Starting build" "-----------------------"), "ingestionTime": 1622802842382, }, ], "nextForwardToken": "f/3618", "nextBackwardToken": "b/3619", "ResponseMetadata": {}, }, ) ] * 2 + [LogStream(FAKE_ID, "logstream", {})] get_image_log_events_mock = mocker.patch( "pcluster.api.controllers.image_logs_controller.ImageBuilder.get_log_events", side_effect=mocked_result) set_env("AWS_DEFAULT_REGION", "us-east-1") base_args = ["get-image-log-events"] command = base_args + self._build_cli_args({**REQUIRED_ARGS, **args}) os.environ["TZ"] = "Europe/London" time.tzset() out = run(command) expected = json.loads( (test_datadir / "pcluster-out.txt").read_text().strip()) assert_that(expected).is_equal_to(out) assert_that(get_image_log_events_mock.call_args).is_length(2) if args.get("limit", None): limit_val = get_image_log_events_mock.call_args[1].get("limit") assert_that(limit_val).is_type_of(int) # verify arguments kwargs = { "start_time": args.get("start_time", None) and to_utc_datetime(args["start_time"]), "end_time": args.get("end_time", None) and to_utc_datetime(args["end_time"]), "start_from_head": True if args.get("start_from_head", None) else None, "limit": int(args["limit"]) if args.get("limit", None) else None, "next_token": args.get("next_token", None), } get_image_log_events_mock.assert_called_with("log-stream-name", **kwargs)
def test_execute(self, mocker, set_env, args): logs = LogStreams() logs.log_streams = [ { "logStreamName": "ip-10-0-0-102.i-0717e670ad2549e72.cfn-init", "creationTime": 1622802842228, "firstEventTimestamp": 1622802790248, "lastEventTimestamp": 1622802893126, "lastIngestionTime": 1622802903119, "uploadSequenceToken": "4961...", "arn": ("arn:aws:logs:eu-west-1:111:log-group:/aws/parallelcluster/" "test22-202106041223:log-stream:ip-10-0-0-102.i-0717e670ad2549e72.cfn-init" ), "storedBytes": 0, }, { "logStreamName": "ip-10-0-0-102.i-0717e670ad2549e72.chef-client", "creationTime": 1622802842207, "firstEventTimestamp": 1622802837114, "lastEventTimestamp": 1622802861226, "lastIngestionTime": 1622802897558, "uploadSequenceToken": "4962...", "arn": ("arn:aws:logs:eu-west-1:111:log-group:/aws/parallelcluster/" "test22-202106041223:log-stream:ip-10-0-0-102.i-0717e670ad2549e72.chef-client" ), "storedBytes": 0, }, ] logs.next_token = "123-456" list_log_streams_mock = mocker.patch( "pcluster.api.controllers.image_logs_controller.ImageBuilder.list_log_streams", return_value=logs) set_env("AWS_DEFAULT_REGION", "us-east-1") base_args = ["list-image-log-streams"] command = base_args + self._build_cli_args({**REQUIRED_ARGS, **args}) out = run(command) # cfn stack events are not displayed if next-token is passed expected_out = [ { "logStreamName": "ip-10-0-0-102.i-0717e670ad2549e72.cfn-init", "firstEventTimestamp": to_iso_timestr(to_utc_datetime(1622802790248)), "lastEventTimestamp": to_iso_timestr(to_utc_datetime(1622802893126)), }, { "logStreamName": "ip-10-0-0-102.i-0717e670ad2549e72.chef-client", "firstEventTimestamp": to_iso_timestr(to_utc_datetime(1622802837114)), "lastEventTimestamp": to_iso_timestr(to_utc_datetime(1622802861226)), }, ] assert_that(out["nextToken"]).is_equal_to(logs.next_token) for i in range(len(logs.log_streams)): select_keys = { "logStreamName", "firstEventTimestamp", "lastEventTimestamp" } out_select = { k: v for k, v in out["logStreams"][i].items() if k in select_keys } assert_that(out_select).is_equal_to(expected_out[i]) assert_that(list_log_streams_mock.call_args).is_length(2) # verify arguments kwargs = {"next_token": None} kwargs.update(args) list_log_streams_mock.assert_called_with(**kwargs)
def test_execute(self, mocker): mocker.patch("pcluster.cli.commands.configure.easyconfig.configure", return_value=True) run(["configure", "--config", "./test/config"])
def test_execute_with_wait(self, mocker, test_datadir): response_dict = { "cluster": { "clusterName": "cluster", "cloudformationStackStatus": "CREATE_IN_PROGRESS", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/cluster/aa", "region": "eu-west-1", "version": "3.0.0", "clusterStatus": "CREATE_IN_PROGRESS", } } status_response_dict = { "creationTime": "2021-01-01 00:00:00.000000+00:00", "headNode": { "launchTime": "2021-01-01T00:00:00+00:00", "instanceId": "i-099aaaaa7000ccccc", "publicIpAddress": "18.118.18.18", "instanceType": "t2.micro", "state": "running", "privateIpAddress": "10.0.0.32", }, "version": "3.0.0", "clusterConfiguration": { "url": ("https://parallelcluster-v1-do-not-delete.s3.amazonaws.com/parallelcluster/3.0.0/clusters/cluster/" "configs/cluster-config.yaml") }, "tags": [{ "value": "3.0.0", "key": "parallelcluster:version" }], "cloudFormationStackStatus": "CREATE_COMPLETE", "clusterName": "cluster", "computeFleetStatus": "RUNNING", "cloudformationStackArn": "arn:aws:cloudformation:us-east-2:000000000000:stack/name/0", "lastUpdatedTime": "2021-01-01 00:00:00.000000+00:00", "region": "us-west-2", "clusterStatus": "CREATE_COMPLETE", } create_response = CreateClusterResponseContent().from_dict( response_dict) create_cluster_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.create_cluster", return_value=create_response, autospec=True, ) response = DescribeClusterResponseContent().from_dict( status_response_dict) describe_cluster_mock = mocker.patch( "pcluster.api.controllers.cluster_operations_controller.describe_cluster", return_value=response) cf_waiter_mock = mocker.patch("botocore.waiter.Waiter.wait") mock_aws_api(mocker) path = str(test_datadir / "config.yaml") command = [ "create-cluster", "-n", "cluster", "-c", path, "-r", "eu-west-1", "--wait" ] out = run(command) expected = wire_translate(response) assert_that(out).is_equal_to(expected) assert_that(create_cluster_mock.call_args).is_length(2) expected_args = { "suppress_validators": None, "validation_failure_level": None, "dryrun": None, "rollback_on_failure": None, "region": "eu-west-1", "create_cluster_request_content": { "clusterName": "cluster", "clusterConfiguration": "" }, } create_cluster_mock.assert_called_with(**expected_args) assert_that(cf_waiter_mock.call_args[1]).is_equal_to( {"StackName": "cluster"}) describe_cluster_mock.assert_called_with(cluster_name="cluster")