diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index bff65974..fe2ee97e 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -31,7 +31,7 @@ The Balena object can be configured with a dict of type Settings ```python balena = Balena({ "balena_host": "balena-cloud.com", - "api_version": "v6", + "api_version": "v7", "device_actions_endpoint_version": "v1", "data_directory": "/home/example/.balena", "image_cache_time": str(1 * 1000 * 60 * 60 * 24 * 7), # 1 week @@ -91,7 +91,6 @@ hesitate to open an issue in GitHub](https://github.com/balena-io/balena-sdk-pyt - [get_all_by_organization(org_handle_or_id, options)](#application.get_all_by_organization) ⇒ [List[TypeApplication]](#typeapplication) - [get_all_directly_accessible(options)](#application.get_all_directly_accessible) ⇒ [List[TypeApplication]](#typeapplication) - [get_by_name(app_name, options, context)](#application.get_by_name) ⇒ [TypeApplication](#typeapplication) - - [get_by_owner(app_name, owner, options)](#application.get_by_owner) ⇒ [TypeApplication](#typeapplication) - [get_dashboard_url(app_id)](#application.get_dashboard_url) ⇒ str - [get_directly_accessible(slug_or_uuid_or_id, options)](#application.get_directly_accessible) ⇒ [TypeApplication](#typeapplication) - [get_id(slug_or_uuid_or_id)](#application.get_id) ⇒ int @@ -281,6 +280,7 @@ hesitate to open an issue in GitHub](https://github.com/balena-io/balena-sdk-pyt - [get_config(slug_or_uuid_or_id, options)](#deviceos.get_config) ⇒ None - [get_download_size(device_type, version)](#deviceos.get_download_size) ⇒ float - [get_max_satisfying_version(device_type, version_or_range, os_type)](#deviceos.get_max_satisfying_version) ⇒ Optional[str] + - [get_supervisor_releases_for_cpu_architecture(cpu_architecture_slug_or_id, options)](#deviceos.get_supervisor_releases_for_cpu_architecture) ⇒ [List[ReleaseType]](#releasetype) - [get_supported_os_update_versions(device_type, current_version)](#deviceos.get_supported_os_update_versions) ⇒ None - [is_architecture_compatible_with(os_architecture, application_architecture)](#deviceos.is_architecture_compatible_with) ⇒ None - [is_supported_os_update(device_type, current_version, target_version)](#deviceos.is_supported_os_update) ⇒ bool @@ -502,24 +502,6 @@ Get all applications directly accessible by the user >>> balena.models.application.get("myapp") ``` - -### Function: get_by_owner(app_name, owner, options) ⇒ [TypeApplication](#typeapplication) - -Get a single application using the appname and the handle of the owning organization. - -#### Args: - app_name (str): application name. - owner (str): The handle of the owning organization. - options (AnyObject): extra pine options to use. - -#### Returns: - TypeApplication: application info. - -#### Examples: -```python ->>> balena.models.application.get_by_owner('foo', 'my_org') -``` - ### Function: get_dashboard_url(app_id) ⇒ str @@ -1943,11 +1925,9 @@ Note a device. ### Function: set_supervisor_release(uuid_or_id, supervisor_version_or_id) ⇒ None Set a specific device to run a particular supervisor release. - #### Args: uuid_or_id (Union[str, int]): device uuid (string) or id (int) supervisor_version_or_id (Union[str, int]): the version of a released supervisor (string) or id (number) - #### Examples: ```python >>> balena.models.device.set_supervisor_release('f55dcdd9ad', 'v13.0.0') @@ -3135,6 +3115,26 @@ Get OS download size estimate. Currently only the raw (uncompressed) size is rep #### Returns: float: OS image download size, in bytes. + +### Function: get_supervisor_releases_for_cpu_architecture(cpu_architecture_slug_or_id, options) ⇒ [List[ReleaseType]](#releasetype) + +Returns the Releases of the supervisor for the CPU Architecture + +#### Args: + cpu_architecture_slug_or_id (Union[str, int]): The slug (string) or id (number) for the CPU Architecture. + options (AnyObject): extra pine options to use. + +#### Returns: + ReleaseType: release info. + + +Example: + results = balena.models.os.get_supervisor_releases_for_cpu_architecture('aarch64'); + results = balena.models.os.get_supervisor_releases_for_cpu_architecture( + 'aarch64', + { $filter: { raw_version: '12.11.0' } }, + ); + ### Function: get_supported_os_update_versions(device_type, current_version) ⇒ None @@ -4175,7 +4175,7 @@ The name must be a string; the optional doc argument can have any type. "start_timestamp": str, "end_timestamp": str, "push_timestamp": str, - "image_size": int, + "image_size": str, "dockerfile": str, "error_message": str, "is_a_build_of__service": Union[List[ServiceType], PineDeferred], @@ -4367,7 +4367,9 @@ The name must be a string; the optional doc argument can have any type. "release_image": Optional[List[ReleaseImageType]], "should_be_running_on__application": Optional[List[TypeApplication]], "is_running_on__device": Optional[List[TypeDevice]], - "should_be_running_on__device": Optional[List[TypeDevice]], + "is_pinned_to__device": Optional[List[TypeDevice]], + "should_operate__device": Optional[List[TypeDevice]], + "should_manage__device": Optional[List[TypeDevice]], "release_tag": Optional[List[BaseTagType]] } ``` @@ -4427,7 +4429,9 @@ The name must be a string; the optional doc argument can have any type. "release_image": Optional[List[ReleaseImageType]], "should_be_running_on__application": Optional[List[TypeApplication]], "is_running_on__device": Optional[List[TypeDevice]], - "should_be_running_on__device": Optional[List[TypeDevice]], + "is_pinned_to__device": Optional[List[TypeDevice]], + "should_operate__device": Optional[List[TypeDevice]], + "should_manage__device": Optional[List[TypeDevice]], "release_tag": Optional[List[BaseTagType]], "images": List[ImageBasicInfoType], "user": BasicUserInfoType @@ -4479,22 +4483,6 @@ The name must be a string; the optional doc argument can have any type. ``` -### SupervisorReleaseType - - -```python -{ - "created_at": str, - "id": int, - "supervisor_version": str, - "image_name": str, - "is_public": bool, - "note": Optional[str], - "is_for__device_type": Union[List[DeviceTypeType], PineDeferred] -} -``` - - ### TeamApplicationAccessType @@ -4544,7 +4532,7 @@ The name must be a string; the optional doc argument can have any type. "id": int, "created_at": str, "app_name": str, - "actor": Union[List[ActorType], int], + "actor": Union[List[ActorType], PineDeferred], "slug": str, "uuid": str, "is_accessible_by_support_until__date": str, @@ -4586,7 +4574,7 @@ The name must be a string; the optional doc argument can have any type. "id": int, "created_at": str, "app_name": str, - "actor": Union[List[ActorType], int], + "actor": Union[List[ActorType], PineDeferred], "slug": str, "uuid": str, "is_accessible_by_support_until__date": str, @@ -4641,7 +4629,7 @@ The name must be a string; the optional doc argument can have any type. ```python { "id": int, - "actor": Union[List[ActorType], int], + "actor": Union[List[ActorType], PineDeferred], "created_at": str, "modified_at": str, "custom_latitude": str, @@ -4669,12 +4657,9 @@ The name must be a string; the optional doc argument can have any type. "os_version": str, "provisioning_progress": int, "provisioning_state": str, - "state": TypeDeviceState, "status": str, - "status_sort_index": int, "supervisor_version": str, "uuid": str, - "vpn_address": str, "api_heartbeat_state": Literal["online", "offline", "timeout", "unknown"], "memory_usage": int, "memory_total": int, @@ -4685,15 +4670,16 @@ The name must be a string; the optional doc argument can have any type. "cpu_temp": int, "cpu_id": str, "is_undervolted": bool, - "overall_status": Any, + "overall_status": Literal["configuring", "inactive", "post-provisioning", "updating", "operational", "disconnected", "reduced-functionality"], "overall_progress": int, "is_of__device_type": Union[List[DeviceTypeType], PineDeferred], "belongs_to__application": Union[List[TypeApplication], PineDeferred], "belongs_to__user": Union[List[UserType], PineDeferred, None], "is_running__release": Union[List[ReleaseType], PineDeferred, None], - "should_be_running__release": Union[List[ReleaseType], PineDeferred, None], + "is_pinned_on__release": Union[List[ReleaseType], PineDeferred, None], "is_managed_by__service_instance": Union[List[ServiceInstanceType], PineDeferred, None], - "should_be_managed_by__supervisor_release": Union[List[SupervisorReleaseType], PineDeferred, None], + "should_be_operated_by__release": Union[List[ReleaseType], PineDeferred, None], + "should_be_managed_by__release": Union[List[ReleaseType], PineDeferred, None], "device_config_variable": Optional[List[EnvironmentVariableBase]], "device_environment_variable": Optional[List[EnvironmentVariableBase]], "device_tag": Optional[List[BaseTagType]], @@ -4703,24 +4689,13 @@ The name must be a string; the optional doc argument can have any type. ``` -### TypeDeviceState - - -```python -{ - "key": str, - "name": str -} -``` - - ### TypeDeviceWithServices ```python { "id": int, - "actor": Union[List[ActorType], int], + "actor": Union[List[ActorType], PineDeferred], "created_at": str, "modified_at": str, "custom_latitude": str, @@ -4748,12 +4723,9 @@ The name must be a string; the optional doc argument can have any type. "os_version": str, "provisioning_progress": int, "provisioning_state": str, - "state": TypeDeviceState, "status": str, - "status_sort_index": int, "supervisor_version": str, "uuid": str, - "vpn_address": str, "api_heartbeat_state": Literal["online", "offline", "timeout", "unknown"], "memory_usage": int, "memory_total": int, @@ -4764,15 +4736,16 @@ The name must be a string; the optional doc argument can have any type. "cpu_temp": int, "cpu_id": str, "is_undervolted": bool, - "overall_status": Any, + "overall_status": Literal["configuring", "inactive", "post-provisioning", "updating", "operational", "disconnected", "reduced-functionality"], "overall_progress": int, "is_of__device_type": Union[List[DeviceTypeType], PineDeferred], "belongs_to__application": Union[List[TypeApplication], PineDeferred], "belongs_to__user": Union[List[UserType], PineDeferred, None], "is_running__release": Union[List[ReleaseType], PineDeferred, None], - "should_be_running__release": Union[List[ReleaseType], PineDeferred, None], + "is_pinned_on__release": Union[List[ReleaseType], PineDeferred, None], "is_managed_by__service_instance": Union[List[ServiceInstanceType], PineDeferred, None], - "should_be_managed_by__supervisor_release": Union[List[SupervisorReleaseType], PineDeferred, None], + "should_be_operated_by__release": Union[List[ReleaseType], PineDeferred, None], + "should_be_managed_by__release": Union[List[ReleaseType], PineDeferred, None], "device_config_variable": Optional[List[EnvironmentVariableBase]], "device_environment_variable": Optional[List[EnvironmentVariableBase]], "device_tag": Optional[List[BaseTagType]], @@ -4809,7 +4782,7 @@ The name must be a string; the optional doc argument can have any type. ```python { "id": int, - "actor": Union[List[ActorType], int], + "actor": Union[List[ActorType], PineDeferred], "created_at": str, "username": str, "organization_membership": Optional[List[OrganizationMembershipType]], diff --git a/balena/__init__.py b/balena/__init__.py index 9b4e1fd4..8eac56e8 100644 --- a/balena/__init__.py +++ b/balena/__init__.py @@ -30,7 +30,7 @@ ```python balena = Balena({ "balena_host": "balena-cloud.com", - "api_version": "v6", + "api_version": "v7", "device_actions_endpoint_version": "v1", "data_directory": "/home/example/.balena", "image_cache_time": str(1 * 1000 * 60 * 60 * 24 * 7), # 1 week diff --git a/balena/models/api_key.py b/balena/models/api_key.py index f352006b..2f9b684a 100644 --- a/balena/models/api_key.py +++ b/balena/models/api_key.py @@ -134,8 +134,8 @@ def get_provisioning_api_keys_by_application( >>> balena.models.api_key.get_provisioning_api_keys_by_application("myorg/myapp") """ - app = self.__application.get(slug_or_uuid_or_id, {"$select": "actor"}) - return self.get_all(merge({"$filter": {"is_of__actor": app.get("actor")}}, options)) + actor_id = self.__application.get(slug_or_uuid_or_id, {"$select": "actor"})["actor"]["__id"] + return self.get_all(merge({"$filter": {"is_of__actor": actor_id}}, options)) def get_device_api_keys_by_device(self, uuid_or_id: Union[str, int], options: AnyObject = {}) -> List[APIKeyType]: """ @@ -150,8 +150,8 @@ def get_device_api_keys_by_device(self, uuid_or_id: Union[str, int], options: An >>> balena.models.api_key.get_device_api_keys_by_device(1111386) """ - dev = self.__device.get(uuid_or_id, {"$select": "actor"}) - return self.get_all(merge({"$filter": {"is_of__actor": dev["actor"]}}, options)) + actor_id = self.__device.get(uuid_or_id, {"$select": "actor"})["actor"]["__id"] + return self.get_all(merge({"$filter": {"is_of__actor": actor_id}}, options)) def get_all_named_user_api_keys(self, options: AnyObject = {}) -> List[APIKeyType]: """ diff --git a/balena/models/application.py b/balena/models/application.py index 53c92935..729bdf23 100644 --- a/balena/models/application.py +++ b/balena/models/application.py @@ -2,7 +2,6 @@ from math import isinf from typing import List, Literal, Optional, Union, cast from urllib.parse import urljoin -from deprecated import deprecated from .. import exceptions from ..balena_auth import request @@ -403,37 +402,6 @@ def get_all_by_organization( } ) - @deprecated("get_by_owner will be removed in a future release, use get_all_by_organization instead") - def get_by_owner(self, app_name: str, owner: str, options: AnyObject = {}) -> TypeApplication: - """ - Get a single application using the appname and the handle of the owning organization. - - Args: - app_name (str): application name. - owner (str): The handle of the owning organization. - options (AnyObject): extra pine options to use. - - Returns: - TypeApplication: application info. - - Examples: - >>> balena.models.application.get_by_owner('foo', 'my_org') - """ - - slug = f"{owner.lower()}/{app_name.lower()}" - app = self.__pine.get( - { - "resource": "application", - "id": {"slug": slug}, - "options": options, - } - ) - - if app is None: - raise exceptions.ApplicationNotFound(slug) - - return app - def has(self, slug_or_uuid_or_id: Union[str, int]) -> bool: """ Check if an application exists. diff --git a/balena/models/device.py b/balena/models/device.py index 5aa1c122..c304a0d2 100644 --- a/balena/models/device.py +++ b/balena/models/device.py @@ -83,19 +83,6 @@ class SupervisorStateType(TypedDict): download_progress: str -class DeviceStatus: - """ - Balena device statuses. - """ - - IDLE = "Idle" - CONFIGURING = "Configuring" - UPDATING = "Updating" - OFFLINE = "Offline" - POST_PROVISIONING = "Post Provisioning" - INACTIVE = "Inactive" - - class Device: """ This class implements device model for balena python SDK. @@ -1620,7 +1607,7 @@ def is_tracking_application_release(self, uuid_or_id: Union[str, int]) -> bool: bool: is tracking the current application release. """ - return not bool(self.get(uuid_or_id, {"$select": "should_be_running__release"})["should_be_running__release"]) + return not bool(self.get(uuid_or_id, {"$select": "is_pinned_on__release"})["is_pinned_on__release"]) # TODO: enable device batching def pin_to_release( @@ -1667,7 +1654,7 @@ def pin_to_release( { "resource": "device", "id": device["id"], - "body": {"should_be_running__release": release["id"]}, + "body": {"is_pinned_on__release": release["id"]}, } ) @@ -1679,7 +1666,7 @@ def track_application_release(self, uuid_or_id_or_ids: Union[str, int, List[int] uuid_or_id_or_ids (Union[str, int, List[int]]): device uuid (str) or id (int) or ids (List[int]) """ - self.__set(uuid_or_id_or_ids, {"should_be_running__release": None}) + self.__set(uuid_or_id_or_ids, {"is_pinned_on__release": None}) # TODO: enable device batching def set_supervisor_release( @@ -1689,11 +1676,9 @@ def set_supervisor_release( ) -> None: """ Set a specific device to run a particular supervisor release. - Args: uuid_or_id (Union[str, int]): device uuid (string) or id (int) supervisor_version_or_id (Union[str, int]): the version of a released supervisor (string) or id (number) - Examples: >>> balena.models.device.set_supervisor_release('f55dcdd9ad', 'v13.0.0') """ @@ -1701,46 +1686,29 @@ def set_supervisor_release( uuid_or_id, { "$select": ["id", "supervisor_version", "os_version"], - "$expand": {"is_of__device_type": {"$select": "slug"}}, + "$expand": {"is_of__device_type": {"$select": "is_of__cpu_architecture"}}, }, ) - device_type_slug = device["is_of__device_type"][0]["slug"] + cpu_arch_id = device["is_of__device_type"][0]["is_of__cpu_architecture"]["__id"] release_options = { "$top": 1, "$select": "id", - "$filter": { - "is_for__device_type": { - "$any": { - "$alias": "dt", - "$expr": { - "dt": { - "slug": device_type_slug, - }, - }, - }, - }, - }, + "$filter": {"id" if is_id(supervisor_version_or_id) else "raw_version": supervisor_version_or_id}, } - if is_id(supervisor_version_or_id): - release_options["$filter"]["id"] = supervisor_version_or_id - else: - release_options["$filter"]["supervisor_version"] = supervisor_version_or_id - try: - release = self.__pine.get({"resource": "supervisor_release", "options": release_options})[0] + release = self.__device_os.get_supervisor_releases_for_cpu_architecture(cpu_arch_id, release_options)[0] except IndexError: raise Exception(f"Supervisor release not found {supervisor_version_or_id}") ensure_version_compatibility(device["supervisor_version"], MIN_SUPERVISOR_MC_API, "supervisor") ensure_version_compatibility(device["os_version"], MIN_OS_MC, "host OS") - self.__pine.patch( { "resource": "device", "id": device["id"], - "body": {"should_be_managed_by__supervisor_release": release["id"]}, + "body": {"should_be_managed_by__release": release["id"]}, } ) diff --git a/balena/models/os.py b/balena/models/os.py index 872e8376..20aefd5f 100644 --- a/balena/models/os.py +++ b/balena/models/os.py @@ -10,7 +10,8 @@ from ..hup import get_hup_action_type from ..pine import PineClient from ..types import AnyObject -from ..utils import compare, merge, normalize_balena_semver +from ..types.models import ReleaseType +from ..utils import compare, merge, normalize_balena_semver, is_id from ..settings import Settings from .application import Application from .device_type import DeviceType @@ -493,6 +494,89 @@ def is_architecture_compatible_with(self, os_architecture: str, application_arch return True + def get_supervisor_releases_for_cpu_architecture( + self, cpu_architecture_slug_or_id: Union[str, int], options: AnyObject = {} + ) -> List[ReleaseType]: + """ + Returns the Releases of the supervisor for the CPU Architecture + + Args: + cpu_architecture_slug_or_id (Union[str, int]): The slug (string) or id (number) for the CPU Architecture. + options (AnyObject): extra pine options to use. + + Returns: + ReleaseType: release info. + + + Example: + results = balena.models.os.get_supervisor_releases_for_cpu_architecture('aarch64'); + results = balena.models.os.get_supervisor_releases_for_cpu_architecture( + 'aarch64', + { $filter: { raw_version: '12.11.0' } }, + ); + """ + return self.__pine.get( + { + "resource": "release", + "options": merge( + { + "$select": ["id", "raw_version", "known_issue_list"], + "$filter": { + "status": "success", + "is_final": True, + "is_invalidated": False, + "semver_major": {"$gt": 0}, + "belongs_to__application": { + "$any": { + "$alias": "a", + "$expr": { + "$and": [ + {"a": {"slug": {"$startswith": "balena_os/"}}}, + {"a": {"slug": {"$endswith": "-supervisor"}}}, + ], + "a": { + "is_public": True, + "is_host": False, + "is_for__device_type": { + "$any": { + "$alias": "dt", + "$expr": { + "dt": { + "is_of__cpu_architecture": ( + cpu_architecture_slug_or_id + if is_id(cpu_architecture_slug_or_id) + else { + "$any": { + "$alias": "c", + "$expr": { + "c": { + "slug": cpu_architecture_slug_or_id, + }, + }, + }, + } + ), + }, + }, + }, + }, + }, + }, + }, + }, + }, + "$orderby": [ + {"semver_major": "desc"}, + {"semver_minor": "desc"}, + {"semver_patch": "desc"}, + {"revision": "desc"}, + ], + }, + options, + ), + } + ) + def __tags_to_dict(self, tags: List[Any]) -> Dict[str, str]: tag_map = {} diff --git a/balena/settings.py b/balena/settings.py index ae2d8981..5259d7cc 100644 --- a/balena/settings.py +++ b/balena/settings.py @@ -49,7 +49,7 @@ def remove(self, key: str) -> bool: DEFAULT_SETTINGS = { # These are default config values "balena_host": "balena-cloud.com", - "api_version": "v6", + "api_version": "v7", "device_actions_endpoint_version": "v1", # cache time : 1 week in milliseconds "image_cache_time": str(1 * 1000 * 60 * 60 * 24 * 7), diff --git a/balena/types/models.py b/balena/types/models.py index e128436f..d7488062 100644 --- a/balena/types/models.py +++ b/balena/types/models.py @@ -9,7 +9,7 @@ class PineDeferred(TypedDict): NavigationResource = Union[List[__T], PineDeferred] ReverseNavigationResource = Union[List[__T], None] -ConceptTypeNavigationResource = Union[List[__T], int] +ConceptTypeNavigationResource = NavigationResource[__T] OptionalNavigationResource = Union[List[__T], PineDeferred, None] @@ -242,11 +242,6 @@ class DeviceTypeType(TypedDict): device_type_alias: ReverseNavigationResource["DeviceTypeAliasType"] -class TypeDeviceState(TypedDict): - key: str - name: str - - class ServiceInstanceType(TypedDict): id: int created_at: str @@ -255,17 +250,6 @@ class ServiceInstanceType(TypedDict): last_heartbeat: str -class SupervisorReleaseType(TypedDict): - created_at: str - id: int - supervisor_version: str - image_name: str - is_public: bool - note: Optional[str] - - is_for__device_type: NavigationResource[DeviceTypeType] - - class ImageInstallType(TypedDict): id: int download_progress: Optional[float] @@ -308,12 +292,9 @@ class TypeDevice(TypedDict): os_version: str provisioning_progress: int provisioning_state: str - state: TypeDeviceState status: str - status_sort_index: int supervisor_version: str uuid: str - vpn_address: str api_heartbeat_state: Literal["online", "offline", "timeout", "unknown"] memory_usage: int memory_total: int @@ -325,7 +306,16 @@ class TypeDevice(TypedDict): cpu_id: str is_undervolted: bool # This is a computed term - overall_status: Any + overall_status: Literal[ + "configuring", + "inactive", + "post-provisioning", + "updating", + "operational", + "disconnected", + "reduced-functionality", + ] + # This is a computed term overall_progress: int @@ -333,9 +323,10 @@ class TypeDevice(TypedDict): belongs_to__application: NavigationResource[TypeApplication] belongs_to__user: OptionalNavigationResource[UserType] is_running__release: OptionalNavigationResource["ReleaseType"] - should_be_running__release: OptionalNavigationResource["ReleaseType"] + is_pinned_on__release: OptionalNavigationResource["ReleaseType"] is_managed_by__service_instance: OptionalNavigationResource[ServiceInstanceType] - should_be_managed_by__supervisor_release: OptionalNavigationResource[SupervisorReleaseType] + should_be_operated_by__release: OptionalNavigationResource["ReleaseType"] + should_be_managed_by__release: OptionalNavigationResource["ReleaseType"] device_config_variable: ReverseNavigationResource["EnvironmentVariableBase"] device_environment_variable: ReverseNavigationResource["EnvironmentVariableBase"] device_tag: ReverseNavigationResource["BaseTagType"] @@ -442,7 +433,7 @@ class ImageType(TypedDict): start_timestamp: str end_timestamp: str push_timestamp: str - image_size: int + image_size: str dockerfile: str error_message: str is_a_build_of__service: NavigationResource["ServiceType"] @@ -508,7 +499,9 @@ class ReleaseType(TypedDict): release_image: ReverseNavigationResource[ReleaseImageType] should_be_running_on__application: ReverseNavigationResource[TypeApplication] is_running_on__device: ReverseNavigationResource[TypeDevice] - should_be_running_on__device: ReverseNavigationResource[TypeDevice] + is_pinned_to__device: ReverseNavigationResource[TypeDevice] + should_operate__device: ReverseNavigationResource[TypeDevice] + should_manage__device: ReverseNavigationResource[TypeDevice] release_tag: ReverseNavigationResource[BaseTagType] diff --git a/balena/utils.py b/balena/utils.py index dd0dd833..a5355996 100644 --- a/balena/utils.py +++ b/balena/utils.py @@ -172,8 +172,7 @@ def get_current_service_details_pine_expand( def get_single_install_summary(raw_data: Any) -> Any: # TODO: Please compare me to node-sdk version """ - Builds summary data for an image install or gateway download - + Builds summary data for an image install """ image = raw_data["image"][0] @@ -207,11 +206,7 @@ def generate_current_service_details(raw_device: TypeDevice) -> TypeDeviceWithSe grouped_services[obj.pop("service_name", None)].append(obj) raw_device["current_services"] = dict(grouped_services) # type: ignore - raw_device["current_gateway_downloads"] = [ # type: ignore - get_single_install_summary(i) for i in raw_device.get("gateway_download", []) - ] raw_device.pop("image_install", None) # type: ignore - raw_device.pop("gateway_download", None) # type: ignore return raw_device # type: ignore diff --git a/tests/functional/models/test_application.py b/tests/functional/models/test_application.py index cb9c65ac..23861c5b 100644 --- a/tests/functional/models/test_application.py +++ b/tests/functional/models/test_application.py @@ -72,21 +72,6 @@ def test_06_get_all_by_organization(self): self.balena.models.application.get_all_by_organization(self.org_id)[0]["app_name"], "FooBar" ) - def test_06_get_by_owner(self): - with self.assertRaises(self.helper.balena_exceptions.ApplicationNotFound): - self.balena.models.application.get_by_owner("AppNotExist", self.helper.credentials["user_id"]) - - self.assertEqual( - self.balena.models.application.get_by_owner("FooBar", self.helper.default_organization["handle"])[ - "app_name" - ], - "FooBar", - ) - - with self.assertRaises(Exception) as cm: - self.balena.models.application.get_by_owner("FooBar", "random_username") - self.assertIn("Application not found: random_username/foobar", cm.exception.message) # type: ignore - def test_07_has(self): self.assertFalse(self.balena.models.application.has("AppNotExist")) self.assertTrue(self.balena.models.application.has(self.app_slug)) diff --git a/tests/functional/models/test_device_os.py b/tests/functional/models/test_device_os.py index 30a53808..bd415041 100644 --- a/tests/functional/models/test_device_os.py +++ b/tests/functional/models/test_device_os.py @@ -38,6 +38,48 @@ def test_02_get_hup_action_type(self): for ver in testVersion: get_hup_action_type("", ver, ver) + def test_03_get_supervisor_releases_for_cpu_architecture(self): + # return an empty array if no image was found + svRelease = self.balena.models.os.get_supervisor_releases_for_cpu_architecture("notACpuArch") + self.assertEqual(svRelease, []) + + # by default include the id, semver and known_issue_list + dt = self.balena.models.device_type.get( + "raspberrypi4-64", {"$select": "slug", "$expand": {"is_of__cpu_architecture": {"$select": "slug"}}} + ) + + svReleases = self.balena.models.os.get_supervisor_releases_for_cpu_architecture( + dt["is_of__cpu_architecture"][0]["slug"] + ) + + self.assertGreater(len(svReleases), 0) + svRelease = svReleases[0] + self.assertListEqual(sorted(svRelease.keys()), sorted(["id", "raw_version", "known_issue_list"])) + + # return the right string when asking for raspberrypi4-64 and v12.11.0 + dt = self.balena.models.device_type.get( + "raspberrypi4-64", {"$select": "slug", "$expand": {"is_of__cpu_architecture": {"$select": "slug"}}} + ) + svReleases = self.balena.models.os.get_supervisor_releases_for_cpu_architecture( + dt["is_of__cpu_architecture"][0]["slug"], + { + "$select": "id", + "$expand": { + "release_image": { + "$select": "id", + "$expand": {"image": {"$select": "is_stored_at__image_location"}}, + }, + }, + "$filter": {"raw_version": "12.11.0"}, + }, + ) + + self.assertEqual(len(svReleases), 1) + svRelease = svReleases[0] + imageLocation = svRelease["release_image"][0]["image"][0]["is_stored_at__image_location"] + self.assertRegex(imageLocation, r"registry2\.[a-z0-9_\-.]+\.[a-z]+\/v2\/[0-9a-f]+") + self.assertEqual(imageLocation, "registry2.balena-cloud.com/v2/4ca706e1c624daff7e519b3009746b2c") + if __name__ == "__main__": unittest.main() diff --git a/tests/functional/test_auth.py b/tests/functional/test_auth.py index 445583d2..e5abbc96 100644 --- a/tests/functional/test_auth.py +++ b/tests/functional/test_auth.py @@ -159,9 +159,9 @@ def test_17_should_login_with_device_key(self): self.assertEqual(whoami["actorType"], "device") self.assertEqual(whoami["actorTypeId"], self.app_info["device"]["id"]) self.assertEqual(whoami["uuid"], device_uuid) - self.assertEqual(whoami["id"], self.app_info["device"]["actor"]) + self.assertEqual(whoami["id"], self.app_info["device"]["actor"]["__id"]) - self.assertEqual(self.balena.auth.get_actor_id(), self.app_info["device"]["actor"]) + self.assertEqual(self.balena.auth.get_actor_id(), self.app_info["device"]["actor"]["__id"]) errMsg = "The authentication credentials in use are not of a user" with self.assertRaises(Exception) as cm: @@ -190,10 +190,10 @@ def test_18_should_login_with_app_key(self): self.assertEqual(whoami["actorType"], "application") self.assertEqual(whoami["actorTypeId"], app_id) - self.assertEqual(whoami["id"], self.app_info["app"]["actor"]) + self.assertEqual(whoami["id"], self.app_info["app"]["actor"]["__id"]) self.assertEqual(whoami["slug"], self.app_info["app"]["slug"]) - self.assertEqual(self.balena.auth.get_actor_id(), self.app_info["app"]["actor"]) + self.assertEqual(self.balena.auth.get_actor_id(), self.app_info["app"]["actor"]["__id"]) errMsg = "The authentication credentials in use are not of a user" with self.assertRaises(Exception) as cm: