From 804be777bfaa70ea8b015a947566caa998381ba8 Mon Sep 17 00:00:00 2001 From: rubengomex Date: Fri, 19 Jun 2026 09:28:54 +0100 Subject: [PATCH 1/5] feat: azure provider and vscode session image types to proc images --- cloudos_cli/procurement/cli.py | 15 +- cloudos_cli/procurement/images.py | 14 +- ...st_reset_procurement_organisation_image.py | 235 +++++++++++-- ...test_set_procurement_organisation_image.py | 315 ++++++++++++++++-- 4 files changed, 509 insertions(+), 70 deletions(-) diff --git a/cloudos_cli/procurement/cli.py b/cloudos_cli/procurement/cli.py index 8a82f19f..689542ad 100644 --- a/cloudos_cli/procurement/cli.py +++ b/cloudos_cli/procurement/cli.py @@ -1,10 +1,11 @@ """CLI commands for Lifebit Platform procurement management.""" import rich_click as click +from rich.console import Console + +from cloudos_cli.configure.configure import CLOUDOS_URL, with_profile_config from cloudos_cli.procurement.images import Images from cloudos_cli.utils.resources import ssl_selector -from cloudos_cli.configure.configure import with_profile_config, CLOUDOS_URL -from rich.console import Console @click.group() @@ -89,10 +90,11 @@ def list_images(ctx, 'SparkInteractiveSessions', 'RStudioInteractiveSessions', 'JupyterInteractiveSessions', + 'VSCodeInteractiveSessions', 'JobDefault', 'NextflowBatchComputeEnvironment'])) -@click.option('--provider', help='The cloud provider. Only aws is supported.', required=True, type=click.Choice(['aws']), default='aws') -@click.option('--region', help='The cloud region. Only aws regions are supported.', required=True) +@click.option('--provider', help='The cloud provider. Only aws and azure are supported.', required=True, type=click.Choice(['aws', 'azure']), default='aws') +@click.option('--region', help='The cloud region. Only aws and azure regions are supported.', required=True) @click.option('--image-id', help='The new image id value.', required=True) @click.option('--image-name', help='The new image name value.', required=False) @click.option('--image-version', help='The new image version value.', required=True) @@ -165,10 +167,11 @@ def set_organisation_image(ctx, 'SparkInteractiveSessions', 'RStudioInteractiveSessions', 'JupyterInteractiveSessions', + 'VSCodeInteractiveSessions', 'JobDefault', 'NextflowBatchComputeEnvironment'])) -@click.option('--provider', help='The cloud provider. Only aws is supported.', required=True, type=click.Choice(['aws']), default='aws') -@click.option('--region', help='The cloud region. Only aws regions are supported.', required=True) +@click.option('--provider', help='The cloud provider. Only aws and azure are supported.', required=True, type=click.Choice(['aws', 'azure']), default='aws') +@click.option('--region', help='The cloud region. Only aws and azure regions are supported.', required=True) @click.option('--disable-ssl-verification', help=('Disable SSL certificate verification. Please, remember that this option is ' + 'not generally recommended for security reasons.'), diff --git a/cloudos_cli/procurement/images.py b/cloudos_cli/procurement/images.py index 8c96e435..d6a07a93 100644 --- a/cloudos_cli/procurement/images.py +++ b/cloudos_cli/procurement/images.py @@ -4,11 +4,13 @@ import json from dataclasses import dataclass -from cloudos_cli.clos import Cloudos from typing import Union + +from cloudos_cli.clos import Cloudos from cloudos_cli.utils.errors import BadRequestException from cloudos_cli.utils.requests import retry_requests_get, retry_requests_put + @dataclass class Images(Cloudos): """Class for procurement images. @@ -94,12 +96,13 @@ def set_procurement_organisation_image(self, organisation_id, image_type, provid SparkInteractiveSessions RStudioInteractiveSessions JupyterInteractiveSessions + VSCodeInteractiveSessions JobDefault NextflowBatchComputeEnvironment provider - The cloud provider. Currently only supporting 'aws'. + The cloud provider. Currently only supporting 'aws' and 'azure'. region - The region. Currently only supporting aws regions. + The region. Currently only supporting aws and azure regions. imageId The new value for image Id. Required. imageName @@ -151,12 +154,13 @@ def reset_procurement_organisation_image(self, organisation_id, image_type, prov SparkInteractiveSessions RStudioInteractiveSessions JupyterInteractiveSessions + VSCodeInteractiveSessions JobDefault NextflowBatchComputeEnvironment provider - The cloud provider. Currently only supporting 'aws'. + The cloud provider. Currently only supporting 'aws' and 'azure'. region - The region. Currently only supporting aws regions. + The region. Currently only supporting aws and azure regions. """ headers = { diff --git a/tests/test_procurement/test_reset_procurement_organisation_image.py b/tests/test_procurement/test_reset_procurement_organisation_image.py index 36ead71a..6d0d2173 100644 --- a/tests/test_procurement/test_reset_procurement_organisation_image.py +++ b/tests/test_procurement/test_reset_procurement_organisation_image.py @@ -1,32 +1,32 @@ import json + import responses +from responses import matchers + from cloudos_cli.procurement import Images from tests.functions_for_pytest import load_json_file -from responses import matchers # Constants -APIKEY = 'vnoiweur89u2ongs' -CLOUDOS_URL = 'http://cloudos.lifebit.ai' -PROCUREMENT_ID = 'lv89ufc838sdig' -ORGANISATION_ID = 'org-12345678' +APIKEY = "vnoiweur89u2ongs" +CLOUDOS_URL = "http://cloudos.lifebit.ai" +PROCUREMENT_ID = "lv89ufc838sdig" +ORGANISATION_ID = "org-12345678" # Files RESET_IMAGE_RESPONSE = "tests/test_data/reset_procurement_image_response.json" + @responses.activate def test_reset_procurement_organisation_image(): mock_response = json.loads(load_json_file(RESET_IMAGE_RESPONSE)) - headers = { - "Content-type": "application/json", - "apikey": APIKEY - } + headers = {"Content-type": "application/json", "apikey": APIKEY} expected_payload = { "organisationId": ORGANISATION_ID, "imageType": "JobDefault", "provider": "aws", - "region": "eu-west-2" + "region": "eu-west-2", } # Mock endpoint @@ -35,7 +35,7 @@ def test_reset_procurement_organisation_image(): url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", body=json.dumps(mock_response), match=[matchers.json_params_matcher(expected_payload)], - status=200 + status=200, ) procurement_images = Images( @@ -43,23 +43,33 @@ def test_reset_procurement_organisation_image(): apikey=APIKEY, procurement_id=PROCUREMENT_ID, verify=True, - cromwell_token=None + cromwell_token=None, ) result = procurement_images.reset_procurement_organisation_image( organisation_id=ORGANISATION_ID, image_type="JobDefault", provider="aws", - region="eu-west-2" + region="eu-west-2", ) # Verify the image configuration details expected_config_keys = { - "id", "organisationId", "imageType", "provider", "region", - "imageId", "imageName", "isLifebitManaged", "lastUpdatedBy", - "organisationName", "updatedAt" + "id", + "organisationId", + "imageType", + "provider", + "region", + "imageId", + "imageName", + "isLifebitManaged", + "lastUpdatedBy", + "organisationName", + "updatedAt", } - assert expected_config_keys.issubset(result.keys()), f"Missing keys in image config: {result}" + assert expected_config_keys.issubset(result.keys()), ( + f"Missing keys in image config: {result}" + ) # Validate specific values assert result["organisationId"] == ORGANISATION_ID @@ -72,16 +82,18 @@ def test_reset_procurement_organisation_image(): assert result["isLifebitManaged"] is True # Should be True for reset to default assert "Lifebit" in result["lastUpdatedBy"] + @responses.activate def test_reset_procurement_organisation_image_different_types(): """Test resetting different image types""" image_types = [ "RegularInteractiveSessions", - "SparkInteractiveSessions", + "SparkInteractiveSessions", "RStudioInteractiveSessions", "JupyterInteractiveSessions", - "NextflowBatchComputeEnvironment" + "VSCodeInteractiveSessions", + "NextflowBatchComputeEnvironment", ] for image_type in image_types: @@ -96,14 +108,14 @@ def test_reset_procurement_organisation_image_different_types(): "isLifebitManaged": True, "lastUpdatedBy": "Lifebit System", "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00" + "updatedAt": "2025-07-28T12:00:00", } expected_payload = { "organisationId": ORGANISATION_ID, "imageType": image_type, "provider": "aws", - "region": "eu-west-2" + "region": "eu-west-2", } responses.add( @@ -111,7 +123,7 @@ def test_reset_procurement_organisation_image_different_types(): url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", body=json.dumps(mock_response), match=[matchers.json_params_matcher(expected_payload)], - status=200 + status=200, ) procurement_images = Images( @@ -119,7 +131,7 @@ def test_reset_procurement_organisation_image_different_types(): apikey=APIKEY, procurement_id=PROCUREMENT_ID, verify=True, - cromwell_token=None + cromwell_token=None, ) # Test each image type @@ -128,7 +140,7 @@ def test_reset_procurement_organisation_image_different_types(): organisation_id=ORGANISATION_ID, image_type=image_type, provider="aws", - region="eu-west-2" + region="eu-west-2", ) assert result["imageType"] == image_type @@ -136,6 +148,7 @@ def test_reset_procurement_organisation_image_different_types(): assert result["imageId"] == f"ami-lifebit-{image_type.lower()[:8]}-default" assert "Lifebit" in result["lastUpdatedBy"] + @responses.activate def test_reset_procurement_organisation_image_different_regions(): """Test resetting image configuration for different AWS regions""" @@ -154,14 +167,14 @@ def test_reset_procurement_organisation_image_different_regions(): "isLifebitManaged": True, "lastUpdatedBy": "Lifebit System", "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00" + "updatedAt": "2025-07-28T12:00:00", } expected_payload = { "organisationId": ORGANISATION_ID, "imageType": "JobDefault", "provider": "aws", - "region": region + "region": region, } responses.add( @@ -169,7 +182,7 @@ def test_reset_procurement_organisation_image_different_regions(): url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", body=json.dumps(mock_response), match=[matchers.json_params_matcher(expected_payload)], - status=200 + status=200, ) procurement_images = Images( @@ -177,7 +190,7 @@ def test_reset_procurement_organisation_image_different_regions(): apikey=APIKEY, procurement_id=PROCUREMENT_ID, verify=True, - cromwell_token=None + cromwell_token=None, ) # Test each region @@ -186,9 +199,173 @@ def test_reset_procurement_organisation_image_different_regions(): organisation_id=ORGANISATION_ID, image_type="JobDefault", provider="aws", - region=region + region=region, ) assert result["region"] == region assert result["imageId"] == f"ami-lifebit-{region}-default" assert result["isLifebitManaged"] is True + + +@responses.activate +def test_reset_procurement_organisation_image_azure_provider(): + """Test resetting image configuration for Azure provider""" + + mock_response = { + "id": "config-azure-123", + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": "eastus", + "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/lifebit-default", + "imageName": "Lifebit Default Job Image (Azure)", + "isLifebitManaged": True, + "lastUpdatedBy": "Lifebit System", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": "eastus", + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + result = procurement_images.reset_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="JobDefault", + provider="azure", + region="eastus", + ) + + assert result["provider"] == "azure" + assert result["region"] == "eastus" + assert result["isLifebitManaged"] is True + + +@responses.activate +def test_reset_procurement_organisation_image_azure_different_regions(): + """Test resetting image configuration for different Azure regions""" + + azure_regions = ["eastus", "westus2", "northeurope", "westeurope", "uksouth"] + + for region in azure_regions: + mock_response = { + "id": f"config-azure-{region}", + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": region, + "imageId": f"/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/lifebit-{region}-default", + "imageName": f"Lifebit Default Job Image ({region})", + "isLifebitManaged": True, + "lastUpdatedBy": "Lifebit System", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": region, + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + # Test each Azure region + for region in azure_regions: + result = procurement_images.reset_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="JobDefault", + provider="azure", + region=region, + ) + + assert result["provider"] == "azure" + assert result["region"] == region + assert result["isLifebitManaged"] is True + + +@responses.activate +def test_reset_procurement_organisation_image_vscode_interactive_sessions(): + """Test resetting VSCodeInteractiveSessions image type specifically""" + + mock_response = { + "id": "config-vscode-123", + "organisationId": ORGANISATION_ID, + "imageType": "VSCodeInteractiveSessions", + "provider": "aws", + "region": "eu-west-2", + "imageId": "ami-lifebit-vscode-default", + "imageName": "Lifebit Default VSCode Interactive Sessions Image", + "isLifebitManaged": True, + "lastUpdatedBy": "Lifebit System", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "VSCodeInteractiveSessions", + "provider": "aws", + "region": "eu-west-2", + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + result = procurement_images.reset_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="VSCodeInteractiveSessions", + provider="aws", + region="eu-west-2", + ) + + assert result["imageType"] == "VSCodeInteractiveSessions" + assert result["isLifebitManaged"] is True + assert "vscode" in result["imageId"].lower() diff --git a/tests/test_procurement/test_set_procurement_organisation_image.py b/tests/test_procurement/test_set_procurement_organisation_image.py index 4d3af092..5376eadc 100644 --- a/tests/test_procurement/test_set_procurement_organisation_image.py +++ b/tests/test_procurement/test_set_procurement_organisation_image.py @@ -1,26 +1,26 @@ import json + import responses +from responses import matchers + from cloudos_cli.procurement import Images from tests.functions_for_pytest import load_json_file -from responses import matchers # Constants -APIKEY = 'vnoiweur89u2ongs' -CLOUDOS_URL = 'http://cloudos.lifebit.ai' -PROCUREMENT_ID = 'lv89ufc838sdig' -ORGANISATION_ID = 'org-12345678' +APIKEY = "vnoiweur89u2ongs" +CLOUDOS_URL = "http://cloudos.lifebit.ai" +PROCUREMENT_ID = "lv89ufc838sdig" +ORGANISATION_ID = "org-12345678" # Files SET_IMAGE_RESPONSE = "tests/test_data/set_procurement_image_response.json" + @responses.activate def test_set_procurement_organisation_image(): mock_response = json.loads(load_json_file(SET_IMAGE_RESPONSE)) - headers = { - "Content-type": "application/json", - "apikey": APIKEY - } + headers = {"Content-type": "application/json", "apikey": APIKEY} expected_payload = { "organisationId": ORGANISATION_ID, @@ -29,7 +29,7 @@ def test_set_procurement_organisation_image(): "region": "eu-west-2", "imageId": "ami-0123456789abcdef0", "imageName": "Custom-Job-Image", - "imageVersion": "1.0.0" + "imageVersion": "1.0.0", } # Mock endpoint @@ -38,7 +38,7 @@ def test_set_procurement_organisation_image(): url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", body=json.dumps(mock_response), match=[matchers.json_params_matcher(expected_payload)], - status=200 + status=200, ) procurement_images = Images( @@ -46,7 +46,7 @@ def test_set_procurement_organisation_image(): apikey=APIKEY, procurement_id=PROCUREMENT_ID, verify=True, - cromwell_token=None + cromwell_token=None, ) result = procurement_images.set_procurement_organisation_image( @@ -56,16 +56,26 @@ def test_set_procurement_organisation_image(): region="eu-west-2", image_id="ami-0123456789abcdef0", image_name="Custom-Job-Image", - image_version="1.0.0" + image_version="1.0.0", ) # Verify the image configuration details expected_config_keys = { - "id", "organisationId", "imageType", "provider", "region", - "imageId", "imageName", "isLifebitManaged", "lastUpdatedBy", - "organisationName", "updatedAt" + "id", + "organisationId", + "imageType", + "provider", + "region", + "imageId", + "imageName", + "isLifebitManaged", + "lastUpdatedBy", + "organisationName", + "updatedAt", } - assert expected_config_keys.issubset(result.keys()), f"Missing keys in image config: {result}" + assert expected_config_keys.issubset(result.keys()), ( + f"Missing keys in image config: {result}" + ) # Validate specific values assert result["organisationId"] == ORGANISATION_ID @@ -77,16 +87,18 @@ def test_set_procurement_organisation_image(): assert isinstance(result["isLifebitManaged"], bool) assert result["isLifebitManaged"] is False + @responses.activate def test_set_procurement_organisation_image_different_types(): """Test setting different image types""" image_types = [ "RegularInteractiveSessions", - "SparkInteractiveSessions", + "SparkInteractiveSessions", "RStudioInteractiveSessions", "JupyterInteractiveSessions", - "NextflowBatchComputeEnvironment" + "VSCodeInteractiveSessions", + "NextflowBatchComputeEnvironment", ] for image_type in image_types: @@ -101,7 +113,7 @@ def test_set_procurement_organisation_image_different_types(): "isLifebitManaged": False, "lastUpdatedBy": "test-user", "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00" + "updatedAt": "2025-07-28T12:00:00", } expected_payload = { @@ -111,7 +123,7 @@ def test_set_procurement_organisation_image_different_types(): "region": "eu-west-2", "imageId": f"ami-{image_type.lower()[:8]}123", "imageName": f"Custom-{image_type}-Image", - "imageVersion": "1.0.0" + "imageVersion": "1.0.0", } responses.add( @@ -119,7 +131,7 @@ def test_set_procurement_organisation_image_different_types(): url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", body=json.dumps(mock_response), match=[matchers.json_params_matcher(expected_payload)], - status=200 + status=200, ) procurement_images = Images( @@ -127,7 +139,7 @@ def test_set_procurement_organisation_image_different_types(): apikey=APIKEY, procurement_id=PROCUREMENT_ID, verify=True, - cromwell_token=None + cromwell_token=None, ) # Test each image type @@ -139,13 +151,14 @@ def test_set_procurement_organisation_image_different_types(): region="eu-west-2", image_id=f"ami-{image_type.lower()[:8]}123", image_name=f"Custom-{image_type}-Image", - image_version="1.0.0" + image_version="1.0.0", ) assert result["imageType"] == image_type assert result["imageId"] == f"ami-{image_type.lower()[:8]}123" -@responses.activate + +@responses.activate def test_set_procurement_organisation_image_without_image_name(): """Test setting image configuration without providing image_name parameter""" @@ -160,7 +173,7 @@ def test_set_procurement_organisation_image_without_image_name(): "isLifebitManaged": False, "lastUpdatedBy": "test-user", "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00" + "updatedAt": "2025-07-28T12:00:00", } expected_payload = { @@ -170,7 +183,7 @@ def test_set_procurement_organisation_image_without_image_name(): "region": "eu-west-2", "imageId": "ami-0123456789abcdef0", "imageName": None, - "imageVersion": "1.0.0" + "imageVersion": "1.0.0", } responses.add( @@ -178,7 +191,7 @@ def test_set_procurement_organisation_image_without_image_name(): url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", body=json.dumps(mock_response), match=[matchers.json_params_matcher(expected_payload)], - status=200 + status=200, ) procurement_images = Images( @@ -186,7 +199,7 @@ def test_set_procurement_organisation_image_without_image_name(): apikey=APIKEY, procurement_id=PROCUREMENT_ID, verify=True, - cromwell_token=None + cromwell_token=None, ) result = procurement_images.set_procurement_organisation_image( @@ -196,7 +209,249 @@ def test_set_procurement_organisation_image_without_image_name(): region="eu-west-2", image_id="ami-0123456789abcdef0", image_name=None, - image_version="1.0.0" + image_version="1.0.0", ) assert result["imageName"] is None + + +@responses.activate +def test_set_procurement_organisation_image_azure_provider(): + """Test setting image configuration for Azure provider""" + + mock_response = { + "id": "config-azure-123", + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": "eastus", + "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/custom-image", + "imageName": "Custom-Azure-Job-Image", + "isLifebitManaged": False, + "lastUpdatedBy": "test-user", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": "eastus", + "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/custom-image", + "imageName": "Custom-Azure-Job-Image", + "imageVersion": "1.0.0", + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + result = procurement_images.set_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="JobDefault", + provider="azure", + region="eastus", + image_id="/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/custom-image", + image_name="Custom-Azure-Job-Image", + image_version="1.0.0", + ) + + assert result["provider"] == "azure" + assert result["region"] == "eastus" + assert result["isLifebitManaged"] is False + + +@responses.activate +def test_set_procurement_organisation_image_azure_different_regions(): + """Test setting image configuration for different Azure regions""" + + azure_regions = ["eastus", "westus2", "northeurope", "westeurope", "uksouth"] + + for region in azure_regions: + mock_response = { + "id": f"config-azure-{region}", + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": region, + "imageId": f"/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/custom-{region}", + "imageName": f"Custom-Azure-Job-Image-{region}", + "isLifebitManaged": False, + "lastUpdatedBy": "test-user", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "JobDefault", + "provider": "azure", + "region": region, + "imageId": f"/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/custom-{region}", + "imageName": f"Custom-Azure-Job-Image-{region}", + "imageVersion": "1.0.0", + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + # Test each Azure region + for region in azure_regions: + result = procurement_images.set_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="JobDefault", + provider="azure", + region=region, + image_id=f"/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/custom-{region}", + image_name=f"Custom-Azure-Job-Image-{region}", + image_version="1.0.0", + ) + + assert result["provider"] == "azure" + assert result["region"] == region + assert result["isLifebitManaged"] is False + + +@responses.activate +def test_set_procurement_organisation_image_vscode_interactive_sessions(): + """Test setting VSCodeInteractiveSessions image type specifically""" + + mock_response = { + "id": "config-vscode-123", + "organisationId": ORGANISATION_ID, + "imageType": "VSCodeInteractiveSessions", + "provider": "aws", + "region": "eu-west-2", + "imageId": "ami-vscode-custom-123", + "imageName": "Custom VSCode Interactive Sessions Image", + "isLifebitManaged": False, + "lastUpdatedBy": "test-user", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "VSCodeInteractiveSessions", + "provider": "aws", + "region": "eu-west-2", + "imageId": "ami-vscode-custom-123", + "imageName": "Custom VSCode Interactive Sessions Image", + "imageVersion": "2.0.0", + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + result = procurement_images.set_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="VSCodeInteractiveSessions", + provider="aws", + region="eu-west-2", + image_id="ami-vscode-custom-123", + image_name="Custom VSCode Interactive Sessions Image", + image_version="2.0.0", + ) + + assert result["imageType"] == "VSCodeInteractiveSessions" + assert result["isLifebitManaged"] is False + assert "vscode" in result["imageId"].lower() + + +@responses.activate +def test_set_procurement_organisation_image_vscode_azure(): + """Test setting VSCodeInteractiveSessions image type with Azure provider""" + + mock_response = { + "id": "config-vscode-azure-123", + "organisationId": ORGANISATION_ID, + "imageType": "VSCodeInteractiveSessions", + "provider": "azure", + "region": "westeurope", + "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/vscode-custom", + "imageName": "Custom VSCode Interactive Sessions Image (Azure)", + "isLifebitManaged": False, + "lastUpdatedBy": "test-user", + "organisationName": "Test-Organisation", + "updatedAt": "2025-07-28T12:00:00", + } + + expected_payload = { + "organisationId": ORGANISATION_ID, + "imageType": "VSCodeInteractiveSessions", + "provider": "azure", + "region": "westeurope", + "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/vscode-custom", + "imageName": "Custom VSCode Interactive Sessions Image (Azure)", + "imageVersion": "1.0.0", + } + + responses.add( + responses.PUT, + url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", + body=json.dumps(mock_response), + match=[matchers.json_params_matcher(expected_payload)], + status=200, + ) + + procurement_images = Images( + cloudos_url=CLOUDOS_URL, + apikey=APIKEY, + procurement_id=PROCUREMENT_ID, + verify=True, + cromwell_token=None, + ) + + result = procurement_images.set_procurement_organisation_image( + organisation_id=ORGANISATION_ID, + image_type="VSCodeInteractiveSessions", + provider="azure", + region="westeurope", + image_id="/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/vscode-custom", + image_name="Custom VSCode Interactive Sessions Image (Azure)", + image_version="1.0.0", + ) + + assert result["imageType"] == "VSCodeInteractiveSessions" + assert result["provider"] == "azure" + assert result["region"] == "westeurope" + assert result["isLifebitManaged"] is False From c4b5f6cc383aee13850331ceaefba126ad197ade Mon Sep 17 00:00:00 2001 From: rubengomex Date: Fri, 19 Jun 2026 16:05:52 +0100 Subject: [PATCH 2/5] chore: remove deprecated instance type values --- cloudos_cli/procurement/cli.py | 6 - cloudos_cli/procurement/images.py | 6 - ...st_reset_procurement_organisation_image.py | 55 -------- ...test_set_procurement_organisation_image.py | 121 ------------------ 4 files changed, 188 deletions(-) diff --git a/cloudos_cli/procurement/cli.py b/cloudos_cli/procurement/cli.py index 689542ad..0f3ed328 100644 --- a/cloudos_cli/procurement/cli.py +++ b/cloudos_cli/procurement/cli.py @@ -88,9 +88,6 @@ def list_images(ctx, type=click.Choice([ 'RegularInteractiveSessions', 'SparkInteractiveSessions', - 'RStudioInteractiveSessions', - 'JupyterInteractiveSessions', - 'VSCodeInteractiveSessions', 'JobDefault', 'NextflowBatchComputeEnvironment'])) @click.option('--provider', help='The cloud provider. Only aws and azure are supported.', required=True, type=click.Choice(['aws', 'azure']), default='aws') @@ -165,9 +162,6 @@ def set_organisation_image(ctx, type=click.Choice([ 'RegularInteractiveSessions', 'SparkInteractiveSessions', - 'RStudioInteractiveSessions', - 'JupyterInteractiveSessions', - 'VSCodeInteractiveSessions', 'JobDefault', 'NextflowBatchComputeEnvironment'])) @click.option('--provider', help='The cloud provider. Only aws and azure are supported.', required=True, type=click.Choice(['aws', 'azure']), default='aws') diff --git a/cloudos_cli/procurement/images.py b/cloudos_cli/procurement/images.py index d6a07a93..a428ca4e 100644 --- a/cloudos_cli/procurement/images.py +++ b/cloudos_cli/procurement/images.py @@ -94,9 +94,6 @@ def set_procurement_organisation_image(self, organisation_id, image_type, provid The image type. Possible values are: RegularInteractiveSessions SparkInteractiveSessions - RStudioInteractiveSessions - JupyterInteractiveSessions - VSCodeInteractiveSessions JobDefault NextflowBatchComputeEnvironment provider @@ -152,9 +149,6 @@ def reset_procurement_organisation_image(self, organisation_id, image_type, prov The image type. Possible values are: RegularInteractiveSessions SparkInteractiveSessions - RStudioInteractiveSessions - JupyterInteractiveSessions - VSCodeInteractiveSessions JobDefault NextflowBatchComputeEnvironment provider diff --git a/tests/test_procurement/test_reset_procurement_organisation_image.py b/tests/test_procurement/test_reset_procurement_organisation_image.py index 6d0d2173..bf9c94e7 100644 --- a/tests/test_procurement/test_reset_procurement_organisation_image.py +++ b/tests/test_procurement/test_reset_procurement_organisation_image.py @@ -90,9 +90,6 @@ def test_reset_procurement_organisation_image_different_types(): image_types = [ "RegularInteractiveSessions", "SparkInteractiveSessions", - "RStudioInteractiveSessions", - "JupyterInteractiveSessions", - "VSCodeInteractiveSessions", "NextflowBatchComputeEnvironment", ] @@ -317,55 +314,3 @@ def test_reset_procurement_organisation_image_azure_different_regions(): assert result["region"] == region assert result["isLifebitManaged"] is True - -@responses.activate -def test_reset_procurement_organisation_image_vscode_interactive_sessions(): - """Test resetting VSCodeInteractiveSessions image type specifically""" - - mock_response = { - "id": "config-vscode-123", - "organisationId": ORGANISATION_ID, - "imageType": "VSCodeInteractiveSessions", - "provider": "aws", - "region": "eu-west-2", - "imageId": "ami-lifebit-vscode-default", - "imageName": "Lifebit Default VSCode Interactive Sessions Image", - "isLifebitManaged": True, - "lastUpdatedBy": "Lifebit System", - "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00", - } - - expected_payload = { - "organisationId": ORGANISATION_ID, - "imageType": "VSCodeInteractiveSessions", - "provider": "aws", - "region": "eu-west-2", - } - - responses.add( - responses.PUT, - url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images/reset", - body=json.dumps(mock_response), - match=[matchers.json_params_matcher(expected_payload)], - status=200, - ) - - procurement_images = Images( - cloudos_url=CLOUDOS_URL, - apikey=APIKEY, - procurement_id=PROCUREMENT_ID, - verify=True, - cromwell_token=None, - ) - - result = procurement_images.reset_procurement_organisation_image( - organisation_id=ORGANISATION_ID, - image_type="VSCodeInteractiveSessions", - provider="aws", - region="eu-west-2", - ) - - assert result["imageType"] == "VSCodeInteractiveSessions" - assert result["isLifebitManaged"] is True - assert "vscode" in result["imageId"].lower() diff --git a/tests/test_procurement/test_set_procurement_organisation_image.py b/tests/test_procurement/test_set_procurement_organisation_image.py index 5376eadc..40a0734d 100644 --- a/tests/test_procurement/test_set_procurement_organisation_image.py +++ b/tests/test_procurement/test_set_procurement_organisation_image.py @@ -95,9 +95,6 @@ def test_set_procurement_organisation_image_different_types(): image_types = [ "RegularInteractiveSessions", "SparkInteractiveSessions", - "RStudioInteractiveSessions", - "JupyterInteractiveSessions", - "VSCodeInteractiveSessions", "NextflowBatchComputeEnvironment", ] @@ -337,121 +334,3 @@ def test_set_procurement_organisation_image_azure_different_regions(): assert result["region"] == region assert result["isLifebitManaged"] is False - -@responses.activate -def test_set_procurement_organisation_image_vscode_interactive_sessions(): - """Test setting VSCodeInteractiveSessions image type specifically""" - - mock_response = { - "id": "config-vscode-123", - "organisationId": ORGANISATION_ID, - "imageType": "VSCodeInteractiveSessions", - "provider": "aws", - "region": "eu-west-2", - "imageId": "ami-vscode-custom-123", - "imageName": "Custom VSCode Interactive Sessions Image", - "isLifebitManaged": False, - "lastUpdatedBy": "test-user", - "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00", - } - - expected_payload = { - "organisationId": ORGANISATION_ID, - "imageType": "VSCodeInteractiveSessions", - "provider": "aws", - "region": "eu-west-2", - "imageId": "ami-vscode-custom-123", - "imageName": "Custom VSCode Interactive Sessions Image", - "imageVersion": "2.0.0", - } - - responses.add( - responses.PUT, - url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", - body=json.dumps(mock_response), - match=[matchers.json_params_matcher(expected_payload)], - status=200, - ) - - procurement_images = Images( - cloudos_url=CLOUDOS_URL, - apikey=APIKEY, - procurement_id=PROCUREMENT_ID, - verify=True, - cromwell_token=None, - ) - - result = procurement_images.set_procurement_organisation_image( - organisation_id=ORGANISATION_ID, - image_type="VSCodeInteractiveSessions", - provider="aws", - region="eu-west-2", - image_id="ami-vscode-custom-123", - image_name="Custom VSCode Interactive Sessions Image", - image_version="2.0.0", - ) - - assert result["imageType"] == "VSCodeInteractiveSessions" - assert result["isLifebitManaged"] is False - assert "vscode" in result["imageId"].lower() - - -@responses.activate -def test_set_procurement_organisation_image_vscode_azure(): - """Test setting VSCodeInteractiveSessions image type with Azure provider""" - - mock_response = { - "id": "config-vscode-azure-123", - "organisationId": ORGANISATION_ID, - "imageType": "VSCodeInteractiveSessions", - "provider": "azure", - "region": "westeurope", - "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/vscode-custom", - "imageName": "Custom VSCode Interactive Sessions Image (Azure)", - "isLifebitManaged": False, - "lastUpdatedBy": "test-user", - "organisationName": "Test-Organisation", - "updatedAt": "2025-07-28T12:00:00", - } - - expected_payload = { - "organisationId": ORGANISATION_ID, - "imageType": "VSCodeInteractiveSessions", - "provider": "azure", - "region": "westeurope", - "imageId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/vscode-custom", - "imageName": "Custom VSCode Interactive Sessions Image (Azure)", - "imageVersion": "1.0.0", - } - - responses.add( - responses.PUT, - url=f"{CLOUDOS_URL}/api/v1/procurements/{PROCUREMENT_ID}/images", - body=json.dumps(mock_response), - match=[matchers.json_params_matcher(expected_payload)], - status=200, - ) - - procurement_images = Images( - cloudos_url=CLOUDOS_URL, - apikey=APIKEY, - procurement_id=PROCUREMENT_ID, - verify=True, - cromwell_token=None, - ) - - result = procurement_images.set_procurement_organisation_image( - organisation_id=ORGANISATION_ID, - image_type="VSCodeInteractiveSessions", - provider="azure", - region="westeurope", - image_id="/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Compute/images/vscode-custom", - image_name="Custom VSCode Interactive Sessions Image (Azure)", - image_version="1.0.0", - ) - - assert result["imageType"] == "VSCodeInteractiveSessions" - assert result["provider"] == "azure" - assert result["region"] == "westeurope" - assert result["isLifebitManaged"] is False From 9f41a9d0cce825eff4bad5f1dfe00a95a9e3386a Mon Sep 17 00:00:00 2001 From: rubengomex Date: Mon, 22 Jun 2026 12:04:26 +0100 Subject: [PATCH 3/5] chore: update README.md file --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 36471980..ec357f62 100644 --- a/README.md +++ b/README.md @@ -196,10 +196,10 @@ This will tell you the implemented commands. Each implemented command has its ow ```bash cloudos job list --help ``` -```console Usage: cloudos job list [OPTIONS] - - Collect workspace jobs from a Lifebit Platform workspace in CSV or JSON format. - +```console Usage: cloudos job list [OPTIONS] + + Collect workspace jobs from a Lifebit Platform workspace in CSV or JSON format. + ╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────╮ │ * --apikey -k TEXT Your Lifebit Platform API key [required] │ │ * --cloudos-url -c TEXT The Lifebit Platform url you are trying to access to. │ @@ -264,7 +264,7 @@ $HOME ### Configure Default Profile -To facilitate the reuse of required parameters, you can create profiles. +To facilitate the reuse of required parameters, you can create profiles. To generate a profile called `default`, use the following command: @@ -286,7 +286,7 @@ The same prompts will appear, including the execution platform (aws or azure). I When configuring a profile, you can specify: - **API Key**: Your Lifebit Platform API credentials -- **Platform URL**: The Lifebit Platform instance URL +- **Platform URL**: The Lifebit Platform instance URL - **Project Name**: Default project for commands - **Execution Platform**: `aws` (default) or `azure` - determines default instance types and available features - **Repository Platform**: Version control system (github, gitlab, etc.) @@ -1279,7 +1279,7 @@ cloudos job cost --profile my_profile --job-id 62c83a1191fe06013b7ef355 The expected output is a formatted table showing: ```console - Job Cost Details - Job ID: 62c83a1191fe06013b7ef355 + Job Cost Details - Job ID: 62c83a1191fe06013b7ef355 ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━┓ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Compute ┃ ┃ ┃ ┃ Instance ┃ ┃ Life-cycle ┃ ┃ Compute ┃ Instance ┃ storage ┃ ┃ @@ -1308,7 +1308,7 @@ On page 1/2: n = next, p = prev, q = quit By pressing 'n', it will show the next page or the last if it is the case. - Job Cost Details - Job ID: 62c83a1191fe06013b7ef355 + Job Cost Details - Job ID: 62c83a1191fe06013b7ef355 ┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━┓ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ Compute ┃ ┃ ┃ ┃ Instance ┃ ┃ Life-cycle ┃ ┃ Compute ┃ Instance ┃ storage ┃ ┃ @@ -1427,7 +1427,7 @@ The expected output is a formatted table showing: ```console Total related analyses found: 15 - Related Analyses + Related Analyses ┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ Status ┃ Name ┃ Owner ┃ ID ┃ Submit time ┃ Run time ┃ Total Cost ┃ ┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━┩ @@ -1909,7 +1909,7 @@ Example Python Script: #!/usr/bin/python3 print("Hello world") ``` - + 2. Or use an interpreter command in the executable field If your script doesn’t have a shebang line, you can execute it by explicitly specifying the interpreter in the executable command: @@ -1986,7 +1986,7 @@ Interactive sessions allow you to work within the platform using different virtu You can get a list of all interactive sessions in your workspace by running `cloudos interactive-session list`. The command can produce three different output formats that can be selected using the `--output-format` option: -- **stdout** (default): Displays a table directly in the terminal with interactive pagination +- **stdout** (default): Displays a table directly in the terminal with interactive pagination - **csv**: Saves session data to a CSV file with a minimum predefined set of columns by default, or all available columns using the `--all-fields` parameter - **json**: Saves complete session information to a JSON file with all available fields @@ -2001,7 +2001,7 @@ cloudos interactive-session list --profile my_profile --output-format stdout The table displays sessions with pagination controls (press `n` for next page, `p` for previous page, or `q` to quit): ```console - Interactive Sessions + Interactive Sessions ┏━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━┓ ┃ Status ┃ Name ┃ Type ┃ ID ┃ Owner ┃ ┡━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━┩ @@ -2611,7 +2611,7 @@ The `datasets ls` command supports different output formats using the `--output- - **`stdout` (default)**: Displays results in the console with Rich formatting - Without `--details`: Simple list of file/folder names with color coding (blue underlined for folders) - With `--details`: Rich formatted table with all file information - + - **`csv`**: Saves results to a CSV file - Without `--details`: CSV with two columns: "Name,Storage Path" - With `--details`: CSV with columns "Type, Owner, Size, Size (bytes), Last Updated, Virtual Name, Storage Path" @@ -2661,7 +2661,7 @@ Any of the `source_path` must be a full path, starting from the `Data` datasets An example of such command is: ``` -cloudos datasets mv Data/results/my_plot.png Data/plots +cloudos datasets mv Data/results/my_plot.png Data/plots ``` #### Rename Files @@ -2672,7 +2672,7 @@ Change file and folder names while keeping them in the same location. This helps > Files and folders within the `Data` dataset can be renamed using the following command ```bash -cloudos datasets rename --profile my_profile +cloudos datasets rename --profile my_profile ``` where `path` is the full path to the file/folder to be renamed and `new_name` is just the name, no path required, as the file will not be moved. @@ -2698,12 +2698,12 @@ or it can happen **across different projects** within the same workspace cloudos datasets cp --profile --destination-project-name ``` -Any of the `source_path` must be a full path; any `destination_path` must be a path starting with `Data` and finishing with the folder where to move the file/folder. +Any of the `source_path` must be a full path; any `destination_path` must be a path starting with `Data` and finishing with the folder where to move the file/folder. An example of such command is: ``` -cloudos datasets cp AnalysesResults/my_analysis/results/my_plot.png Data/plots +cloudos datasets cp AnalysesResults/my_analysis/results/my_plot.png Data/plots ``` @@ -2754,7 +2754,7 @@ Create new organizational folders within your projects to maintain structured da > New folders can be created within the `Data` dataset and its subfolders. ```bash -cloudos datasets mkdir --profile my_profile +cloudos datasets mkdir --profile my_profile ``` #### Remove Files or Folders @@ -2762,14 +2762,14 @@ cloudos datasets mkdir --profile my_profile Remove unnecessary files or empty folders from your File Explorer. Note that this removes files from Lifebit Platform but not from underlying cloud storage. > [!NOTE] -> Files and folders can be removed in the `Data` datasets and its subfolders. +> Files and folders can be removed in the `Data` datasets and its subfolders. ```bash cloudos datasets rm --profile my_profile ``` > [!NOTE] > If a file was uploaded by the user, in order to be removed you must use `--force` and that will permanently remove the file. If the file is "linked" (e.g a s3 folder or file), removing it using `cloudos datasets rm` will not remove it from the the s3 bucket. - + --- ### Link @@ -2891,7 +2891,7 @@ To list images for a specific procurement, use the following command: ```bash cloudos procurement images ls \ - -- profile procurement_profile + -- profile procurement_profile --procurement-id "your_procurement_id_here" ``` @@ -2950,12 +2950,10 @@ cloudos procurement images set --profile procurement_profile --image-type "JobDe - `--image-type`: The Lifebit Platform resource image type (required). Possible values: - `RegularInteractiveSessions` - `SparkInteractiveSessions` - - `RStudioInteractiveSessions` - - `JupyterInteractiveSessions` - `JobDefault` - `NextflowBatchComputeEnvironment` -- `--provider`: The cloud provider (required). Currently only `aws` is supported -- `--region`: The cloud region (required). Currently only AWS regions are supported +- `--provider`: The cloud provider (required). Currently only supporting `aws` and `azure`. +- `--region`: The cloud region (required). Currently only supporting aws and azure regions. - `--image-id`: The new image ID value (required) - `--image-name`: The new image name value (optional) - `--image-version`: The new image version (required) @@ -2988,7 +2986,7 @@ cloudos procurement images reset --profile procurement_profile --image-type "Job - `--organisation-id`: The organization ID where the change will be applied (required) - `--image-type`: The Lifebit Platform resource image type (required). Same values as for `set` command - `--provider`: The cloud provider (required). Currently only `aws` is supported -- `--region`: The cloud region (required). Currently only AWS regions are supported +- `--region`: The cloud region (required). Currently only supporting aws and azure regions. - `--disable-ssl-verification`: Disable SSL certificate verification - `--ssl-cert`: Path to your SSL certificate file - `--profile`: Profile to use from the config file @@ -3017,7 +3015,7 @@ Executing status... Current Cromwell server status is: Stopped ``` -```bash +```bash # Cromwell start cloudos cromwell start --profile my_profile ``` @@ -3243,8 +3241,8 @@ responses>=0.21.0 mock>=3.0.5 ``` -Command to run tests from the `cloudos-cli` main folder: +Command to run tests from the `cloudos-cli` main folder: ``` python -m pytest -s -v -``` +``` From 00dc395c98843b365f2ff958a9fde890c6afbf55 Mon Sep 17 00:00:00 2001 From: rubengomex Date: Tue, 23 Jun 2026 01:04:19 +0100 Subject: [PATCH 4/5] chore: update version --- cloudos_cli/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudos_cli/_version.py b/cloudos_cli/_version.py index 1271f796..363dce345 100644 --- a/cloudos_cli/_version.py +++ b/cloudos_cli/_version.py @@ -1 +1 @@ -__version__ = '2.91.0' +__version__ = '2.92.0' From b0d976e74148420da61cee02bd5e732ece193ec3 Mon Sep 17 00:00:00 2001 From: rubengomex Date: Tue, 23 Jun 2026 11:05:51 +0100 Subject: [PATCH 5/5] chore: update changelog --- CHANGELOG.md | 168 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07f2df07..ffacae10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ ## lifebit-ai/cloudos-cli: changelog +## v2.92.0 (2026-06-23) + +### Feat + +- Add support for procurement images set/reset for azure provider +- Consolidate the image type to regular sessions + ## v2.91.0 (2026-05-28) -### Feat: +### Feat - Implements linking of files in interactive session creation - Implements linking of files in `cloudos link` @@ -10,11 +17,10 @@ - Enforces a maximum of 100 linked items per interactive session - Adds clearer, actionable error messages when mounts fail (e.g. translates "prefix does not exist" / "access denied" into workspace-permission guidance) -### Breaking: +### Breaking - `cloudos link` and `cloudos datasets link`: File Explorer paths must now be RELATIVE to `--project-name` (do NOT prepend the project name). Previously the leading `/` segment was advertised but produced confusing errors; it is now rejected up front with a clear message pointing to the correct form. `cloudos interactive-session create --link` still uses `/` format — see each command's `--help` for the explicit cross-reference. - ## v2.90.2 (2026-05-07) ### Patch @@ -210,14 +216,12 @@ - adds tablular standard output for job list - ## v2.74.0 (2025-12-05) ### Feat - Adds bulk deletion script with documentation - ## v2.73.0 (2025-12-02) ### Feat @@ -263,7 +267,6 @@ - Adds (deletion) status check for job workdir - Adds error message for when trying to get workdirs or results that have been deleted - ## v2.68.0 (2025-11-07) ### Feat @@ -271,14 +274,12 @@ - Adds checks for linking job completion - Fix workdir command to return correct path for resumed jobs. - ## v2.67.0 (2025-11-06) ### Feat - Implement viewing related job analyses - ## v2.66.2 (2025-11-5) ### Patch @@ -286,7 +287,6 @@ - Fix job help message for resume and clone - Implements datasets ls for single files - ## v2.66.1 (2025-10-29) ### Patch @@ -438,10 +438,10 @@ - Adds support for querying the working directory of a job. - ## v2.55.0 (2025-08-27) ### Feat + - changes column name in datasets ls --details command from "File Name" to "Virtual Name". - Improved error message when attempting to move an item to an S3 folder - Changed terminology in messages in datasets rm @@ -461,19 +461,17 @@ - Enables cloning existing jobs with parameter overrides including queue-name, cost-limit, master-instance, job-name, nextflow-version, branch, nextflow-profile, save-logs, use-fusion, workflow-name, and parameter - Provides comprehensive parameter validation and error handling for job cloning operations - ## v2.52.0 (2025-08-25) ### Feat -- Implements filtering options for `cloudos job list` (`filter_status`, `filter_job_name`, `filter_project`, `filter_workflow`, `filter_job_id`, `filter_only_mine` , `filter_owner`, `filter_queue` ) +- Implements filtering options for `cloudos job list` (`filter_status`, `filter_job_name`, `filter_project`, `filter_workflow`, `filter_job_id`, `filter_only_mine` , `filter_owner`, `filter_queue` ) ## v2.51.0 (2025-08-21) ### Fix -- set image name - +- set image name ## v2.50.0 (2025-08-14) @@ -603,7 +601,7 @@ ### Feat -- Adds command to create new folders +- Adds command to create new folders ## v2.34.0 (2025-06-25) @@ -695,7 +693,7 @@ - Updates jobs POST endpoint from v1 to v2 - Removes `cloudos job run-curated-examples` functionality, as it was deprecated from the platform -- Removes the following deprecated `cloudos job run` flags: `spot`, `ignite`, `batch` +- Removes the following deprecated `cloudos job run` flags: `spot`, `ignite`, `batch` - Adds `--git-branch` to `cloudos job run` command, to be able to specify the git branch to run ## v2.22.0 (2025-05-15) @@ -865,138 +863,180 @@ - add workflows list --curated option ### 2.1.0 - 2023-03-30 + - Feature: `cloudos job list` has the new parameter `--last-n-jobs n`, if used, the last -`n` jobs from the user will be collected. Default is last 30, which was the previous behaviour. + `n` jobs from the user will be collected. Default is last 30, which was the previous behaviour. ### 2.0.1 - 2023-03-07 + - Removes some default fields returned from `cloudos job list` command in preparation for -its deprecation from the CloudOS API. In particular, the following fields were removed: - * `resumeWorkDir` - * `project.user` - * `project.team` + its deprecation from the CloudOS API. In particular, the following fields were removed: + _ `resumeWorkDir` + _ `project.user` \* `project.team` ### 2.0.0 - 2023-02-20 + - Remove all cohort browser functionality that will be maintained in a separated -repository. + repository. ### 1.3.2 - 2023-02-08 + - Patch: fixes problems with CloudOS environments using the new API specification for -`projects` endpoint while maintaining backwards compatibility. + `projects` endpoint while maintaining backwards compatibility. ### 1.3.1 - 2022-12-01 + - Patch: fixes `BarRequestException` and `TimeOutException` messages when the response from -the API server is empty. + the API server is empty. ### 1.3.0 - 2022-11-07 + - All Cromwell functionality works now with personal API key. The -`--cromwell-token` argument is maintained for backwards compatibility, but can -be completely substituted by `--apikey`. + `--cromwell-token` argument is maintained for backwards compatibility, but can + be completely substituted by `--apikey`. - Changes `--wdl-importsfile` parameter to be optional even when running a -WDL pipeline as `importsFiles` are not always present in WDL pipelines. + WDL pipeline as `importsFiles` are not always present in WDL pipelines. - Fixes some incomplete error messages. ### 1.2.1 - 2022-11-03 + - Modifies default `--cost-limit` from infinite (`-1`) to `30.0`. This will prevent -wasting resources without a purpose of running a pipeline. + wasting resources without a purpose of running a pipeline. ### 1.2.0 - 2022-10-28 + - Adds `--disable-ssl-verification` new flag to be able to disable SSL certificate -verification when required. It also disables `urllib3` associated warning messages. + verification when required. It also disables `urllib3` associated warning messages. - Adds `--ssl-cert` new option to specify the path to the corresponding SSL certificate -file. + file. ### 1.1.0 - 2022-09-29 + - Adds `--request-interval` new parameter to allow the custom time specification -for job status request. This will be useful for big jobs, to specify a bigger -interval since a smaller one is causing the API to consider it as spam or simply -to crash. + for job status request. This will be useful for big jobs, to specify a bigger + interval since a smaller one is causing the API to consider it as spam or simply + to crash. - Changes `REQUEST_INTERVAL` for `REQUEST_INTERVAL_CROMWELL`. This is only used in the -`cromwell` workflows. + `cromwell` workflows. ### 1.0.0 - 2022-07-28 + - Adds `--parameter / -p` new argument to allow to specify the job -parameters using the command-line. -This version introduces a backwards incompatible change -The -p flag is now used for parameters and not for the nextflow profile. -Commands that utilised -p for denoting a profile will break with this release. + parameters using the command-line. + This version introduces a backwards incompatible change + The -p flag is now used for parameters and not for the nextflow profile. + Commands that utilised -p for denoting a profile will break with this release. ### 0.1.4 - 2022-07-27 + - Unittests added for method `load` and `create` from class `Cloudos` ### 0.1.3 - 2022-07-26 + - Adds `--cost-limit ` to `cloudos job run` command. It is -used to indicate the job cost limit, in $. + used to indicate the job cost limit, in $. ### 0.1.2b - 2022-07-26 + - Adds worked example of CohortBrowser to README ### 0.1.2 - 2022-07-14 + - Adds WDL pipeline support, iteration 2: WDL workflows can be run -using the regular `cloudos job run` using the new arguments: - * `--wdl-mainfile` - * `--wdl-importsfile` - * `--cromwell-token` + using the regular `cloudos job run` using the new arguments: + _ `--wdl-mainfile` + _ `--wdl-importsfile` \* `--cromwell-token` - Adds the new argument `--repository-platform` to specify the -repository platform (Default: 'github'). + repository platform (Default: 'github'). ### 0.1.1 - 2022-07-12 + - Adds WDL pipeline support, iteration 1: cromwell server managing. -Now, a new command `cloudos cromwell` is available, with the following -subcommands: - * status - * start - * stop + Now, a new command `cloudos cromwell` is available, with the following + subcommands: + _ status + _ start \* stop ### 0.1.0 - 2022-07-07 + - Adds `cloudos workflow list` command. This command allows to -collect all the workflows data from a given workspace. + collect all the workflows data from a given workspace. - Adds JSON output for `cloudos job list` and `cloudos workflow list` -commands. + commands. ### 0.0.9 - 2022-06-28 + - Adds support for lustre storage with the new `--storage-mode` and -`--lustre-size` parameters. + `--lustre-size` parameters. ### 0.0.8 - 2022-06-16 + - Adds `--nextflow-profile` parameter to accept nextflow profiles. It -also makes `--job-config` parameter optional, as a run with only -profiles is possible. + also makes `--job-config` parameter optional, as a run with only + profiles is possible. ### 0.0.7a - 2022-04-07 + - Hotfix: extends the wait time from 1s to 60s when checking for job -status (`--wait-completion true`). This helps preventing API call -errors from CloudOS API server. + status (`--wait-completion true`). This helps preventing API call + errors from CloudOS API server. ### 0.0.7 - 2021-03-10 + - Adds support for aborted jobs - Adds `--batch` option to `job` subtool to be able to use `batch` -executor instead of the default `ignite` in CloudOS. + executor instead of the default `ignite` in CloudOS. ### 0.0.6 - 2021-12-09 + - Unittests added for method `process_job_list` from class `Cloudos` - Unittests added for method `convert_nextflow_to_json` from class `Jobs` ### 0.0.5b - 2021-11-24 + - Adds Cohort class ### 0.0.5 - 2021-11-16 -- Adds `git-commit` and `--git-tag` optional arguments to -`cloudos job run` to be able to set the github commit or tag -to run. + +- Adds `git-commit` and `--git-tag` optional arguments to + `cloudos job run` to be able to set the github commit or tag + to run. ### 0.0.4 - 2021-10-15 + - Changes `--job-params` to `--job-config` -- Removes the collection of the `project.description` column from the -returned json when listing all jobs, as this column is not available -in all the CloudOS workspaces. +- Removes the collection of the `project.description` column from the + returned json when listing all jobs, as this column is not available + in all the CloudOS workspaces. ### 0.0.3 - 2021-09-08 + - Adds `cloudos job list` command. - Minor changes in `stdout` of the other commands to improve + readability. +- Adds a small docstring to each command. + +### 0.0.2 - 2021-09-07 + +- Refactors `runjob` and `jobstatus` commands. Now, the main + `cloudos` tool have the `job` subtool which in turn has its + `run` and `status` commands performing the previous + functionality. This way, now the tool can be used with: + `cloudos job run [OPTIONS]` and `cloudos job status [OPTIONS]`. +- Adding `--wait-completion` option to `cloudos job run` command, + to be able to wait until job completion or failure. + +### 0.0.1 - 2021-08-18 + +Initial implementation of the `cloudos` python package: + +- Implements `runjob` and `jobstatus` commands to send jobs and get + - Minor changes in `stdout` of the other commands to improve readability. - Adds a small docstring to each command. ### 0.0.2 - 2021-09-07 + - Refactors `runjob` and `jobstatus` commands. Now, the main `cloudos` tool have the `job` subtool which in turn has its `run` and `status` commands performing the previous @@ -1006,6 +1046,8 @@ functionality. This way, now the tool can be used with: to be able to wait until job completion or failure. ### 0.0.1 - 2021-08-18 + Initial implementation of the `cloudos` python package: + - Implements `runjob` and `jobstatus` commands to send jobs and get their status, respectively.