From fd0505337ffc25a9105d8ac00d5cb9f0f0ac9446 Mon Sep 17 00:00:00 2001 From: Yaswanth Kumar Togarapu Date: Thu, 19 Sep 2024 13:01:39 +0530 Subject: [PATCH] fix!: remove unsupported APIs BREAKING CHANGE: The SDK methods for unsupported APIs are dropped --- README.md | 29 +- docs/source/build.rst | 35 - docs/source/deployment.rst | 29 - docs/source/native_network.rst | 31 - docs/source/package.rst | 34 - docs/source/project.rst | 38 - docs/source/routed_network.rst | 33 - docs/source/secret.rst | 23 - docs/source/static_route.rst | 26 - docs/source/volumes.rst | 26 - rapyuta_io/__init__.py | 10 +- rapyuta_io/clients/__init__.py | 6 +- rapyuta_io/clients/build.py | 603 ------ rapyuta_io/clients/buildoperation.py | 64 - rapyuta_io/clients/catalog_client.py | 141 +- rapyuta_io/clients/common_models.py | 81 - rapyuta_io/clients/core_api_client.py | 169 +- rapyuta_io/clients/deployment.py | 303 --- rapyuta_io/clients/device.py | 49 +- rapyuta_io/clients/native_network.py | 255 --- rapyuta_io/clients/package.py | 967 --------- rapyuta_io/clients/persistent_volumes.py | 348 ---- rapyuta_io/clients/plan.py | 124 -- rapyuta_io/clients/project.py | 70 +- rapyuta_io/clients/provision_client.py | 93 - rapyuta_io/clients/routed_network.py | 153 -- rapyuta_io/clients/secret.py | 179 -- rapyuta_io/clients/static_route.py | 49 - rapyuta_io/clients/validation_schema.py | 44 - rapyuta_io/rio_client.py | 1032 +--------- rapyuta_io/utils/settings.py | 2 +- sdk_test/config.json.example | 19 +- sdk_test/config.py | 53 +- sdk_test/coreapi/project_test.py | 52 - sdk_test/coreapi/query_metrics_test.py | 94 - sdk_test/coreapi/secret_test.py | 40 - sdk_test/coreapi/user_test.py | 3 - sdk_test/coreapi/usergroup_test.py | 17 +- sdk_test/device/deployment_test.py | 66 - sdk_test/device/topic_test.py | 163 -- sdk_test/jsons/builds/listener.json | 9 - sdk_test/jsons/builds/pingpong.json | 16 - sdk_test/jsons/builds/talker-noetic.json | 10 - sdk_test/jsons/builds/talker.json | 9 - .../jsons/builds/throttle-latch-build.json | 15 - sdk_test/jsons/packages/cloud-non-ros.json | 53 - sdk_test/jsons/packages/cloud-transform.json | 59 - .../packages/delete-package-using-client.json | 53 - sdk_test/jsons/packages/delete-package.json | 53 - sdk_test/jsons/packages/device-volume.json | 44 - ...ast-talker-device-docker-with-rosbags.json | 82 - .../inbound-incoming-scoped-targeted.json | 59 - sdk_test/jsons/packages/latching-pkg.json | 66 - sdk_test/jsons/packages/listener-docker.json | 55 - sdk_test/jsons/packages/listener.json | 43 - .../jsons/packages/nginx-multi-component.json | 92 - .../packages/nginx-single-component.json | 57 - .../jsons/packages/no-scoped-targeted.json | 82 - sdk_test/jsons/packages/pv-reader.json | 50 - .../jsons/packages/rosbag-talker-cloud.json | 59 - sdk_test/jsons/packages/scoped-cloud.json | 65 - sdk_test/jsons/packages/scoped-targeted.json | 95 - .../jsons/packages/talker-cloud-device.json | 77 - sdk_test/jsons/packages/talker-cloud.json | 67 - sdk_test/jsons/packages/talker-docker.json | 58 - sdk_test/jsons/packages/talker-noetic.json | 56 - sdk_test/jsons/packages/talker.json | 56 - sdk_test/jsons/packages/throttling-pkg.json | 66 - sdk_test/openshift/sdk-config.sample.yaml | 21 +- sdk_test/package/__init__.py | 0 sdk_test/package/cloud_non_ros_test.py | 81 - .../package/cloud_scoped_targeted_test.py | 114 -- sdk_test/package/cloud_transform_test.py | 99 - sdk_test/package/configuration_tests.py | 77 - sdk_test/package/create_package_test.py | 39 - sdk_test/package/delete_package_test.py | 33 - sdk_test/package/deployment_test.py | 90 - sdk_test/package/get_all_package_test.py | 71 - .../inbound_incoming_scoped_targeted_test.py | 55 - sdk_test/package/native_network_tests.py | 198 -- sdk_test/package/noetic_test.py | 45 - sdk_test/package/package_test.py | 151 -- sdk_test/package/rosbag_test.py | 524 ----- sdk_test/package/routed_networks_tests.py | 158 -- sdk_test/package/static_route_test.py | 100 - .../transformer_with_docker_device_test.py | 86 - sdk_test/package/volume_test.py | 89 - sdk_test/run_rio_sdk_test.py | 4 - sdk_test/util.py | 386 ---- sdk_test/v2_client.py | 38 + setup.py | 9 +- tests/build_test.py | 867 -------- tests/client_get_package_test.py | 221 --- tests/deployment_test.py | 232 --- tests/native_network_test.py | 536 ----- tests/package_test.py | 1741 ----------------- tests/persistent_volume_test.py | 147 -- tests/project_test.py | 345 ---- tests/routed_network_test.py | 420 ---- tests/secret_test.py | 352 ---- tests/user_test.py | 1 - 101 files changed, 171 insertions(+), 14218 deletions(-) delete mode 100644 docs/source/build.rst delete mode 100644 docs/source/deployment.rst delete mode 100644 docs/source/native_network.rst delete mode 100644 docs/source/package.rst delete mode 100644 docs/source/project.rst delete mode 100644 docs/source/routed_network.rst delete mode 100644 docs/source/secret.rst delete mode 100644 docs/source/static_route.rst delete mode 100644 docs/source/volumes.rst delete mode 100644 rapyuta_io/clients/build.py delete mode 100644 rapyuta_io/clients/buildoperation.py delete mode 100644 rapyuta_io/clients/common_models.py delete mode 100644 rapyuta_io/clients/deployment.py delete mode 100644 rapyuta_io/clients/native_network.py delete mode 100644 rapyuta_io/clients/package.py delete mode 100644 rapyuta_io/clients/persistent_volumes.py delete mode 100644 rapyuta_io/clients/plan.py delete mode 100644 rapyuta_io/clients/provision_client.py delete mode 100644 rapyuta_io/clients/routed_network.py delete mode 100644 rapyuta_io/clients/secret.py delete mode 100644 rapyuta_io/clients/static_route.py delete mode 100644 rapyuta_io/clients/validation_schema.py delete mode 100644 sdk_test/coreapi/project_test.py delete mode 100644 sdk_test/coreapi/query_metrics_test.py delete mode 100644 sdk_test/coreapi/secret_test.py delete mode 100644 sdk_test/device/deployment_test.py delete mode 100644 sdk_test/device/topic_test.py delete mode 100644 sdk_test/jsons/builds/listener.json delete mode 100644 sdk_test/jsons/builds/pingpong.json delete mode 100644 sdk_test/jsons/builds/talker-noetic.json delete mode 100644 sdk_test/jsons/builds/talker.json delete mode 100644 sdk_test/jsons/builds/throttle-latch-build.json delete mode 100644 sdk_test/jsons/packages/cloud-non-ros.json delete mode 100644 sdk_test/jsons/packages/cloud-transform.json delete mode 100644 sdk_test/jsons/packages/delete-package-using-client.json delete mode 100644 sdk_test/jsons/packages/delete-package.json delete mode 100644 sdk_test/jsons/packages/device-volume.json delete mode 100644 sdk_test/jsons/packages/fast-talker-device-docker-with-rosbags.json delete mode 100644 sdk_test/jsons/packages/inbound-incoming-scoped-targeted.json delete mode 100644 sdk_test/jsons/packages/latching-pkg.json delete mode 100644 sdk_test/jsons/packages/listener-docker.json delete mode 100644 sdk_test/jsons/packages/listener.json delete mode 100644 sdk_test/jsons/packages/nginx-multi-component.json delete mode 100644 sdk_test/jsons/packages/nginx-single-component.json delete mode 100644 sdk_test/jsons/packages/no-scoped-targeted.json delete mode 100644 sdk_test/jsons/packages/pv-reader.json delete mode 100644 sdk_test/jsons/packages/rosbag-talker-cloud.json delete mode 100644 sdk_test/jsons/packages/scoped-cloud.json delete mode 100644 sdk_test/jsons/packages/scoped-targeted.json delete mode 100644 sdk_test/jsons/packages/talker-cloud-device.json delete mode 100644 sdk_test/jsons/packages/talker-cloud.json delete mode 100644 sdk_test/jsons/packages/talker-docker.json delete mode 100644 sdk_test/jsons/packages/talker-noetic.json delete mode 100644 sdk_test/jsons/packages/talker.json delete mode 100644 sdk_test/jsons/packages/throttling-pkg.json delete mode 100644 sdk_test/package/__init__.py delete mode 100644 sdk_test/package/cloud_non_ros_test.py delete mode 100644 sdk_test/package/cloud_scoped_targeted_test.py delete mode 100644 sdk_test/package/cloud_transform_test.py delete mode 100644 sdk_test/package/configuration_tests.py delete mode 100644 sdk_test/package/create_package_test.py delete mode 100644 sdk_test/package/delete_package_test.py delete mode 100644 sdk_test/package/deployment_test.py delete mode 100644 sdk_test/package/get_all_package_test.py delete mode 100644 sdk_test/package/inbound_incoming_scoped_targeted_test.py delete mode 100644 sdk_test/package/native_network_tests.py delete mode 100644 sdk_test/package/noetic_test.py delete mode 100644 sdk_test/package/package_test.py delete mode 100644 sdk_test/package/rosbag_test.py delete mode 100644 sdk_test/package/routed_networks_tests.py delete mode 100644 sdk_test/package/static_route_test.py delete mode 100644 sdk_test/package/transformer_with_docker_device_test.py delete mode 100644 sdk_test/package/volume_test.py create mode 100644 sdk_test/v2_client.py delete mode 100644 tests/build_test.py delete mode 100644 tests/client_get_package_test.py delete mode 100644 tests/deployment_test.py delete mode 100644 tests/native_network_test.py delete mode 100644 tests/package_test.py delete mode 100644 tests/persistent_volume_test.py delete mode 100644 tests/project_test.py delete mode 100644 tests/routed_network_test.py delete mode 100644 tests/secret_test.py diff --git a/README.md b/README.md index 0fc403fe..fc61f44f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,14 @@ command. python setup.py install ``` +## Development + +Create a python virtual environment, having version less than 3.11 + +```bash +pipenv install --dev +``` + ## Getting Started Before using the SDK, you need the Rapyuta Token. You can get it from @@ -40,17 +48,14 @@ from rapyuta_io import Project project = client.create_project(Project("python-sdk")) client.set_project(project.guid) - -# Create a Build -from rapyuta_io import Build, StrategyType, DeviceArch - -client.create_build( - Build( - "dummy", - StrategyType.DOCKER, - "https://github.com/ankitrgadiya/dummy-package", - DeviceArch.AMD64, - ) -) ``` +## SDK Test + +`RIO_CONFIG` environment variable pointing to the config.json must be sourced to +run the sdk integration test. The sample config is present in `sdk_test` directory. +Run `run_rio_sdk_test.py` to start the sdk tests. + +Currently only one docker compose device is needed to be created and added to the config, +SDK Test will add the device to the newly created project and onboard it and run tests. + diff --git a/docs/source/build.rst b/docs/source/build.rst deleted file mode 100644 index fe88c6ab..00000000 --- a/docs/source/build.rst +++ /dev/null @@ -1,35 +0,0 @@ -Build modules -====================================== - - -Builds on rapyuta.io are a fundamental resource which convert your source code residing in your VCS into a container image. - -Builds can be referenced when creating packages and enables an end-to-end “Code to Deployment” pipeline for your Robotics solution. - - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, list_builds, create_build, get_build, delete_build, trigger_build, rollback_build - - -Build ------------------ - -.. automodule:: rapyuta_io.clients.build - :members: - :exclude-members: is_ready, poll_till_ready - -Operations on Builds ---------------------- - -.. automodule:: rapyuta_io.clients.buildoperation - :members: \ No newline at end of file diff --git a/docs/source/deployment.rst b/docs/source/deployment.rst deleted file mode 100644 index 8392f0b0..00000000 --- a/docs/source/deployment.rst +++ /dev/null @@ -1,29 +0,0 @@ -Deployment module -====================================== - - -A deployment is a rapyuta.io resource that represents a unique instantiation of a rapyuta.io package. It holds information about the package deployed, the configuration used, and interfaces exposed. It possesses a unique identifier and provides a mechanism to introspect its phase and state that are needed to ascertain the state of a system. - -Tooling such as logs, debug terminals and other automation leverage this uniquely identifiable resource to allow the operator to manage, debug and observe a particular running instance of their application. - -Deployments support composition patterns to allow the user to combine multiple different applications to help realize a potentially more complex robotics solution. - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, get_deployment, get_all_deployments, update_deployment - - - -Deployments Module ------------------------- - -.. automodule:: rapyuta_io.clients.deployment - :members: \ No newline at end of file diff --git a/docs/source/native_network.rst b/docs/source/native_network.rst deleted file mode 100644 index 6f7b9d18..00000000 --- a/docs/source/native_network.rst +++ /dev/null @@ -1,31 +0,0 @@ -Native Network module -====================================== - - -rapyuta.io provides a way to establish robot to robot communication without using brokered solution in a local network. The main purpose of native network is to give the same communication mechanism as present in a single shared ROS master. - - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, list_native_networks, get_native_network, create_native_network, delete_native_network - - - -Native Network Module ------------------------- - -.. automodule:: rapyuta_io.clients.native_network - :members: - :exclude-members: get_deserialize_map,get_serialize_map - -.. autoclass:: rapyuta_io.clients.common_models.InternalDeploymentStatus - :members: - :exclude-members: get_deserialize_map,get_serialize_map diff --git a/docs/source/package.rst b/docs/source/package.rst deleted file mode 100644 index 065ae097..00000000 --- a/docs/source/package.rst +++ /dev/null @@ -1,34 +0,0 @@ -Package module -====================================== - - -A package is a fundamental rapyuta.io resource that represents a declaration of your application. It is the smallest unit of deployment in rapyuta.io, and it can be deployed either on a device or the cloud or both. - -To make this possible a package must encapsulate any information about how it should be built, its compatibility and runtime requirements, network endpoints and ROS interfaces it exposes, and any configuration information it may require. - - - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, get_package, get_all_packages, - create_package, create_package_from_manifest, delete_package - - - -Package Module ------------------------- - -.. automodule:: rapyuta_io.clients.package - :members: - -.. autoclass:: rapyuta_io.clients.plan.Plan - :members: - :exclude-members: get_component_id, get_component_by_name, needs_aliases, validate diff --git a/docs/source/project.rst b/docs/source/project.rst deleted file mode 100644 index 9c06a614..00000000 --- a/docs/source/project.rst +++ /dev/null @@ -1,38 +0,0 @@ -Project module -====================================== - - -Any rapyuta.io resource that you create, or allocate and use must belong to a project. You can think of a project as the organizational unit for what you are building. A project is made up of the settings, configuration, and other metadata that describe your applications. Resources within a single project can work together easily, for example, by communicating through an internal network. The resources that each project contains remain separate across project boundaries; you can only interconnect them through an external connection. - -Each project has: - -- Auto-generated unique project ID -- A project name, which you provide. -- miscellaneous metadata: e.g., creator name, timestamp, etc. -- You may create multiple projects and use them to organize rapyuta.io resources. - - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -.. autosummary:: - :toctree: _autosummary - :template: custom-module-template.rst - :recursive: - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, list_projects, create_project,get_project, delete_project, set_project, add_user_to_project, remove_user_from_project - -Project Module ------------------ - -.. automodule:: rapyuta_io.clients.project - :members: - :exclude-members: get_deserialize_map,get_serialize_map - diff --git a/docs/source/routed_network.rst b/docs/source/routed_network.rst deleted file mode 100644 index 11a0866c..00000000 --- a/docs/source/routed_network.rst +++ /dev/null @@ -1,33 +0,0 @@ -Routed Network module -====================================== - - -rapyuta.io implements various features for automatically linking different deployments, and hence, aid software composition. It implements a dedicated communication plane for ROS. - -Routed network is a rapyuta.io resource to enable ROS communication between different ROS package deployments. -Binding a routed network resource to your deployment will enable other deployments on the same routed network to consume ROS topics/services/actions as defined in the package. -Data flow occurs only when another package chooses to subscribe to a topic, call a service or call an action. - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, get_all_routed_networks, get_routed_network, create_cloud_routed_network, create_device_routed_network, delete_routed_network - - - -Routed Network Module ------------------------- - -.. automodule:: rapyuta_io.clients.routed_network - :members: - -.. autoclass:: rapyuta_io.clients.common_models.InternalDeploymentStatus - :members: - :exclude-members: get_deserialize_map,get_serialize_map \ No newline at end of file diff --git a/docs/source/secret.rst b/docs/source/secret.rst deleted file mode 100644 index af2c0a02..00000000 --- a/docs/source/secret.rst +++ /dev/null @@ -1,23 +0,0 @@ -Secret module -====================================== - -A secret is an object containing sensitive information or confidential data such as a password, SSH private key, SSL certificate. -It grants rapyuta.io access to private git repositories and private docker images so that the platform can build source code in private git repositories or use private docker images. - - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, create_secret,list_secrets,get_secret,delete_secret - - -Secret Module ------------------ -.. automodule:: rapyuta_io.clients.secret - :members: - :exclude-members: get_deserialize_map,get_serialize_map diff --git a/docs/source/static_route.rst b/docs/source/static_route.rst deleted file mode 100644 index 954bcbb8..00000000 --- a/docs/source/static_route.rst +++ /dev/null @@ -1,26 +0,0 @@ -Static Route module -====================================== - - -rapyuta.io enables you to create a static route URL and give it a globally unique FQDN. When you add a static route, an externally exposed endpoint is essentially guaranteed to be available at the URL of that particular static route. It makes externally exposed endpoints (and hence the deployments exposing them) resilient to failure or re-deployment, facilitates maintenance and upgrades to the backend/deployment while retaining the same unique globally available URL. - -Static routes are used frequently to get a deterministic URL/route for your application while exposing the network endpoint externally - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, get_all_static_routes, get_static_route, get_static_route_by_name, create_static_route, delete_static_route - - - -Static Route Module -------------------- - -.. automodule:: rapyuta_io.clients.static_route - :members: \ No newline at end of file diff --git a/docs/source/volumes.rst b/docs/source/volumes.rst deleted file mode 100644 index bb6be13e..00000000 --- a/docs/source/volumes.rst +++ /dev/null @@ -1,26 +0,0 @@ -Persistent Volumes -====================================== - -Applications running on the cloud de-allocate any resources consumed when they stop, scale down, or fail. This implies that the working storage associated with them is ephemeral. To get around this problem rapyuta.io provides a mechanism to consume persistent block storage for your applications running in the cloud. This storage can be associated with at most one running deployment at any given point of time. A user is typically required to manage the lifecycle of the application code independently from the associated storage. - -The Rapyuta IO Persistent Volume is a storage package. A storage package is a public package which is available to all users out of the box. You cannot delete or modify storage packages, and they are available to every user. - -.. toctree:: - :maxdepth: 3 - :caption: Contents: - - -Client Module ------------------ - -.. autoclass:: rapyuta_io.rio_client.Client - :members: __init__, get_persistent_volume, get_volume_instance - - - - -Persistent Volumes Module ---------------------------- - -.. automodule:: rapyuta_io.clients.persistent_volumes - :members: \ No newline at end of file diff --git a/rapyuta_io/__init__.py b/rapyuta_io/__init__.py index 9c2039e5..4a187c6a 100644 --- a/rapyuta_io/__init__.py +++ b/rapyuta_io/__init__.py @@ -1,17 +1,9 @@ from __future__ import absolute_import -from .clients.deployment import DeploymentPhaseConstants, DeploymentStatusConstants -from .clients.device import TopicKind, DeviceStatus, TopicQOS, QoS +from .clients.device import TopicKind, DeviceStatus, TopicQOS, QoS, DeploymentPhaseConstants, ROSDistro from .clients.model import Label, Command, DeviceConfig, TopicsStatus -from .clients.persistent_volumes import DiskType from rapyuta_io.utils import error from .rio_client import Client -from .clients.package import ROSDistro from .clients.device_manager import DeviceArch -from .clients.build import Build, BuildStatus, StrategyType, SimulationOptions, CatkinOption, \ - BuildOptions -from .clients.buildoperation import BuildOperation, BuildOperationInfo -from .clients.project import Project -from .clients.secret import Secret, SecretConfigDocker from .clients.rosbag import UploadOptions from .clients.user_group import UserGroup diff --git a/rapyuta_io/clients/__init__.py b/rapyuta_io/clients/__init__.py index b55616f1..1e711ded 100644 --- a/rapyuta_io/clients/__init__.py +++ b/rapyuta_io/clients/__init__.py @@ -1,9 +1,5 @@ from .device_manager import DeviceManagerClient, DeviceArch from .model import * -from .provision_client import ProvisionClient from .paramserver import _ParamserverClient -from .package import ROSDistro -from .build import Build, BuildStatus, StrategyType, SimulationOptions, CatkinOption, BuildOptions -from .buildoperation import BuildOperation, BuildOperationInfo -from .device import Device +from .device import Device, ROSDistro from .user_group import UserGroup diff --git a/rapyuta_io/clients/build.py b/rapyuta_io/clients/build.py deleted file mode 100644 index 2dd7009a..00000000 --- a/rapyuta_io/clients/build.py +++ /dev/null @@ -1,603 +0,0 @@ -from __future__ import absolute_import -import enum -import time - -from rapyuta_io.utils.error import InvalidParameterException -from rapyuta_io.utils import ObjDict, to_objdict -from rapyuta_io.clients.package import ROSDistro -from rapyuta_io.clients.device_manager import DeviceArch -from rapyuta_io.utils.utils import create_auth_header, get_api_response_data -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils import RestClient, RetriesExhausted -from rapyuta_io.utils.error import BuildFailed -from rapyuta_io.clients.buildoperation import BuildOperation, BuildOperationInfo -from rapyuta_io.utils.error import BuildOperationFailed -from rapyuta_io.utils.partials import PartialMixin -from rapyuta_io.utils.pollers import RefreshPollerMixin -import six -from six.moves import range - - -class SimulationOptions(ObjDict): - """ - Simulation Options represents whether simulation is required at the time of building package - - :ivar simulation: whether simulation is required (bool). - """ - def __init__(self, simulation): - self.validate(simulation) - self.simulation = simulation - super(ObjDict, self).__init__() - - @staticmethod - def validate(simulation): - if not isinstance(simulation, bool): - raise InvalidParameterException('simulation must be a boolean') - - -class BuildOptions(ObjDict): - """ - BuildOptions represent Build Options. - - :ivar catkinOptions: represents list of catkin options :py:class:`~rapyuta_io.clients.build.CatkinOption`. - - """ - def __init__(self, catkinOptions): - self.validate(catkinOptions) - self.catkinOptions = catkinOptions - super(ObjDict, self).__init__() - - @staticmethod - def validate(catkinOptions): - if not isinstance(catkinOptions, list): - raise InvalidParameterException('catkinOptions must be an instance of list') - for opt in catkinOptions: - if not isinstance(opt, CatkinOption): - raise InvalidParameterException('Every catkinOption must be an instance of ' - 'rapyuta_io.clients.build.CatkinOption') - - -class CatkinOption(ObjDict): - """ - CatkinOption represents Catkin Options - - :ivar rosPkgs: Represents ROS packages to be included for build. - :ivar cmakeArgs: Represents cmakeArgs to be used in the build. - :ivar makeArgs: Represents makeArgs to be used in the build. - :ivar blacklist: Used if you want to avoid build certain packages in your build. - :ivar catkinMakeArgs: Represents catkinMakeArgs to be used in the build. - - """ - def __init__(self, rosPkgs=None, cmakeArgs=None, makeArgs=None, blacklist=None, catkinMakeArgs=None): - self.validate(rosPkgs, cmakeArgs, makeArgs, blacklist, catkinMakeArgs) - self.rosPkgs = rosPkgs - self.cmakeArgs = cmakeArgs - self.makeArgs = makeArgs - self.blacklist = blacklist - self.catkinMakeArgs = catkinMakeArgs - super(ObjDict, self).__init__() - - @staticmethod - def validate(rosPkgs, cmakeArgs, makeArgs, blacklist, catkinMakeArgs): - if rosPkgs and not isinstance(rosPkgs, six.string_types): - raise InvalidParameterException('rosPkgs must be of string type') - - if cmakeArgs and not isinstance(cmakeArgs, six.string_types): - raise InvalidParameterException('cmakeArgs must be of string type') - - if makeArgs and not isinstance(makeArgs, six.string_types): - raise InvalidParameterException('makeArgs must be of string type') - - if blacklist and not isinstance(blacklist, six.string_types): - raise InvalidParameterException('blacklist must be of string type') - - if catkinMakeArgs and not isinstance(catkinMakeArgs, six.string_types): - raise InvalidParameterException('catkinMakeArgs must be of string type') - - -class GithubWebhook(ObjDict): - """ - Github Webhook to be triggered on build completion - - :ivar workflowName: Represents name of the github dispatch workflow file. - :ivar accessToken: Represents github access token - - """ - WebhookType = "githubWorkflow" - - def __init__(self, workflowName, accessToken): - self.validate(workflowName, accessToken) - self.webhookType = GithubWebhook.WebhookType - self.workflowName = workflowName - self.accessToken = accessToken - - @staticmethod - def validate(workflowName, accessToken): - - if not workflowName or not isinstance(workflowName, six.string_types): - raise InvalidParameterException('workflowName must be present and should be of string type') - - if not accessToken or not isinstance(accessToken, six.string_types): - raise InvalidParameterException('accessToken must be present and should be of string type') - - -class Build(PartialMixin, RefreshPollerMixin, ObjDict): - - """ - Build represents the main class whose instance will be used to perform various operations like create, get, update, - delete, trigger and rollback - - :param buildName: name of build - :type buildName: str - :param strategyType: Strategy type used for the build - :type strategyType: Union[:py:class:`~rapyuta_io.clients.build.StrategyType`, str] - :param repository: Git repository to be used for building docker image while deploying package. - :type repository: str - :param architecture: Architecture required for using the build - :type architecture: Union[:py:class:`~rapyuta_io.clients.device_manager.DeviceArch`, str] - :param rosDistro: ROS distro used by the build - :type rosDistro: Union[:py:class:`~rapyuta_io.clients.package.ROSDistro`, str] - :param isRos: Whether the build support ros components - :type isRos: bool - :param contextDir: context dir to be used in the build - :type contextDir: str - :param dockerFilePath: Represents Docker file path - :type dockerFilePath: str - :param secret: Represents secret for a private git repository - :type secret: str - :param dockerPullSecret: GUID of the docker secret for a private base image in Dockerfile - :type dockerPullSecret: str - :param dockerPushSecret: GUID of the docker secret for pushing the image to an external registry. - :type dockerPushSecret: str - :param dockerPushRepository: An external docker repository where Build will push the image. For example 'docker.io/user/example' - :type dockerPushRepository: str - :param simulationOptions: Represents simulation options used by the build - :type simulationOptions: :py:class:`~rapyuta_io.clients.build.SimulationOptions` - :param buildOptions: Represents build options used by the build - :type buildOptions: :py:class:`~rapyuta_io.clients.build.BuildOptions` - :param branch: Represents branch corresponding to the repository used by the build - :type branch: str - :param triggerName: Represents trigger name of the build - :type triggerName: str - :param tagName: Represents tag name of the build - :type tagName: str - :param buildWebhooks: Represents webhooks to be triggered on build completion - :type buildWebhooks: list(:py:class:`~rapyuta_io.clients.build.GithubWebhook`) - """ - def __init__(self, buildName, strategyType, repository, architecture, rosDistro='', - isRos=False, contextDir='', dockerFilePath='', secret='', - dockerPullSecret='', dockerPushSecret='', dockerPushRepository='', - simulationOptions=None, buildOptions=None, branch='', triggerName='', tagName='', buildWebhooks=None): - - self.validate(buildName, strategyType, repository, architecture, rosDistro, isRos, - contextDir, dockerFilePath, secret, dockerPullSecret, dockerPushSecret, - dockerPushRepository, simulationOptions, buildOptions, branch, triggerName, tagName, buildWebhooks) - - if not strategyType or strategyType not in list(StrategyType.__members__.values()): - raise InvalidParameterException('StrategyType must be one of rapyuta_io.clients.package.StrategyType') - - if not architecture or architecture not in list(DeviceArch.__members__.values()): - raise InvalidParameterException('architecture must be one of rapyuta_io.clients.device_manager.DeviceArch') - - if rosDistro != '' and rosDistro not in list(ROSDistro.__members__.values()): - raise InvalidParameterException('rosDistro must be one of rapyuta_io.clients.package.ROSDistro') - - self.buildName = buildName - self.secret = secret - self.dockerPullSecret = dockerPullSecret - self.dockerPushSecret = dockerPushSecret - self.dockerPushRepository = dockerPushRepository - self.triggerName = triggerName - self.tagName = tagName - self.buildInfo = ObjDict( - repository=repository, - branch=branch, - strategyType=strategyType, - dockerFilePath=dockerFilePath, - contextDir=contextDir, - architecture=architecture, - isRos=isRos, - rosDistro=rosDistro - ) - self.buildInfo.simulationOptions = simulationOptions - self.buildInfo.buildOptions = buildOptions - self.buildWebhooks = buildWebhooks - - super(ObjDict, self).__init__() - - @staticmethod - def validate(buildName, strategyType, repository, architecture, rosDistro, isRos, - contextDir, dockerFilePath, secret, dockerPullSecret, dockerPushSecret, - dockerPushRepository, simulationOptions, buildOptions, branch, triggerName, tagName, buildWebhooks): - - if not buildName or not isinstance(buildName, six.string_types): - raise InvalidParameterException('buildName must be a non-empty string') - - if not strategyType or strategyType not in list(StrategyType.__members__.values()): - raise InvalidParameterException('StrategyType must be one of rapyuta_io.clients.package.StrategyType') - - if not repository or not isinstance(repository, six.string_types): - raise InvalidParameterException('repository must be a valid non-empty string') - - if branch != '' and not isinstance(branch, six.string_types): - raise InvalidParameterException('branch must be a valid non-empty string') - - if not architecture or architecture not in list(DeviceArch.__members__.values()): - raise InvalidParameterException('Architecture must be one of rapyuta_io.clients.device_manager.DeviceArch') - - if not isinstance(isRos, bool): - raise InvalidParameterException('isRos must be of bool type') - - if not isRos and rosDistro != '': - raise InvalidParameterException('rosDistro must not be set if isRos is False') - elif isRos and rosDistro not in list(ROSDistro.__members__.values()): - raise InvalidParameterException('rosDistro must be one of rapyuta_io.clients.package.ROSDistro') - - if not isRos and (buildOptions or simulationOptions): - raise InvalidParameterException('isRos must be true for passing simulationOptions or buildOptions') - - if simulationOptions is not None and not isinstance(simulationOptions, SimulationOptions): - raise InvalidParameterException('simulationOptions must be of rapyuta_io.clients.build.SimulationOptions') - - if buildOptions is not None and not isinstance(buildOptions, BuildOptions): - raise InvalidParameterException('buildOptions must be of rapyuta_io.clients.build.BuildOptions') - - if not isinstance(contextDir, six.string_types): - raise InvalidParameterException('contextDir must be a string') - - if strategyType == StrategyType.SOURCE and dockerFilePath: - raise InvalidParameterException('cannot use dockerFilePath for source strategyType') - - if not isinstance(dockerFilePath, six.string_types): - raise InvalidParameterException('dockerFilePath must be a non-empty string') - - if not isinstance(secret, six.string_types): - raise InvalidParameterException('secret must be a string') - - if not isinstance(dockerPullSecret, six.string_types): - raise InvalidParameterException('dockerPullSecret must be a string') - - if not isinstance(dockerPushSecret, six.string_types): - raise InvalidParameterException('dockerPushSecret must be a string') - - if not isinstance(dockerPushRepository, six.string_types): - raise InvalidParameterException('dockerPushRepository must be a string') - - if (dockerPushRepository == '' and dockerPushSecret != '') or (dockerPushRepository != '' and dockerPushSecret == ''): - raise InvalidParameterException('both dockerPushRepository and dockerPushSecret must be present') - - if not isinstance(triggerName, six.string_types): - raise InvalidParameterException('triggerName must be a non-empty string') - - if not isinstance(tagName, six.string_types): - raise InvalidParameterException('tagName must be a non-empty string') - - if dockerPushSecret == '' and tagName != '': - raise InvalidParameterException('cannot use tagName without dockerPushSecret') - - if buildWebhooks is not None and (not isinstance(buildWebhooks, list) or not all(isinstance(webhook, GithubWebhook) for webhook in buildWebhooks)): - raise InvalidParameterException('buildWebhooks must be a list of rapyuta_io.clients.build.GithubWebhook') - - - @classmethod - def _deserialize(cls, data, obj=None): - if obj: # refresh existing object - for key, value in six.iteritems(data): - if key != 'buildInfo': # preserve class objects like enums & options - setattr(obj, key, value) - return obj - - obj = cls.__new__(cls) - for key, value in six.iteritems(data): - setattr(obj, key, value) - obj.buildInfo['strategyType'] = StrategyType(obj.buildInfo['strategyType']) - obj.buildInfo['architecture'] = DeviceArch(obj.buildInfo['architecture']) - if obj.buildInfo.get('rosDistro'): - obj.buildInfo['rosDistro'] = ROSDistro(obj.buildInfo.get('rosDistro')) - obj.buildInfo = to_objdict(obj.buildInfo) # Not allowing buildOptions, simulationOptions to get recursively - # converted into objdict - buildOptions = getattr(obj.buildInfo, 'buildOptions', None) - buildOptsObj = None - if buildOptions: - buildOptsObj = BuildOptions.__new__(BuildOptions) - catkinOptObjs = [] - for opt in buildOptions.get('catkinOptions', []): - catkinOptObj = CatkinOption.__new__(CatkinOption) - catkinOptObj.update(**opt) - catkinOptObjs.append(catkinOptObj) - buildOptsObj.catkinOptions = catkinOptObjs - obj.buildInfo.buildOptions = buildOptsObj - simulationOptions = getattr(obj.buildInfo, 'simulationOptions', None) - simOptsObj =None - if simulationOptions: - simOptsObj = SimulationOptions.__new__(SimulationOptions) - simOptsObj.update(**simulationOptions) - obj.buildInfo.simulationOptions = simOptsObj - # populating default values in case of missing fields - fields_inside_buildinfo = ['repository', 'branch', 'dockerFilePath', 'contextDir', 'rosDistro'] - fields_outside_buildinfo = ['secret', 'dockerPullSecret', 'dockerPushSecret', 'dockerPushRepository'] - for key in fields_inside_buildinfo: - setattr(obj.buildInfo, key, getattr(obj.buildInfo, key, '')) - for key in fields_outside_buildinfo: - setattr(obj, key, getattr(obj, key, '')) - return obj - - def _serialize(self): - build = { - 'buildName': self.buildName, - 'strategyType': self.buildInfo.strategyType, - 'repository': self.buildInfo.repository, - 'architecture': self.buildInfo.architecture, - 'isRos': self.buildInfo.isRos, - } - if self.secret != '': - build['secret'] = self.secret - - if self.dockerPullSecret != '': - build['dockerPullSecrets'] = [self.dockerPullSecret] - - if self.dockerPushSecret != '': - build['dockerPushSecret'] = self.dockerPushSecret - - if self.dockerPushRepository != '': - build['dockerPushRepository'] = self.dockerPushRepository - - if hasattr(self, 'triggerName') and self.triggerName != '': - build['triggerName'] = self.triggerName - - if hasattr(self, 'tagName') and self.tagName != '': - build['tagName'] = self.tagName - - if self.buildInfo.get('rosDistro'): - build['rosDistro'] = self.buildInfo.get('rosDistro') - - if self.buildInfo.get('simulationOptions'): - build['simulationOptions'] = self.buildInfo.get('simulationOptions') - - if self.buildInfo.get('buildOptions'): - build['buildOptions'] = self.buildInfo.get('buildOptions') - - if self.buildInfo.get('dockerFilePath'): - build['dockerFilePath'] = self.buildInfo.get('dockerFilePath') - - if self.buildInfo.get('contextDir'): - build['contextDir'] = self.buildInfo.get('contextDir') - - if self.buildInfo.get('branch'): - build['branch'] = self.buildInfo.get('branch') - - if hasattr(self, 'buildWebhooks') and self.buildWebhooks is not None: - build['buildWebhooks'] = self.buildWebhooks - - return build - - def refresh(self): - - """ - Fetches the updated resource from the server, and adds/updates object attributes based on it. - - :raises: :py:class:`~utils.error.APIError`: If the api returns an error, the status code is - anything other than 200/201 - """ - url = self._host + '/build/{}'.format(self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - response_data = get_api_response_data(response, parse_full=True) - self._deserialize(response_data, obj=self) - self.is_partial = False - - def save(self): - - """ - Save the build after updating attributes - - Following are the attributes that can be updated - - * build.buildInfo.repository - * build.buildInfo.branch - * build.buildInfo.dockerFilePath - * build.buildInfo.contextDir - * build.buildInfo.buildOptions - * build.secret - * build.dockerPullSecret - * build.dockerPushRepository - * build.buildWebhooks - - Following example demonstrates how to save a build: - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.get_build('build-guid') - >>> build.buildInfo.branch = 'master' - >>> build.save() - """ - if not hasattr(self, 'buildWebhooks'): - self.buildWebhooks = None - self.validate(self.buildName, - self.buildInfo.strategyType, - self.buildInfo.repository, - self.buildInfo.architecture, - self.buildInfo.rosDistro, - self.buildInfo.isRos, - self.buildInfo.contextDir, - self.buildInfo.dockerFilePath, - self.secret, - self.dockerPullSecret, - self.dockerPushSecret, - self.dockerPushRepository, - self.buildInfo.simulationOptions, - self.buildInfo.buildOptions, - self.buildInfo.branch, - '', - '', - self.buildWebhooks - ) - url = self._host + '/build/{}'.format(self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.PUT).headers(headers).execute(payload=self._serialize()) - get_api_response_data(response, parse_full=True) - - def delete(self): - - """ - Delete the build using the build object. - :raises: :py:class:`ForbiddenError`: Returned in case of status code 403 - - Following example demonstrates how to delete a build using build object: - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import ForbiddenError - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.get_build('build-guid') - >>> try: - ... build.delete() - ... except ForbiddenError as e: - ... print e - - """ - url = self._host + '/build/{}'.format(self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute() - get_api_response_data(response, parse_full=True) - - def trigger(self, triggerName=None, tagName=None): - - """ - Trigger a new build request for a build using build object. - - :raises: :py:class:`BuildOperationFailed`: Returned in case trigger build fails - - Following example demonstrates how to trigger a new build request using build object: - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import BuildOperationFailed - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.get_build('build-guid') - >>> try: - ... build.trigger() - ... except BuildOperationFailed as e: - ... print e - - """ - url = self._host + '/build/operation/trigger' - headers = create_auth_header(self._auth_token, self._project) - build_operation_info = [BuildOperationInfo(self.guid, triggerName=triggerName, tagName=tagName)] - request = BuildOperation(buildOperationInfo=build_operation_info) - trigger_response = RestClient(url).method(HttpMethod.PUT).headers(headers).execute(payload=request) - response_data = get_api_response_data(trigger_response, parse_full=True) - if not response_data['buildOperationResponse'][0]['success']: - raise BuildOperationFailed(response_data['buildOperationResponse'][0]['error']) - self['buildGeneration'] = response_data['buildOperationResponse'][0]['buildGenerationNumber'] - - def rollback(self, buildGenerationNumber): - - """ - Rollback a build using build object. - - :param buildGenerationNumber: build generation number used for rollback. - :type buildGenerationNumber: int - :raises: :py:class:`BuildOperationFailed`: Returned in case rollback build fails. - :raises: :py:class:`InvalidParameterException` - - Following example demonstrates how to rollback a build using build object: - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import BuildOperationFailed - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.get_build('build-guid') - >>> try: - ... build.rollback(1) - ... except BuildOperationFailed as e: - ... print e - - """ - if not isinstance(buildGenerationNumber, int) or buildGenerationNumber <= 0: - msg = 'build generation number must be an integer and greater than 0' - raise InvalidParameterException(msg) - url = self._host + '/build/operation/rollback' - headers = create_auth_header(self._auth_token, self._project) - build_operation_info = [BuildOperationInfo(self.guid, buildGenerationNumber)] - request = BuildOperation(buildOperationInfo=build_operation_info) - rollback_response = RestClient(url).method(HttpMethod.PUT).headers(headers).execute(payload=request) - response_data = get_api_response_data(rollback_response, parse_full=True) - if not response_data['buildOperationResponse'][0]['success']: - raise BuildOperationFailed(response_data['buildOperationResponse'][0]['error']) - self['buildGeneration'] = buildGenerationNumber - - def poll_build_till_ready(self, retry_count=120, sleep_interval=5): - """ - Polls the build till its status changes from BuildInProgress to Complete/BuildFailed. - - :param retry_count: Number of retries. - :type retry_count: int - :param sleep_interval: Sleep seconds between retries. - :type sleep_interval: int - :raises: :py:class:`BuildFailed`: If status becomes BuildFailed. - :raises: :py:class:`RetriesExhausted`: If the number of polling retries exhausted before the object was ready. - - Following example demonstrates how to poll a newly created build using build object: - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import BuildFailed, RetriesExhausted - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = Build('test-build', 'Source', 'repository', 'amd64', 'melodic', isRos=True) - >>> build = client.create_build(build) - >>> try: - ... build.poll_build_till_ready() - ... except BuildFailed: - ... print 'Build Failed' - ... except RetriesExhausted as e: - ... print e, 'Retry again ?' - - """ - self.poll_till_ready(retry_count, sleep_interval) - - def is_ready(self): - if self.status == BuildStatus.BUILD_FAILED: - raise BuildFailed() - return self.status == BuildStatus.COMPLETE - - -class BuildStatus(str, enum.Enum): - """ - Enumeration variables for build status - - Build status can be any of the following types \n - - BuildStatus.BUILD_IN_PROGRESS \n - BuildStatus.COMPLETE \n - BuildStatus.BUILD_FAILED \n - - """ - - def __str__(self): - return str(self.value) - - BUILD_IN_PROGRESS = 'BuildInProgress' - COMPLETE = 'Complete' - BUILD_FAILED = 'BuildFailed' - - @staticmethod - def validate(statuses): - if not isinstance(statuses, list): - raise InvalidParameterException('statuses must be an instance of list') - for status in statuses: - if status not in list(BuildStatus.__members__.values()): - raise InvalidParameterException('status must be of rapyuta_io.clients.build.BuildStatus') - - -class StrategyType(str, enum.Enum): - """ - Enumeration variables for Strategy Types. - - Strategy Type can be any of the following types \n - - StrategyType.SOURCE \n - StrategyType.DOCKER \n - """ - - def __str__(self): - return str(self.value) - - SOURCE = 'Source' - DOCKER = 'Docker' \ No newline at end of file diff --git a/rapyuta_io/clients/buildoperation.py b/rapyuta_io/clients/buildoperation.py deleted file mode 100644 index 3df59e38..00000000 --- a/rapyuta_io/clients/buildoperation.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import absolute_import -from rapyuta_io.utils.error import InvalidParameterException -from rapyuta_io.utils import ObjDict -import six - - -class BuildOperationInfo(ObjDict): - - """ - BuildOperationInfo represents information about the operation which will be performed on the build. - - :ivar buildGuid: Represents GUID of the build - :ivar buildGenerationNumber: Represents build generation number of the build. - :ivar triggerName: Represents trigger name of the build - :ivar tagName: Represents tag name of the build - - """ - def __init__(self, buildGuid, buildGenerationNumber=None, triggerName=None, tagName=None): - self.validate(buildGuid, buildGenerationNumber, triggerName, tagName) - self.buildGUID = buildGuid - if buildGenerationNumber: - self.buildGenerationNumber = buildGenerationNumber - if triggerName: - self.triggerName = triggerName - if tagName: - self.tagName = tagName - super(ObjDict, self).__init__() - - @staticmethod - def validate(buildGuid, buildGenerationNumber, triggerName, tagName): - if not buildGuid or not isinstance(buildGuid, six.string_types): - raise InvalidParameterException('buildGuid must be a non-empty string') - - if buildGenerationNumber and not isinstance(buildGenerationNumber, int): - raise InvalidParameterException('buildGenerationNumber should be a integer') - - if triggerName is not None and not isinstance(triggerName, six.string_types) or triggerName == "": - raise InvalidParameterException('triggerName must be a non-empty string') - - if tagName is not None and not isinstance(tagName, six.string_types) or tagName == "": - raise InvalidParameterException('tagName must be a non-empty string') - - -class BuildOperation(ObjDict): - """ - BuildOperation represents Build Operation - - :ivar buildOperationInfo: represents a list of information about the operation which will be performed on - the build list(:py:class:`~rapyuta_io.clients.buildoperation.BuildOperationInfo`). - - """ - def __init__(self, buildOperationInfo): - self.validate(buildOperationInfo) - self.buildOperationInfo = buildOperationInfo - super(ObjDict, self).__init__() - - @staticmethod - def validate(buildOperationInfo): - if not isinstance(buildOperationInfo, list): - raise InvalidParameterException('buildOperationInfo must be an instance of list') - for buildOp in buildOperationInfo: - if not isinstance(buildOp, BuildOperationInfo): - raise InvalidParameterException('Every buildOperation must be an instance of ' - 'rapyuta_io.clients.buildoperation.BuildOperationInfo') diff --git a/rapyuta_io/clients/catalog_client.py b/rapyuta_io/clients/catalog_client.py index 1a9300db..86daf17f 100644 --- a/rapyuta_io/clients/catalog_client.py +++ b/rapyuta_io/clients/catalog_client.py @@ -1,18 +1,14 @@ # encoding: utf-8 from __future__ import absolute_import + import os import re -import six from rapyuta_io.clients.api_client import CatalogConfig -from rapyuta_io.clients.package import Package -from rapyuta_io.clients.persistent_volumes import PersistentVolumes -from rapyuta_io.utils import RestClient, PackageNotFound, to_objdict, APIError +from rapyuta_io.utils import RestClient, PackageNotFound from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.settings import CATALOG_API_PATH, VOLUME_PACKAGE_ID +from rapyuta_io.utils.settings import CATALOG_API_PATH from rapyuta_io.utils.utils import response_validator -from six.moves import map -from rapyuta_io.utils.partials import PartialMixin class CatalogClient(CatalogConfig): @@ -34,117 +30,6 @@ def _get_service(self, package_id, retry_limit=0): url = self._get_api_path() + "?package_uid=%s" % package_id return self._execute(url, HttpMethod.GET, retry_limit) - @response_validator(True) - def get_all_packages(self, retry_limit): - url = self._catalog_api_host + '/v2/catalog' - return self._execute(url, HttpMethod.GET, retry_limit) - - def get_package(self, package_id, retry_limit): - package = self._get_service(package_id, retry_limit) - try: - package = package['packageInfo'] - except Exception: - raise APIError("packageInfo not present in the package") - if package_id == VOLUME_PACKAGE_ID: - pkg = PersistentVolumes(to_objdict(package)) - pkg.planId = 'default' - else: - # PARTIAL_ATTR set before initializing Package: Package._post_init() depends on is_partial - package[PartialMixin.PARTIAL_ATTR] = False - pkg = Package(to_objdict(package)) - setattr(pkg, 'packageId', package_id) - return pkg - - def delete_package(self, package_id): - path = '/serviceclass/delete?package_uid={}'.format(package_id) - url = self._catalog_api_host + path - return self._execute(url, HttpMethod.DELETE) - - @response_validator(True) - def get_deployment(self, deployment_id, retry_limit): - path = '/serviceinstance/{}'.format(deployment_id) - url = self._catalog_api_host + path - return self._execute(url, HttpMethod.GET, retry_limit) - - @response_validator(True) - def update_deployment(self, payload, retry_limit): - path = '/v2/service_instances/{}'.format(payload['deployment_id']) - url = self._catalog_api_host + path - return self._execute(url, HttpMethod.PATCH, retry_limit, payload=payload) - - @response_validator(True) - def deployment_list(self, phases, device_id, retry_limit): - url = self._catalog_api_host + '/deployment/list' - query_params = {} - if phases: - query_params = {'phase': list(map(str, phases))} - if device_id: - query_params['device_uid'] = device_id - - return self._execute(url, HttpMethod.GET, retry_limit, query_params=query_params) - - @response_validator(True) - def create_package(self, package_payload, retry_limit): - url = self._catalog_api_host + '/serviceclass/add' - return self._execute(url, HttpMethod.POST, retry_limit, package_payload) - - @response_validator(True) - def create_routed_network(self, **network_payload): - url = self._catalog_api_host + '/routednetwork' - routed_network = self._execute(url, HttpMethod.POST, retry_count=0, payload=network_payload) - return routed_network - - @response_validator(True) - def get_routed_network(self, network_guid): - url = self._catalog_api_host + '/routednetwork/{}'.format(network_guid) - return self._execute(url, HttpMethod.GET) - - @response_validator(True) - def delete_routed_network(self, network_guid): - url = self._catalog_api_host + '/routednetwork/{}'.format(network_guid) - return self._execute(url, HttpMethod.DELETE) - - @response_validator(True) - def list_routed_network(self): - url = self._catalog_api_host + '/routednetwork' - return self._execute(url, HttpMethod.GET) - - @response_validator(True) - def create_build(self, build): - url = self._catalog_api_host + '/build' - return self._execute(url, HttpMethod.POST, payload=build._serialize()) - - @response_validator(True) - def get_build(self, guid, include_build_requests): - url = self._catalog_api_host + '/build/{}'.format(guid) - query_params = None - if include_build_requests: - query_params = {'include_build_requests': include_build_requests} - return self._execute(url, HttpMethod.GET, query_params=query_params) - - @response_validator(True) - def list_builds(self, statuses): - url = self._catalog_api_host + '/build' - query_params = None - if statuses: - query_params = {'status': statuses} - - return self._execute(url, HttpMethod.GET, query_params=query_params) - - def delete_build(self, guid): - url = self._catalog_api_host + '/build/{}'.format(guid) - return self._execute(url, HttpMethod.DELETE) - - @response_validator(True) - def trigger_build(self, buildOperation): - url = self._catalog_api_host + '/build/operation/trigger' - return self._execute(url, HttpMethod.PUT, payload=buildOperation) - - @response_validator(True) - def rollback_build(self, buildOperation): - url = self._catalog_api_host + '/build/operation/rollback' - return self._execute(url, HttpMethod.PUT, payload=buildOperation) - @response_validator(True) def get_rosbag_job(self, guid): url = self._catalog_api_host + '/rosbag-jobs/job/{}'.format(guid) @@ -222,23 +107,3 @@ def download_blob(signed_url, filename, download_dir): def delete_rosbag_blob(self, guid): url = self._catalog_api_host + '/rosbag-blobs/{}'.format(guid) return self._execute(url, HttpMethod.DELETE) - - @response_validator(True) - def create_native_network(self, network_payload): - url = self._catalog_api_host + '/nativenetwork' - return self._execute(url, HttpMethod.POST, retry_count=0, payload=network_payload.serialize()) - - @response_validator(True) - def get_native_network(self, network_guid): - url = self._catalog_api_host + '/nativenetwork/{}'.format(network_guid) - return self._execute(url, HttpMethod.GET) - - @response_validator(True) - def delete_native_network(self, network_guid): - url = self._catalog_api_host + '/nativenetwork/{}'.format(network_guid) - return self._execute(url, HttpMethod.DELETE) - - @response_validator(True) - def list_native_network(self): - url = self._catalog_api_host + '/nativenetwork' - return self._execute(url, HttpMethod.GET) diff --git a/rapyuta_io/clients/common_models.py b/rapyuta_io/clients/common_models.py deleted file mode 100644 index d71d7930..00000000 --- a/rapyuta_io/clients/common_models.py +++ /dev/null @@ -1,81 +0,0 @@ -import rapyuta_io -from rapyuta_io.utils.object_converter import ObjBase, enum_field -import six -from rapyuta_io.utils.error import InvalidParameterException -from rapyuta_io.utils import ObjDict - - -class InternalDeploymentStatus(ObjBase): - """ - InternalDeploymentStatus represents Internal Deployment Status. - - :ivar phase: phase of the internal deployment - :vartype phase: :py:class:`rapyuta_io.clients.deployment.DeploymentPhaseConstants` - :ivar status: (full-only) status of the internal deployment - :vartype status: :py:class:`rapyuta_io.clients.deployment.DeploymentStatusConstants` - :ivar error_code: error code of the internal deployment - :vartype error_code: list(str) - - :param phase: phase of the internal deployment - :type phase: :py:class:`rapyuta_io.clients.deployment.DeploymentPhaseConstants` - :param status: status of the internal deployment - :type status: :py:class:`rapyuta_io.clients.deployment.DeploymentStatusConstants` - :param error_code: error code of the internal deployment - :type error_code: list(str) - """ - - def __init__(self, phase, status=None, error_code=None): - self.phase = phase - self.status = status - self.error_code = error_code - - def get_deserialize_map(self): - return { - 'phase': enum_field('phase', rapyuta_io.DeploymentPhaseConstants), - 'status': enum_field('status', rapyuta_io.DeploymentStatusConstants), - 'error_code': 'error_code' - } - - def get_serialize_map(self): - return {} - - -class Limits(ObjDict, ObjBase): - """ - Limits represent the cpu and memory specs of a cloud network - - :ivar cpu: cpu - :vartype cpu: Union [float, integer] - :ivar memory: memory - :vartype memory: integer - - :param cpu: cpu - :type cpu: Union [float, integer] - :param memory: memory - :type memory: integer - """ - - def __init__(self, cpu, memory): - self.validate(cpu, memory) - super(ObjDict, self).__init__(cpu=cpu, memory=memory) - - @staticmethod - def validate(cpu, memory): - if not isinstance(cpu, float) and not isinstance(cpu, six.integer_types): - raise InvalidParameterException('cpu must be a float or integer') - if cpu <= 0: - raise InvalidParameterException('cpu must be a positive number') - if not isinstance(memory, six.integer_types) or memory <= 0: - raise InvalidParameterException('memory must be a positive integer') - - def get_deserialize_map(self): - return { - 'cpu': 'cpu', - 'memory': 'memory', - } - - def get_serialize_map(self): - return { - 'cpu': 'cpu', - 'memory': 'memory', - } diff --git a/rapyuta_io/clients/core_api_client.py b/rapyuta_io/clients/core_api_client.py index 302216f3..6dba92db 100644 --- a/rapyuta_io/clients/core_api_client.py +++ b/rapyuta_io/clients/core_api_client.py @@ -1,20 +1,13 @@ from rapyuta_io.clients.project import Project, User -from rapyuta_io.clients.secret import Secret from rapyuta_io.clients.user_group import UserGroup from rapyuta_io.utils.utils import prepend_bearer_to_auth_token, create_auth_header, get_api_response_data from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils import RestClient, to_objdict -from rapyuta_io.clients.static_route import StaticRoute -from rapyuta_io.utils.error import ResourceNotFoundError +from rapyuta_io.utils import RestClient from rapyuta_io.utils.settings import METRICS_API_QUERY_PATH, LIST_METRICS_API_QUERY_PATH, \ LIST_TAGS_KEY_API_QUERY_PATH, LIST_TAGS_VALUE_API_QUERY_PATH, GET_USER_PATH class CoreAPIClient: - STATIC_ROUTE_PATH = '/api/staticroute' - PROJECT_PATH = '/api/project' - SECRET_PATH = '/api/secret' - def __init__(self, auth_token, project, core_api_host): self._core_api_host = core_api_host self._auth_token = prepend_bearer_to_auth_token(auth_token) @@ -33,166 +26,6 @@ def _add_auth_token_to_routes(self, routes): for route in routes: self._add_header_fields(route) - def _get_all_static_routes(self): - url = self._core_api_host + self.STATIC_ROUTE_PATH + '/list' - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).headers(headers).execute() - return get_api_response_data(response, parse_full=True) - - def _get_static_route_by_url_prefix(self, url_prefix): - url = self._core_api_host + self.STATIC_ROUTE_PATH + '/filter' - query_params = {'urlPrefix': url_prefix} - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).headers(headers).query_param(query_param=query_params).execute() - return get_api_response_data(response, parse_full=True) - - def _get_static_route(self, route_guid): - url = "{}{}/{}/get".format(self._core_api_host, self.STATIC_ROUTE_PATH, route_guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).headers(headers).execute() - return get_api_response_data(response, parse_full=True) - - def _create_static_route(self, url_prefix): - url = self._core_api_host + self.STATIC_ROUTE_PATH + '/create' - headers = create_auth_header(self._auth_token, self._project) - payload = {"urlPrefix": url_prefix} - response = RestClient(url).method(HttpMethod.POST).headers(headers).execute(payload) - return get_api_response_data(response, parse_full=True) - - def get_all_static_routes(self): - static_routes = [] - data = self._get_all_static_routes() - for route in data: - static_route = StaticRoute(to_objdict(route)) - static_routes.append(static_route) - self._add_auth_token_to_routes(static_routes) - return static_routes - - def get_static_route(self, route_guid): - data = self._get_static_route(route_guid) - route = StaticRoute(to_objdict(data)) - self._add_header_fields(route) - return route - - def create_static_route(self, name): - data = self._create_static_route(name) - route = StaticRoute(to_objdict(data)) - self._add_header_fields(route) - return route - - def delete_static_route(self, guid): - url = self._core_api_host + self.STATIC_ROUTE_PATH + '/delete' - headers = create_auth_header(self._auth_token, self._project) - payload = {"guid": guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) - - def get_static_route_by_name(self, name): - try: - routes = self._get_static_route_by_url_prefix(name) - except ResourceNotFoundError as e: - return None - route = StaticRoute(to_objdict(routes[0])) - self._add_header_fields(route) - return route - - def create_project(self, project): - url = self._core_api_host + self.PROJECT_PATH + '/create' - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.POST).headers(headers).execute(project.serialize()) - data = get_api_response_data(response, parse_full=True) - project = Project.deserialize(data) - self._add_header_fields(project) - return project - - def get_project(self, guid): - url = '{}{}/{}/get'.format(self._core_api_host, self.PROJECT_PATH, guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - data = get_api_response_data(response, parse_full=True) - project = Project.deserialize(data) - self._add_header_fields(project) - return project - - def list_projects(self): - url = self._core_api_host + self.PROJECT_PATH + '/list' - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - data = get_api_response_data(response, parse_full=True) - projects = [] - for project_data in data: - project = Project.deserialize(project_data) - self._add_header_fields(project) - projects.append(project) - return projects - - def delete_project(self, guid): - url = self._core_api_host + self.PROJECT_PATH + '/delete' - headers = create_auth_header(self._auth_token, self._project) - payload = {'guid': guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) - - def add_user_to_project(self, project_guid, user_guid): - url = '{}{}/{}/adduser'.format(self._core_api_host, self.PROJECT_PATH, project_guid) - headers = create_auth_header(self._auth_token, self._project) - payload = {'userGUID': user_guid} - response = RestClient(url).method(HttpMethod.PUT).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) - - def remove_user_from_project(self, project_guid, user_guid): - url = '{}{}/{}/removeuser'.format(self._core_api_host, self.PROJECT_PATH, project_guid) - headers = create_auth_header(self._auth_token, self._project) - payload = {'userGUID': user_guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) - - def create_secret(self, secret): - url = self._core_api_host + self.SECRET_PATH + '/create' - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.POST).headers(headers).execute(secret.serialize()) - data = get_api_response_data(response, parse_full=True) - secret = Secret.deserialize(data) - self._add_header_fields(secret) - return secret - - def get_secret(self, guid): - url = '{}{}/{}/get'.format(self._core_api_host, self.SECRET_PATH, guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - data = get_api_response_data(response, parse_full=True) - secret = Secret.deserialize(data) - self._add_header_fields(secret) - return secret - - def list_secrets(self): - url = self._core_api_host + self.SECRET_PATH + '/list' - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - data = get_api_response_data(response, parse_full=True) - secrets = [] - for secret_data in data: - secret = Secret.deserialize(secret_data) - self._add_header_fields(secret) - secrets.append(secret) - return secrets - - def update_secret(self, guid, secret): - url = self._core_api_host + self.SECRET_PATH + '/' + guid + '/update' - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.PUT).headers(headers).execute(secret.serialize()) - data = get_api_response_data(response, parse_full=True) - secret = Secret.deserialize(data) - self._add_header_fields(secret) - return secret - - def delete_secret(self, guid): - url = self._core_api_host + self.SECRET_PATH + '/delete' - headers = create_auth_header(self._auth_token, self._project) - payload = {'guid': guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) - def query_metrics(self, metrics_query): url = self._core_api_host + METRICS_API_QUERY_PATH headers = create_auth_header(self._auth_token, self._project) diff --git a/rapyuta_io/clients/deployment.py b/rapyuta_io/clients/deployment.py deleted file mode 100644 index a6eec2e9..00000000 --- a/rapyuta_io/clients/deployment.py +++ /dev/null @@ -1,303 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import -import enum -import time - -from rapyuta_io.clients.provision_client import ProvisionClient -from rapyuta_io.utils import ObjDict, to_objdict, DeploymentNotRunningException, RetriesExhausted -from rapyuta_io.utils.settings import BIND_ID, DEFAULT_SLEEP_INTERVAL, \ - DEPLOYMENT_STATUS_RETRY_COUNT -from rapyuta_io.utils.partials import PartialMixin -import six -from six.moves import range - - -def _poll_till_ready(instance, retry_count, sleep_interval, ready_phases=None): - # TODO: convert into DeploymentPollerMixin (pollers.py) - """ - - :param instance: instance can be a deployment, volume, or a routed network instance with get_status method - :param retry_count: Parameter to specify the retries. - :param sleep_interval: Parameter to specify the interval between retries. - - """ - if ready_phases is None: - ready_phases = [] - - dep_status = None - for _ in range(retry_count): - dep_status = instance.get_status() - - if dep_status.phase in ready_phases: - return dep_status - - if dep_status.phase == DeploymentPhaseConstants.SUCCEEDED.value: - if dep_status.status in [DeploymentStatusConstants.RUNNING.value, - DeploymentStatusConstants.AVAILABLE.value, - DeploymentStatusConstants.RELEASED.value]: - return dep_status - time.sleep(sleep_interval) - continue - if dep_status.phase == DeploymentPhaseConstants.INPROGRESS.value: - time.sleep(sleep_interval) - continue - if dep_status.phase == DeploymentPhaseConstants.PROVISIONING.value: - errors = dep_status.errors or [] - if 'DEP_E153' not in errors: # DEP_E153 (image-pull error) will persist across retries - time.sleep(sleep_interval) - continue - - msg = 'Deployment might not progress: phase={} status={} errors={}'.format( - dep_status.phase, dep_status.status, dep_status.errors) - raise DeploymentNotRunningException(msg, deployment_status=dep_status) - - msg = 'Retries exhausted: Tried {} times with {}s interval.'.format(retry_count, - sleep_interval) - if dep_status: - msg += ' Deployment: phase={} status={} errors={}'.format(dep_status.phase, dep_status.status, - dep_status.errors) - raise RetriesExhausted(msg) - - -class DeploymentPhaseConstants(str, enum.Enum): - """ - Enumeration variables for the deployment phase - - Deployment phase can be any of the following types \n - DeploymentPhaseConstants.INPROGRESS \n - DeploymentPhaseConstants.PROVISIONING \n - DeploymentPhaseConstants.SUCCEEDED \n - DeploymentPhaseConstants.FAILED_TO_START \n - DeploymentPhaseConstants.PARTIALLY_DEPROVISIONED \n - DeploymentPhaseConstants.DEPLOYMENT_STOPPED \n - """ - - def __str__(self): - return str(self.value) - - INPROGRESS = 'In progress' - PROVISIONING = 'Provisioning' - SUCCEEDED = 'Succeeded' - FAILED_TO_START = 'Failed to start' - PARTIALLY_DEPROVISIONED = 'Partially deprovisioned' - DEPLOYMENT_STOPPED = 'Deployment stopped' - - -class DeploymentStatusConstants(str, enum.Enum): - """ - Enumeration variables for the deployment status - - Deployment status can be any of the following types \n - DeploymentStatusConstants.RUNNING \n - DeploymentStatusConstants.PENDING \n - DeploymentStatusConstants.ERROR \n - DeploymentStatusConstants.UNKNOWN \n - DeploymentStatusConstants.STOPPED \n - """ - - def __str__(self): - return str(self.value) - - RUNNING = 'Running' - PENDING = 'Pending' - ERROR = 'Error' - UNKNOWN = 'Unknown' - STOPPED = 'Stopped' - - # Disk statuses, not meant to be documented - BOUND = 'Bound' - RELEASED = 'Released' - AVAILABLE= 'Available' - FAILED = 'Failed' - - -class DeploymentStatus(ObjDict): - """ - DeploymentStatus class - - :ivar deploymentId: Deployment Id. - :ivar name: Deployment name. - :ivar packageId: Package Id. - :ivar status: Deployment status - :ivar phase: Deployment phase - :ivar errors: Deployment errors - :ivar componentInfo: List containing the deployment components and their status. - :ivar dependentDeploymentStatus: Dependent deployment status. - :ivar packageDependencyStatus: Package dependency status. - """ - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - -class Deployment(PartialMixin, ObjDict): - """ - Deployment class represents a running deployment. Member variables of the class represent the - properties of the deployment. \n - Variables marked as (full-only) are only available on a full object. Use `refresh()` to convert a - partial object into a full one. - - :ivar deploymentId: Deployment Id. - :ivar name: Deployment name. - :ivar packageId: Package Id. - :ivar packageName: Package Name. - :ivar packageAPIVersion: Package API Version. - :ivar planId: Plan Id. - :ivar bindable: Deployment is bindable or not. - :ivar labels: (full-only) Labels associated with the deployment. - :ivar parameters: (full-only) Deployment parameters. - :ivar componentInfo: (full-only) List of component details. - :ivar componentInstanceIds: (full-only) List of component instance ids. - :ivar dependentDeployments: (full-only) List of dependent deployments. - :ivar dependentDeploymentStatus: (full-only) Dependent deployments status details. - :ivar packageDependencyStatus: (full-only) Package dependency status details. - :ivar coreNetworks: (full-only) Routed and Native network details. - :ivar phase: Phase of the deployment. - :vartype phase: :py:class:`~rapyuta_io.clients.deployment.DeploymentPhaseConstants` - :ivar status: (full-only) Status of the deployment. - :vartype status: :py:class:`~rapyuta_io.clients.deployment.DeploymentStatusConstants` - :ivar provisionContext: (full-only) Context set during provisioning. - :ivar currentGeneration: (full-only) Build generation number. - :ivar errors: (full-only) List of errors. - :ivar inUse: Deployment is in use or not. - :ivar ownerProject: Owner project guid. - :ivar creator: Creator user guid. - :ivar CreatedAt: Date of creation. - :ivar UpdatedAt: Date of updation. - :ivar DeletedAt: Date of deletion. - """ - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - def _get_status(self, retry_limit=0): - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - return provision_client.deployment_status(self.deploymentId, retry_limit) - - def refresh(self): - full_deployment = self._get_status() - for key, value in six.iteritems(full_deployment): - setattr(self, key, to_objdict(value)) - self.is_partial = False - - def get_status(self, retry_limit=0): - """ - Get the deployment status - - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :returns: instance of class :py:class:`DeploymentStatus`: - :raises: :py:class:`APIError`: If the get deployment status api returns an error, the status - code is anything other than 200/201 - - Following example demonstrates how to get a deployment status - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> deployment = client.get_deployment('test_deployment_id') - >>> deployment.get_status() - - """ - return DeploymentStatus(to_objdict(self._get_status(retry_limit))) - - def deprovision(self, retry_limit=0): - """ - Deprovision the deployment instance represented by the corresponding :py:class:`~Deployment`: class. - - :param retry_limit: - :return: True if de-provision is successful, False otherwise - :raises: :py:class:`~rapyuta_io.utils.error.ParameterMissingException`: If the planId or - deploymentId is missing in the request. - :raises: :py:class:`~rapyuta_io.utils.error.APIError`: If the deprovision-api returns an error, the status code - is anything other than 200/201 - - Following example demonstrates how to deprovision a deployment - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> deployment = client.get_deployment('test_deployment_id') - >>> deployment.deprovision() - - """ - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - return provision_client.deprovision(self.deploymentId, self.planId, self.packageId, - retry_limit) - - def get_service_binding(self, binding_id=None, retry_limit=0): - """ - Get the service bindings of the deployment. Service Bindings contain the credentials that - can be used to communicate with the deployment. - - :param binding_id: Optional parameter Binding Id - :type binding_id: string - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: Service binding dictionary containing credentials. - :raises: :py:class:`ServiceBindingError`: If the request failed to get the service binding. - :raises: :py:class:`APIError`: If service binding api return an error, the status code is - anything other than 200/201 - - Following example demonstrates how to get the service binding - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> deployment = client.get_deployment('test_deployment_id') - >>> deployment.get_service_binding() - - """ - - if binding_id is None: - binding_id = BIND_ID - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - credentials = provision_client.service_binding(self.deploymentId, self.planId, - self.packageId, binding_id, retry_limit) - return credentials - - def poll_deployment_till_ready(self, retry_count=DEPLOYMENT_STATUS_RETRY_COUNT, - sleep_interval=DEFAULT_SLEEP_INTERVAL, ready_phases=None): - """ - - Wait for the deployment to be ready - - :param retry_count: Optional parameter to specify the retries. Default value is 15 - :param sleep_interval: Optional parameter to specify the interval between retries. - Default value is 6 Sec. - :return: instance of class :py:class:`DeploymentStatus`: - :raises: :py:class:`APIError`: If service binding api return an error, the status code is - anything other than 200/201 - :raises: :py:class:`DeploymentNotRunningException`: If the deployment’s state might not - progress due to errors. - :raises: :py:class:`RetriesExhausted`: If number of polling retries exhausted before the - deployment could succeed or fail. - - Following example demonstrates use of poll_deployment_till_ready, and in case of deployment - failure uses error codes to check whether it was due to device being offline. - Read more on error codes: https://userdocs.rapyuta.io/6_troubleshoot/611_deployment-error-codes/ - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import (DeploymentNotRunningException, - ... RetriesExhausted) - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> deployment = client.get_deployment('test_deployment_id') - >>> try: - ... dep_status = deployment.poll_deployment_till_ready() - ... print dep_status - ... except RetriesExhausted as e: - ... print e, 'Retry again?' - ... except DeploymentNotRunningException as e: - ... print e - ... if 'DEP_E151' in e.deployment_status.errors: - ... print 'Device is either offline or not reachable' - - - """ - return _poll_till_ready(self, retry_count, sleep_interval, ready_phases) - - def get_component_instance_id(self, component_name): - for component_info in self.componentInfo: - component_instance_id = component_info.get('componentInstanceID') - if component_info.get('name') == component_name: - return component_instance_id - return None diff --git a/rapyuta_io/clients/device.py b/rapyuta_io/clients/device.py index 348e78d0..46d74f48 100644 --- a/rapyuta_io/clients/device.py +++ b/rapyuta_io/clients/device.py @@ -10,7 +10,6 @@ import six import rapyuta_io -from rapyuta_io.clients.deployment import DeploymentPhaseConstants, Deployment from rapyuta_io.clients.model import TopicsStatus, DeviceConfig, Label, Metric, LogUploadStatus, \ LogUploads, SharedURL from rapyuta_io.utils import ObjDict, RestClient, ParameterMissingException, \ @@ -292,11 +291,11 @@ def validate(name, runtime, runtime_docker, runtime_preinstalled, ros_distro, ro raise InvalidParameterException( 'python_version must be one of rapyuta.io.client.device.DevicePythonVersion') if ros_distro is not None and ( - ros_distro not in list(rapyuta_io.clients.package.ROSDistro.__members__.values())): + ros_distro not in list(ROSDistro.__members__.values())): raise InvalidParameterException('ros_distro must be one of rapyuta_io.clients.package.ROSDistro') - if runtime_preinstalled and ros_distro == rapyuta_io.clients.package.ROSDistro.NOETIC: + if runtime_preinstalled and ros_distro == ROSDistro.NOETIC: raise InvalidParameterException('preinstalled runtime does not support noetic ros_distro yet') - if ros_distro == rapyuta_io.clients.package.ROSDistro.NOETIC and python_version == DevicePythonVersion.PYTHON2: + if ros_distro == ROSDistro.NOETIC and python_version == DevicePythonVersion.PYTHON2: raise InvalidParameterException('noetic ros_distro not supported on python_version 2') if rosbag_mount_path is not None and not isinstance(rosbag_mount_path, six.string_types): raise InvalidParameterException('rosbag_mount_path must be of type string') @@ -344,8 +343,6 @@ def _deserialize(cls, data): obj.config_variables = [DeviceConfig(to_objdict(config)) for config in obj.config_variables] if obj.labels: obj.labels = [Label(to_objdict(label)) for label in obj.labels] - if hasattr(obj, 'deployments') and obj.deployments: - obj.deployments = [Deployment(to_objdict(deployment)) for deployment in obj.deployments] return obj def is_online(self): @@ -1396,3 +1393,43 @@ def upgrade(self): if response.status_code == requests.codes.BAD_REQUEST: raise DeploymentRunningException() get_api_response_data(response) + + +class ROSDistro(str, enum.Enum): + """ + Enumeration variables for the Supported ROS Distros. ROS Distro may be one of: \n + ROSDistro.KINETIC ('kinetic') \n + ROSDistro.MELODIC ('melodic') \n + ROSDistro.NOETIC ('noetic') \n + """ + + def __str__(self): + return str(self.value) + + KINETIC = 'kinetic' + MELODIC = 'melodic' + NOETIC = 'noetic' + + +class DeploymentPhaseConstants(str, enum.Enum): + """ + Enumeration variables for the deployment phase + + Deployment phase can be any of the following types \n + DeploymentPhaseConstants.INPROGRESS \n + DeploymentPhaseConstants.PROVISIONING \n + DeploymentPhaseConstants.SUCCEEDED \n + DeploymentPhaseConstants.FAILED_TO_START \n + DeploymentPhaseConstants.PARTIALLY_DEPROVISIONED \n + DeploymentPhaseConstants.DEPLOYMENT_STOPPED \n + """ + + def __str__(self): + return str(self.value) + + INPROGRESS = 'In progress' + PROVISIONING = 'Provisioning' + SUCCEEDED = 'Succeeded' + FAILED_TO_START = 'Failed to start' + PARTIALLY_DEPROVISIONED = 'Partially deprovisioned' + DEPLOYMENT_STOPPED = 'Deployment stopped' diff --git a/rapyuta_io/clients/native_network.py b/rapyuta_io/clients/native_network.py deleted file mode 100644 index e4eaae62..00000000 --- a/rapyuta_io/clients/native_network.py +++ /dev/null @@ -1,255 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import - -import rapyuta_io.clients.package # to prevent cyclic import -from rapyuta_io.clients.deployment import _poll_till_ready -from rapyuta_io.clients.common_models import InternalDeploymentStatus -from rapyuta_io.utils import RestClient -from rapyuta_io.utils.error import InvalidParameterException, OperationNotAllowedError, ParameterMissingException -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.utils import create_auth_header, get_api_response_data -from rapyuta_io.utils.object_converter import ObjBase, enum_field, nested_field -from rapyuta_io.utils.partials import PartialMixin -import six -from rapyuta_io.clients.common_models import Limits - -class NativeNetwork(PartialMixin, ObjBase): - """ - NativeNetwork represents native network. \n - Variables marked as (full-only) are only available on a full object. Use `refresh()` to convert a - partial object into a full one. - - :ivar name: name of the native network - :vartype name: str - :ivar runtime: runtime of the native network - :vartype runtime: :py:class:`~rapyuta_io.clients.package.Runtime` - :ivar ros_distro: ROS distribution - :vartype ros_distro: :py:class:`~rapyuta_io.clients.package.ROSDistro` - :ivar parameters: parameters of the native network - :vartype parameters: :py:class:`~rapyuta_io.clients.native_network.Parameters` - :ivar created_at: creation time of the native network - :vartype created_at: str - :ivar updated_at: updating time of the native network - :vartype updated_at: str - :ivar guid: native network guid - :vartype guid: str - :ivar owner_project: project id - :vartype owner_project: str - :ivar creator: user id - :vartype creator: str - :ivar internal_deployment_guid: guid of the internal deployment - :vartype internal_deployment_guid: str - :ivar internal_deployment_status: internal deployment status of the native network - :vartype internal_deployment_status: :py:class:`~rapyuta_io.clients.common_models.InternalDeploymentStatus` - - :param name: name of the native network - :type name: str - :param runtime: runtime of the native network - :type runtime: :py:class:`~rapyuta_io.clients.package.Runtime` - :param ros_distro: ROS distribution - :type ros_distro: :py:class:`~rapyuta_io.clients.package.ROSDistro` - :param parameters: parameters of the native network - :type parameters: :py:class:`~rapyuta_io.clients.native_network.Parameters` - """ - NATIVE_NETWORK_PATH = 'nativenetwork' - - def __init__(self, name, runtime, ros_distro, parameters=None): - self.validate(name, runtime, ros_distro, parameters) - self.name = name - self.runtime = runtime - self.ros_distro = ros_distro - self.parameters = parameters - self.created_at = None - self.updated_at = None - self.guid = None - self.owner_project = None - self.creator = None - self.internal_deployment_guid = None - self.internal_deployment_status = None - - @staticmethod - def validate(name, runtime, ros_distro, parameters=None): - if not name or not isinstance(name, six.string_types): - raise InvalidParameterException('name must be a non-empty string') - if ros_distro not in list(rapyuta_io.clients.package.ROSDistro.__members__.values()): - raise InvalidParameterException('ros_distro must be one of rapyuta_io.clients.package.ROSDistro') - if runtime not in list(rapyuta_io.clients.package.Runtime.__members__.values()): - raise InvalidParameterException('runtime must be one of rapyuta_io.clients.package.Runtime') - if ros_distro == rapyuta_io.clients.package.ROSDistro.NOETIC and \ - runtime == rapyuta_io.clients.package.Runtime.DEVICE: - raise InvalidParameterException('device runtime does not support noetic ros_distro yet') - if parameters is not None and not isinstance(parameters, Parameters): - raise InvalidParameterException('parameters must be of type rapyuta_io.clients.native_network.Parameters') - if runtime == rapyuta_io.clients.package.Runtime.DEVICE.value: - if parameters is None: - raise InvalidParameterException('parameters must be present for device runtime') - if not parameters.device_id: - raise InvalidParameterException('device_id field must be present in rapyuta_io.clients.' - 'native_network.Parameters object for device runtime') - if not parameters.network_interface: - raise InvalidParameterException('network_interface must be present in rapyuta_io.clients.' - 'native_network.Parameters object for device runtime') - - def get_deserialize_map(self): - return { - 'name': 'name', - 'guid': 'guid', - 'owner_project': 'ownerProject', - 'creator': 'creator', - 'runtime': enum_field('runtime', rapyuta_io.clients.package.Runtime), - 'ros_distro': enum_field('rosDistro', rapyuta_io.clients.package.ROSDistro), - 'internal_deployment_guid': 'internalDeploymentGUID', - 'internal_deployment_status': nested_field('internalDeploymentStatus', InternalDeploymentStatus), - 'parameters': nested_field('parameters', Parameters), - 'created_at': 'CreatedAt', - 'updated_at': 'UpdatedAt' - } - - def get_serialize_map(self): - return { - 'name': 'name', - 'runtime': 'runtime', - 'rosDistro': 'ros_distro', - 'parameters': 'parameters' - } - - def poll_native_network_till_ready(self, retry_count=120, sleep_interval=5): - # TODO: implement and use DeploymentPollerMixin. see _poll_till_ready - """ - - Wait for the native network to be ready - - :param retry_count: Optional parameter to specify the retries. Default value is 120 - :param sleep_interval: Optional parameter to specify the interval between retries. - Default value is 5 Sec. - :return: instance of class :py:class:`~rapyuta_io.clients.common_models.InternalDeploymentStatus`: - :raises: :py:class:`APIError`: If service binding api return an error, the status code is - anything other than 200/201 - :raises: :py:class:`DeploymentNotRunningException`: If the deployment’s state might not - progress due to errors. - :raises: :py:class:`RetriesExhausted`: If number of polling retries exhausted before the - deployment could succeed or fail. - - Following example demonstrates use of poll_native_network_till_ready: - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import (DeploymentNotRunningException, - ... RetriesExhausted) - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> native_network = client.get_native_network('network-guid') - >>> try: - ... network_status = native_network.poll_native_network_till_ready() - ... print network_status - ... except RetriesExhausted as e: - ... print e, 'Retry again?' - ... except DeploymentNotRunningException as e: - ... print e, e.deployment_status - - """ - _poll_till_ready(self, retry_count, sleep_interval) - return self - - def get_status(self): - if self.guid is None: - raise OperationNotAllowedError('resource has not been created') - native_network = NativeNetwork.deserialize(self._get_full_resource()) - internal_deployment_status = native_network.internal_deployment_status - internal_deployment_status.errors = native_network.get_error_code() - return internal_deployment_status - - def _get_full_resource(self): - url = '{}/{}/{}'.format(self._host, self.NATIVE_NETWORK_PATH, self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - return get_api_response_data(response, parse_full=True) - - def refresh(self): - NativeNetwork.deserialize(self._get_full_resource(), obj=self) - self.is_partial = False - - def delete(self): - - """ - Delete the native network using the native network object. - - Following example demonstrates how to delete a native network using native network object: - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> native_network = client.get_native_network(network_guid='network_guid') - >>> native_network.delete() - - """ - - url = '{}/{}/{}'.format(self._host, self.NATIVE_NETWORK_PATH, self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute() - get_api_response_data(response, parse_full=True) - return True - - def get_error_code(self): - getattr(self.internal_deployment_status, "error_code", []) - - -class Parameters(ObjBase): - """ - Parameters represents Native Network Parameters - - :ivar limits: Values corresponding to limits of the parameters - :vartype limits: :py:class:`~rapyuta_io.clients.common_models.Limits` - :ivar device_id: device_id of device on which the native network is deployed. - :vartype device_id: str - :ivar network_interface: network interface to which native network is binded. - :vartype network_interface: str - :ivar restart_policy: restart policy of native network. - :vartype restart_policy: enum :py:class:`~rapyuta_io.clients.package.RestartPolicy` - - :param limits: Values corresponding to limits of the parameters - :type limits: :py:class:`~rapyuta_io.clients.common_models.Limits` - :param device: device on which the native network is deployed. - :type device: :py:class:`~rapyuta_io.clients.device.Device` - :param network_interface: network interface to which native network is binded. - :type network_interface: str - :param restart_policy: restart policy of native network. - :type restart_policy: enum :py:class:`~rapyuta_io.clients.package.RestartPolicy` - """ - - def __init__(self, limits=None, device=None, network_interface=None, restart_policy=None): - self.validate(device, network_interface, restart_policy) - self.limits = limits - self.device_id = device and device.uuid - self.network_interface = network_interface - self.restart_policy = restart_policy - - @staticmethod - def validate(device, network_interface, restart_policy): - if device: - if not isinstance(device, rapyuta_io.clients.device.Device): - raise InvalidParameterException('device must be of type rapyuta_io.clients.device.Device') - if not device.get('uuid'): - raise InvalidParameterException('uuid field must be present in rapyuta_io.clients.device.Device object') - if not device.get('ip_interfaces'): - raise InvalidParameterException( - 'ip_interfaces field must be present in rapyuta_io.clients.device.Device object') - ip_interfaces = device.ip_interfaces or {} - if network_interface not in list(ip_interfaces.keys()): - raise InvalidParameterException('NETWORK_INTERFACE should be in {}'.format(list(ip_interfaces.keys()))) - if restart_policy is not None and ( - restart_policy not in list(rapyuta_io.clients.package.RestartPolicy.__members__.values())): - raise InvalidParameterException('RestartPolicy must be one of rapyuta_io.clients.package.RestartPolicy') - - def get_deserialize_map(self): - return { - 'limits': nested_field('limits', Limits), - 'device_id': 'device_id', - 'network_interface': 'NETWORK_INTERFACE', - 'restart_policy': enum_field('restart_policy', rapyuta_io.clients.package.RestartPolicy), - } - - def get_serialize_map(self): - return { - 'limits': 'limits', - 'device_id': 'device_id', - 'NETWORK_INTERFACE': 'network_interface', - 'restart_policy': 'restart_policy' - } diff --git a/rapyuta_io/clients/package.py b/rapyuta_io/clients/package.py deleted file mode 100644 index 03496a97..00000000 --- a/rapyuta_io/clients/package.py +++ /dev/null @@ -1,967 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import - -from collections import defaultdict - -import enum -import six - -import rapyuta_io -from rapyuta_io.clients import ProvisionClient -from rapyuta_io.clients.deployment import Deployment -from rapyuta_io.clients.deployment import DeploymentPhaseConstants -from rapyuta_io.clients.native_network import NativeNetwork -from rapyuta_io.clients.device import SPACE_GUID, VOLUME_PACKAGE_ID, INSTANCE_ID, Device -from rapyuta_io.clients.plan import Plan -from rapyuta_io.clients.rosbag import ROSBagJob, ROSBagOptions, UploadOptions, OverrideOptions -from rapyuta_io.clients.routed_network import RoutedNetwork -from rapyuta_io.clients.static_route import StaticRoute -from rapyuta_io.utils import ObjDict, to_objdict, OperationNotAllowedError, PlanNotFound, \ - InvalidParameterException, RestClient, APIError, ParameterMissingException, \ - AliasNotProvidedException, DuplicateAliasException -from rapyuta_io.utils.constants import DEVICE, DEVICE_ID, LABELS -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.settings import ORGANIZATION_GUID, CATALOG_API_PATH -from rapyuta_io.utils.utils import is_empty, \ - get_api_response_data, \ - create_auth_header -from rapyuta_io.utils.partials import PartialMixin - -CURRENT_PKG_VERSION = '2.0.0' - - -class Package(PartialMixin, ObjDict): - """ - Package class represents a service package. It contains method to provision - an instance of the package on cloud or on device. Additionally, it provides other utility - method. \n - Variables marked as (full-only) are only available on a full object. Use `refresh()` to convert a - partial object into a full one. - - :ivar packageId: Id of the package. - :ivar packageName: Package name. - :ivar packageVersion: Version of the package. - :ivar apiVersion: (full-only) Package API Version. - :ivar bindable: Package is bindable or not. - :ivar description: Description of the package. - :ivar category: (full-only) Package category. - :ivar plans: (full-only) List of plans associated with the package. - :vartype plans: list(:py:class:`~rapyuta_io.clients.plan.Plan`) - :ivar isPublic: (full-only) Boolean denoting whether the package is public or not. - :ivar status: (full-only) Status of the package. - :ivar tags: (full-only) Tags associated with the package. - :ivar buildGeneration: (full-only) Build generation. - :ivar ownerProject: (full-only) Owner project guid. - :ivar creator: (full-only) Creator user guid. - :ivar CreatedAt: (full-only) Date of creation. - :ivar UpdatedAt: (full-only) Date of updation. - :ivar DeletedAt: (full-only) Date of deletion. - - """ - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - # Normalize object across responses from /serviceclass/status and /v2/catalog - if 'guid' in self: # /serviceclass/status - self.packageId = self.guid - else: # /v2/catalog - self.packageId = self.id - self.packageName = self.name - self.packageVersion = self.metadata['packageVersion'] - - self._post_init() - - def refresh(self): - self._refresh() - - def _refresh(self): - url = self._host + CATALOG_API_PATH + "?package_uid=%s" % self.packageId - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - package = get_api_response_data(response, True) - try: - package = package['packageInfo'] - except Exception: - raise APIError("packageInfo not present in the package") - # PARTIAL_ATTR set before initializing Package: Package._post_init() depends on is_partial - package[PartialMixin.PARTIAL_ATTR] = False - pkg = Package(to_objdict(package)) - for attr in pkg.keys(): - self.__setattr__(attr, pkg.__getattr__(attr)) - - def delete(self): - - """ - Delete the package using the package object. - - Following example demonstrates how to delete a package using package object: - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> package = client.get_package(package_id='package_id') - >>> package.delete() - - """ - - url = self._host + '/serviceclass/delete?package_uid={}'.format(self.packageId) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute() - get_api_response_data(response, parse_full=True) - - def _post_init(self): - if self.plans and not self.is_partial: - plans = list() - for plan in self.plans: - plans.append(Plan(to_objdict(plan))) - self.plans = plans - return - - def _update_auth_token(self, objects): - for obj in objects: - setattr(obj, '_host', self._host) - setattr(obj, '_auth_token', self._auth_token) - setattr(obj, '_project', self._project) - return - - def get_plan_by_id(self, plan_id): - for plan in self.plans: - if plan.planId == plan_id: - return plan - raise PlanNotFound(plan_id) - - def provision(self, deployment_name, provision_configuration, retry_limit=0): - """ - Provision the package (represented by the package class). Package can be deployed on device - or cloud. If the required runtime of the package is device, then specify the device in the - package config. \n - If the Package object is not complete, as indicated by `self.is_partial`, then `self.refresh()` is called to - update the object. - - :param deployment_name: Deployment Name - :type deployment_name: string - :param provision_configuration: Provision payload - :type provision_configuration: :py:class:`ProvisionConfiguration`: - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: Instance of class :py:class:`Deployment`: - :raises: :py:class:`APIError`: If the API returns an error, a status code of - anything other than 200/201 is returned. - :raises: :py:class:`OperationNotAllowedError`: If the provision request is invalid - - """ - if not deployment_name or not isinstance(deployment_name, six.string_types): - raise InvalidParameterException("deployment_name must be a non-empty string") - if not isinstance(provision_configuration, ProvisionConfiguration): - raise InvalidParameterException('provision_configuration must be of type ProvisionConfiguration') - if provision_configuration._is_provisioned: - raise InvalidParameterException('cannot reuse this ProvisionConfiguration for provisioning') - if self.is_partial: - self.refresh() - provision_configuration.validate() - provision_configuration.context['name'] = deployment_name - delattr(provision_configuration, 'plan') - delattr(provision_configuration, '_dependency_seen_aliases') - provision_configuration._is_provisioned = True - - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - response = provision_client.provision(provision_configuration, retry_limit) - deployment_status = provision_client.deployment_status(response['operation'], retry_limit) - deployment = Deployment(to_objdict(deployment_status)) - deployment.is_partial = False - self._update_auth_token([deployment]) - return deployment - - def deployments(self, phases=None, retry_limit=0): - - """ - Get all the deployments of the package - - :param phases: optional parameter to filter out the deployments based on current deployment - :type phases: list(DeploymentPhaseConstants) - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: list of instance of class :py:class:`Deployment`: - :raises: :py:class:`APIError`: If deployment info api return an error, the status code is - anything other than 200/201 - - Following example demonstrates how to the deployment list - - >>> from rapyuta_io import Client, DeploymentPhaseConstants - >>> client = Client(auth_token='auth_token', project='project') - >>> package = client.get_package('test_package_id') - >>> package.deployments() - >>> deployments_list_filtered_by_phase = package.deployments(phases= - >>> [DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING]) - - """ - - deployment_list = ProvisionClient(self._host, self._auth_token, self._project) \ - .deployments(self.packageId, phases, retry_limit) - deployments = list() - if deployment_list: - for deployment in deployment_list: - deployments.append(Deployment(to_objdict(deployment))) - self._update_auth_token(deployments) - - return deployments - - def get_provision_configuration(self, plan_id=None): - """ - Get provision configuration payload for package provision request. \n - If the Package object is not complete, as indicated by `self.is_partial`, then `self.refresh()` is called to - update the object. - - :param plan_id: Plan Id - :type plan_id: string - :return: return instance of class :py:class:`ProvisionConfiguration`: - - """ - if self.is_partial: - self.refresh() - - if plan_id: - plan = self.get_plan_by_id(plan_id) - else: - try: - plan = self.plans[0] - except IndexError: - raise PlanNotFound() - - provision_request = ProvisionConfiguration(self.packageId, plan) - return provision_request - - -class ProvisionConfiguration(ObjDict): - """ - ProvisionConfiguration class that contains the component configuration for a package - deployment. - - """ - - def __init__(self, package_id, plan, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - self.service_id = package_id - self.plan = plan - self.plan_id = plan.planId - self.api_version = kwargs.get('api_version', '1.0.0') - self._init_parameters() - self._dependency_seen_aliases = set() - self._is_provisioned = False - self._devices = dict() - - def _init_parameters(self): - self.context = dict() - self.context['component_context'] = defaultdict(lambda: defaultdict(dict)) - self.instance_id = INSTANCE_ID - self.organization_guid = ORGANIZATION_GUID - self.space_guid = SPACE_GUID - self.accepts_incomplete = True - self.parameters = {'global': dict()} - self.context.setdefault('labels', list()) - self.context.setdefault('dependentDeployments', list()) - self.context.setdefault('diskMountInfo', list()) - self.context.setdefault('routedNetworks', list()) - self.context.setdefault('nativeNetworks', list()) - for component in self.plan.components.components: - component_id = self.plan.get_component_id(component.name) - com_dict = dict(component_id=component_id) - for params in component.parameters: - if not params.get('default'): - com_dict[params.name] = None - else: - com_dict[params.name] = params.default - self.parameters[component_id] = com_dict - self.set_component_alias(component.name) - job_defs = getattr(component, 'rosBagJobDefs', []) - for job_def in job_defs: - rosbag_opts = ROSBagOptions.deserialize(job_def.recordOptions) - upload_opts = UploadOptions.deserialize(job_def.uploadOptions) if hasattr(job_def, 'uploadOptions') \ - else None - override_opts = OverrideOptions.deserialize(job_def.overrideOptions) if \ - hasattr(job_def, 'overrideOptions') else None - job = ROSBagJob( - job_def.name, - rosbag_options=rosbag_opts, - upload_options=upload_opts, - override_options=override_opts - ) - self.add_rosbag_job(component.name, job) - - def _validate_device_id(self, component_id): - value = self.parameters[component_id].get(DEVICE_ID, None) - if is_empty(value): - msg = 'component is not mapped with the device' - raise OperationNotAllowedError(msg) - return True - - def _get_component_by_name(self, component_name): - for component in self.plan.components.components: - if component.name == component_name: - return component - raise AttributeError - - def validate_component_executables( - self, - component_name, - device_runtime, - device_docker_enabled, - device_preinstalled_enabled - ): - component = self.plan.get_component_by_name(component_name) - if not component: - raise OperationNotAllowedError('Component named %s is not found on the plan' % - component_name) - for executable in component.executables: - is_docker_executable = executable.get('buildGUID') or \ - executable.get('gitExecutable') or executable.get('docker') - - if is_docker_executable and (device_runtime != Device.DOCKER_COMPOSE and not device_docker_enabled): - raise OperationNotAllowedError('Device must be a {} device'.format(Device.DOCKER_COMPOSE)) - if not is_docker_executable and \ - (device_runtime != Device.PRE_INSTALLED and not device_preinstalled_enabled): - raise OperationNotAllowedError('Device must be a {} device'.format(Device.PRE_INSTALLED)) - - def add_restart_policy(self, component_name, restart_policy): - """ - Add RestartPolicy for the component - - :param component_name: Component name - :type component_name: string - :param restart_policy: one of RestartPolicy enums - :type restart_policy: enum :py:class:`~RestartPolicy` - :return: Updated instance of class :py:class:`ProvisionConfiguration` - :raises: :py:class:`InvalidParameterException`: If restart policy is not invalid - """ - if restart_policy not in list(RestartPolicy.__members__.values()): - raise InvalidParameterException('Restart policy must be one of rapyuta_io.clients.package.RestartPolicy') - component_id = self.plan.get_component_id(component_name) - self.context['component_context'][component_id]['component_override']['restart_policy'] = restart_policy.value - return self - - def add_rosbag_job(self, component_name, rosbag_job): - """ - Add rosbag for a component - - :param component_name: Component name - :type component_name: string - :param rosbag_job: instance of ROSBagJob - :type rosbag_job: :py:class:`~rapyuta_io.clients.rosbag.ROSBagJob` - :return: Updated instance of class :py:class:`ProvisionConfiguration` - :raises: :py:class:`InvalidParameterException`: If rosbag_job is not instance of ROSBagJob - :raises: :py:class:`OperationNotAllowedError`: If component is non ros or is a device component - """ - component_id = self.plan.get_component_id(component_name) - if not isinstance(rosbag_job, ROSBagJob): - raise InvalidParameterException('rosbag_job needs to a ROSBagJob object') - component = self.plan.get_component_by_name(component_name) - if not component.ros.isROS: - raise OperationNotAllowedError('rosbag job is only supported for ros components') - component_context = self.context['component_context'][component_id] - component_context['ros_bag_job_defs'] = component_context.get('ros_bag_job_defs', []) - for job in component_context['ros_bag_job_defs']: - if job['name'] == rosbag_job.name: - raise OperationNotAllowedError('rosbag job with same name already exists') - rosbag_jobs = {'name': rosbag_job.name, - 'recordOptions': rosbag_job.rosbag_options.serialize()} - if rosbag_job.upload_options: - rosbag_jobs['uploadOptions'] = rosbag_job.upload_options.serialize() - if rosbag_job.override_options: - rosbag_jobs['overrideOptions'] = rosbag_job.override_options.serialize() - component_context['ros_bag_job_defs'].append(rosbag_jobs) - - def remove_rosbag_job(self, component_name, job_name): - """ - Remove rosbag job by its name - - :param component_name: Component name - :type component_name: string - :param job_name: name of ROSBagJob - :type job_name: string - """ - component_id = self.plan.get_component_id(component_name) - component_context = self.context['component_context'][component_id] - component_context['ros_bag_job_defs'] = \ - [job for job in component_context['ros_bag_job_defs'] if job['name'] != job_name] - - def add_routed_network(self, routed_network, network_interface=None): - """ - Add Routed Network - - :param routed_network: RoutedNetwork - :type routed_network: instance of :py:class:`~rapyuta_io.clients.routed_network.RoutedNetwork` - :param network_interface: interface to which current deployment to bind - :type network_interface: string - :return: Updated instance of class :py:class:`ProvisionConfiguration` - :raises: :py:class:`InvalidParameterException`: If routed network is not valid - :raises: :py:class:`OperationNotAllowedError`: If network interface given for cloud runtime - """ - - if not isinstance(routed_network, RoutedNetwork): - raise InvalidParameterException('routed networks must be of type RoutedNetwork') - - if routed_network.runtime == Runtime.CLOUD and network_interface: - raise OperationNotAllowedError('cloud routed network does not bind to network interface') - - routed_network_config = dict() - routed_network_config_exists = False - for routed_net in self.context['routedNetworks']: - if routed_net['guid'] == routed_network['guid']: - routed_network_config_exists = True - routed_network_config = routed_net - break - - if network_interface: - routed_network_config['bindParameters'] = {'NETWORK_INTERFACE': network_interface} - - if not routed_network_config_exists: - routed_network_config['guid'] = routed_network.guid - self.context['routedNetworks'].append(routed_network_config) - - return self - - def add_routed_networks(self, routed_networks): - """ - - :param routed_networks: list of routed network :py:class:`~rapyuta_io.clients.routed_network.RoutedNetwork` - :type routed_networks: list - :return: Updated instance of class :py:class:`ProvisionConfiguration` - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.package import ROSDistro - >>> client = Client(auth_token='auth_token', project='project') - >>> routed_network = client.create_cloud_routed_network('network_name', - ROSDistro.KINETIC, True) - >>> routed_network.poll_routed_network_till_ready() - >>> package = client.get_package('test_package_id') - >>> package_provision_config = package.get_provision_configuration('test_plan_id') - >>> package_provision_config.add_routed_networks([routed_network]) - >>> package.provision(deployment_name, package_provision_config) - """ - - for routed_network in routed_networks: - self.add_routed_network(routed_network) - return self - - def add_native_network(self, native_network, network_interface=None): - """ - Add Native Network - - :param native_network: NativeNetwork - :type native_network: instance of :py:class:`~rapyuta_io.clients.native_network.NativeNetwork` - :param network_interface: interface to which current deployment to bind, only required for device native network - :type network_interface: string - - :return: Updated instance of class :py:class:`ProvisionConfiguration` - :raises: :py:class:`InvalidParameterException`: If native network is not valid - :raises: :py:class:`OperationNotAllowedError`: If native network is not of cloud runtime - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.package import ROSDistro, Runtime - >>> from rapyuta_io.clients.native_network import NativeNetwork - >>> client = Client(auth_token='auth_token', project='project') - >>> native_network = NativeNetwork("native_network_name", Runtime.CLOUD, - ... ROSDistro.KINETIC) - >>> native_network = client.create_native_network(native_network) - >>> native_network.poll_native_network_till_ready() - >>> package = client.get_package('test_package_id') - >>> package_provision_config = package.get_provision_configuration('test_plan_id') - >>> package_provision_config.add_native_network(native_network) - >>> package.provision('deployment_name', package_provision_config) - """ - if not isinstance(native_network, NativeNetwork): - raise InvalidParameterException('native network must be of type NativeNetwork') - - if native_network.runtime == Runtime.CLOUD and network_interface: - raise OperationNotAllowedError('cloud native network does not bind to network interface') - - native_network_config = dict() - native_network_config_exists = False - for native_net in self.context['nativeNetworks']: - if native_net['guid'] == native_network.guid: - native_network_config_exists = True - native_network_config = native_net - break - - if network_interface: - native_network_config['bindParameters'] = {'NETWORK_INTERFACE': network_interface} - - if not native_network_config_exists: - native_network_config['guid'] = native_network.guid - self.context['nativeNetworks'].append(native_network_config) - - return self - - def add_native_networks(self, native_networks): - """ - Add Native Networks - - :param native_networks: list of native network :py:class:`~rapyuta_io.clients.native_network.NativeNetwork` - :type native_networks: list - :return: Updated instance of class :py:class:`ProvisionConfiguration` - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.package import ROSDistro, Runtime - >>> from rapyuta_io.clients.native_network import NativeNetwork - >>> client = Client(auth_token='auth_token', project='project') - >>> native_network = NativeNetwork("native_network_name", Runtime.CLOUD, - ROSDistro.KINETIC) - >>> native_network = client.create_native_network(native_network) - >>> native_network.poll_native_network_till_ready() - >>> package = client.get_package('test_package_id') - >>> package_provision_config = package.get_provision_configuration('test_plan_id') - >>> package_provision_config.add_native_networks([native_network]) - >>> package.provision('deployment_name', package_provision_config) - """ - for native_network in native_networks: - self.add_native_network(native_network) - return self - - def add_device(self, component_name, device, ignore_device_config=None, set_component_alias=True): - """ - Map component configuration with a device. ie, Setting the component is going to deploy on the given device. - By Default, the component alias name is set to the device name, if this has to be ignored please use - 'set_component_alias=False' as one of the method parameters. - - :param component_name: Component name - :type component_name: string - :param device: Device - :type device: instance of class :py:class:`Device`: - :param ignore_device_config: Optional parameter to ignore the device config variables - :type ignore_device_config: list - :param set_component_alias: Optional parameter to set the alias name of the component same as device name. - Defaults to True - :type set_component_alias: bool - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - :raises: :py:class:`OperationNotAllowedError`: If the device is not online - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project') - >>> package = client.get_package('test_package_id') - >>> device = client.get_device('test_device_id') - >>> package_provision_config = package.get_provision_configuration('test_plan_id') - >>> # ros_workspace will be ignored while adding device to provision configuration - >>> package_provision_config.add_device('test_component_name', device, - >>> ignore_device_config=['ros_workspace'], set_component_alias=False) - >>> package.provision('deployment_name', package_provision_config) - - """ - ignore_device_config = ignore_device_config or [] - - device_runtime = device.get_runtime() - device_docker_enabled = device.is_docker_enabled() - device_preinstalled_enabled = device.is_preinstalled_enabled() - - self.validate_component_executables( - component_name, - device_runtime, - device_docker_enabled, - device_preinstalled_enabled - ) - - component_id = self.plan.get_component_id(component_name) - component_params = self.parameters.get(component_id) - component_params[DEVICE_ID] = device.deviceId - - if set_component_alias: - self.set_component_alias(component_name, device.name) - if (device_runtime != device.PRE_INSTALLED and not device_preinstalled_enabled) \ - and 'ros_workspace' not in ignore_device_config: - ignore_device_config.append('ros_workspace') - if (device_runtime != device.DOCKER_COMPOSE and not device_docker_enabled) and \ - 'rosbag_mount_path' not in ignore_device_config: - ignore_device_config.append('rosbag_mount_path') - - for config_var in device.get_config_variables(): - if config_var.key in ignore_device_config: - continue - component_params[config_var.key] = config_var.value - - if device_runtime == device.PRE_INSTALLED or device_preinstalled_enabled: - if 'ros_workspace' not in ignore_device_config and not self._validate_ros_workspace( - component_id): - raise InvalidParameterException('ros_workspace is not set') - - component = self._get_component_by_name(component_name) - if not component.ros.isROS: - self.parameters[component_id].pop('ros_distro', None) - if (device_runtime == device.PRE_INSTALLED or device_preinstalled_enabled) and \ - not self._validate_ros_distro(component.ros.isROS, component_id): - raise InvalidParameterException('ros_distro is not set') - global_config = self.parameters.get('global') - if 'device_ids' not in global_config: - global_config['device_ids'] = list() - global_config['device_ids'].append(device.deviceId) - self._devices[device.deviceId] = device - return self - - def add_parameter(self, component_name, key, value): - """ - Add component parameters - - :param component_name: Component name - :type component_name: string - :param key: Parameter key - :type key: string - :param value: Parameter value - :type value: string - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - """ - if not component_name or not isinstance(component_name, six.string_types): - raise InvalidParameterException("component_name must be a non-empty string") - if not key or not isinstance(key, six.string_types): - raise InvalidParameterException("key must be a non-empty string") - - component_id = self.plan.get_component_id(component_name) - self.parameters[component_id][key] = value - - return self - - def set_component_alias(self, component_name, alias="", set_ros_namespace=False): - """ - Set an alias and ROS_NAMESPACE environment variable flag for the selected component. - This is used in scoping and targeting. alias defaults to the component name. - set_ros_namespace defaults to false - - *Note:* In typical scenarios in the case of a cloud deployment, alias is set to the component name - (or some derivation thereof) and on the device it is set to the device name. But it is left to the user. - All set aliases in a deployment and its dependent deployments are required to be unique. - - :param component_name: Component name - :type component_name: string - :param alias: alias for component - :type alias: string - :param set_ros_namespace: flag to set alias as ROS_NAMESPACE environment variable in the deployment. - It should be used only for deployments using native networks. - :type set_ros_namespace: bool - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - """ - if not component_name or not isinstance(component_name, six.string_types): - raise InvalidParameterException("component_name must be a non-empty string") - if not isinstance(alias, six.string_types): - raise InvalidParameterException("alias must be a string") - if not isinstance(set_ros_namespace, bool): - raise InvalidParameterException("set_ros_namespace must be a boolean") - component_id = self.plan.get_component_id(component_name) - alias = component_name if alias == "" else alias - self.parameters[component_id]["bridge_params"] = {"alias": alias, "setROSNamespace": set_ros_namespace} - - def _add_dependency(self, deployment_id, component_id=None, mount_path=None, network_interface=None, - executable_mounts=None): - dep_info = dict() - dep_info['dependentDeploymentId'] = deployment_id - if component_id: - dep_info['applicableComponentId'] = component_id - else: - dep_info['applicableComponentId'] = '' - - dep_info['config'] = dict() - if mount_path: - dep_info['config']['mountPath'] = mount_path - if executable_mounts: - d = {} - for mount in executable_mounts: - d[mount.exec_name] = {} - d[mount.exec_name]['mountPath'] = mount.mount_path - if mount.sub_path: - d[mount.exec_name]['subPath'] = mount.sub_path - dep_info['config']['mountPaths'] = d - if network_interface: - dep_info['config']['NETWORK_INTERFACE'] = network_interface - self.context['dependentDeployments'].append(dep_info) - - def _add_disk_mount_info(self, resource_id, component_id, executable_mounts): - dep_info = dict() - dep_info['diskResourceId'] = resource_id - dep_info['applicableComponentId'] = component_id - dep_info['config'] = dict() - mountPaths = {} - for mount in executable_mounts: - mountPaths[mount.exec_name] = {} - mountPaths[mount.exec_name]['mountPath'] = mount.mount_path - if mount.sub_path: - mountPaths[mount.exec_name]['subPath'] = mount.sub_path - else: - mountPaths[mount.exec_name]['subPath'] = '/' - - dep_info['config']['mountPaths'] = mountPaths - self.context['diskMountInfo'].append(dep_info) - - def mount_volume(self, component_name, volume=None, device=None, mount_path=None, executable_mounts=None): - """ - To mount a volume instance. - - :param component_name: Component name - :type component_name: string - :param volume: VolumeInstance class - :type volume: instance of class :py:class:`VolumeInstance`: - :param device: Device class - :type device: instance of class :py:class:`Device`: - :param mount_path: Mount path - :type mount_path: string - :param executable_mounts: list of executable mounts. mandatory parameter for device volumes - :type executable_mounts: list(:py:class:`ExecutableMount`) - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - """ - if volume == None and device == None: - raise InvalidParameterException('either a volume or device parameter must be present') - if volume != None and device != None: - raise InvalidParameterException('both volume and device parameter cannot be present') - component_id = self.plan.get_component_id(component_name) - if device != None: - if not isinstance(device, Device): - raise InvalidParameterException('device must be of type Device') - if not isinstance(executable_mounts, list) or not all( - isinstance(mount, ExecutableMount) for mount in executable_mounts): - raise InvalidParameterException( - 'executable_mounts must be a list of rapyuta_io.clients.package.ExecutableMount') - if device.get_runtime() != Device.DOCKER_COMPOSE and not device.is_docker_enabled(): - raise OperationNotAllowedError('Device must be a {} device'.format(Device.DOCKER_COMPOSE)) - component_params = self.parameters.get(component_id) - if component_params.get(DEVICE_ID) != device.deviceId: - raise OperationNotAllowedError('Device must be added to the component') - self._add_disk_mount_info(device.deviceId, component_id, executable_mounts) - else: - if not isinstance(volume, rapyuta_io.clients.persistent_volumes.VolumeInstance): - raise InvalidParameterException( - 'volume must be of type rapyuta_io.clients.persistent_volumes.VolumeInstance') - if not volume.packageId == VOLUME_PACKAGE_ID: - raise InvalidParameterException('Invalid volume instance') - if volume.get_status().phase != DeploymentPhaseConstants.SUCCEEDED.value: - raise OperationNotAllowedError('Dependent deployment is not running') - if (mount_path is None and executable_mounts is None) or ( - mount_path is not None and executable_mounts is not None): - raise InvalidParameterException('One of mount_path or executable_mounts should be present') - if executable_mounts is not None and ((not isinstance(executable_mounts, list)) or not all( - isinstance(mount, ExecutableMount) for mount in executable_mounts)): - raise InvalidParameterException( - 'executable_mounts must be a list of rapyuta_io.clients.package.ExecutableMount') - self._add_dependency(volume.deploymentId, component_id, mount_path, executable_mounts=executable_mounts) - return self - - def add_dependent_deployment(self, deployment, ready_phases=[DeploymentPhaseConstants.SUCCEEDED.value]): - """ - Add dependent deployments. \n - `deployment.refresh()` is called to get the latest deployment status. - - :param deployment: Deployment - :type deployment: class :py:class:`Deployment`: - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - """ - deployment.refresh() - if deployment.phase not in ready_phases: - raise OperationNotAllowedError('dependent deployment is not ready') - self._update_dependency_seen_aliases_set(deployment) - self._add_dependency(deployment_id=deployment.deploymentId) - - return self - - def add_static_route(self, component_name, endpoint_name, static_route): - """ - Add static route to a component in a package - - :param component_name: Name of the component to add static route to - :param endpoint_name: Name of the endpoint (Should be exposed externally) - :param static_route: class :py:class:`StaticRoute`: - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - """ - if not isinstance(static_route, StaticRoute): - raise TypeError("{} is not of type Static Route".format(static_route)) - component_id = self.plan.get_component_id(component_name) - self.context['component_context'][component_id]['static_route_config'][endpoint_name] = static_route.guid - return self - - def _update_dependency_seen_aliases_set(self, deployment): - for params in deployment.parameters.values(): - try: - self._dependency_seen_aliases.add(params.bridge_params.alias) - self.plan._needs_alias = True - except AttributeError: - pass - - def add_label(self, key, value): - """ - Add labels - - :param key: Key - :type key: string - :param value: Value - :type value: string - :return: Updated instance of class :py:class:`ProvisionConfiguration`: - """ - if not key or not value: - raise ParameterMissingException(str.format("key or value of parameter is missing")) - - label = {'key': key, 'value': value} - self.context[LABELS].append(label) - return self - - def _validate_ros_workspace(self, component_id): - if not self.parameters[component_id].get('ros_workspace'): - return False - return True - - def _validate_ros_distro(self, isRos, component_id): - if isRos and not self.parameters[component_id].get('ros_distro'): - return False - return True - - def validate(self): - for component in self.plan.components.components: - component_id = self.plan.get_component_id(component.name) - if component.requiredRuntime == DEVICE: - self._validate_device_id(component_id) - self._validate_rosbag_devices(component_id) - component_params = component.parameters - for param in component_params: - name = param.name - if is_empty(self.parameters[component_id][name]): - raise InvalidParameterException('Provide the value for the parameter {} in ' - 'component {}'.format(param.name, component.name)) - - self._validate_aliases() - return True - - def _validate_rosbag_devices(self, component_id): - if not self.context['component_context'][component_id].get('ros_bag_job_defs'): - return - device_id = self.parameters[component_id].get(DEVICE_ID, None) - if not device_id: - return - device = self._devices.get(device_id) - if device.get_runtime() == device.PRE_INSTALLED: - raise InvalidParameterException('ROSBag on Device does not support Preinstalled ' - 'devices') - required_config = [x for x in device.config_variables if x.key == 'rosbag_mount_path' - and x.value != ''] - if not required_config: - raise InvalidParameterException('This device does not have ROSBag components installed.' - ' Please re-onboard the device to use ROSBag features') - - def _validate_aliases(self): - aliases_needed = self.plan.needs_aliases() - if not aliases_needed: - for params in self.parameters.values(): - if "bridge_params" in params: - params.pop("bridge_params") - return - seen_aliases = set() - for component_id, params in self.parameters.items(): - if component_id == "global": - continue - try: - alias = params["bridge_params"]["alias"] - if alias in seen_aliases: - raise DuplicateAliasException( - "Aliases must be unique. Alias %s provided for %s component isn't" % (alias, component_id)) - if alias in self._dependency_seen_aliases: - raise DuplicateAliasException( - "Aliase %s for %s component conflicts with dependant deployment" % (alias, component_id)) - seen_aliases.add(alias) - except KeyError: - raise AliasNotProvidedException( - "Aliases are required but not provided for %s component" % component_id) - - -class ExecutableMount(object): - """ - ExecutableMount defines the mount details specific to an executable. - - :ivar exec_name: Name of the executable. - :vartype exec_name: str - :ivar mount_path: Mountpath of the executable - :vartype mount_path: str - :ivar sub_path: Subpath of the executable - :vartype sub_path: str - :ivar uid: Userid to which subpath belongs to - :vartype uid: int - :ivar gid: Groupid to which subpath belongs to - :vartype gid: int - :ivar perm: Permissions for subpath - :vartype perm: int - - :param exec_name: Name of the executable. - :type exec_name: str - :param mount_path: Mountpath of the executable - :type mount_path: str - :param sub_path: Subpath of the executable - :type sub_path: str - :param uid: userid of subpath - :type uid: int - :param gid: groupid of subpath - :type gid: int - :param perm: permissions of subpath - :type perm: int - """ - - def __init__(self, exec_name, mount_path, sub_path=None, uid=None, gid=None, perm=None): - self.validate(exec_name, mount_path, sub_path, uid, gid, perm) - self.exec_name = exec_name - self.mount_path = mount_path - self.sub_path = sub_path - self.uid = uid - self.gid = gid - self.perm = perm - - @staticmethod - def validate(exec_name, mount_path, sub_path=None, uid=None, gid=None, perm=None): - if not isinstance(exec_name, six.string_types): - raise InvalidParameterException('exec_name must be a non-empty string') - if not isinstance(mount_path, six.string_types): - raise InvalidParameterException('mount_path must be a non-empty string') - if sub_path is not None and not isinstance(sub_path, six.string_types): - raise InvalidParameterException('sub_path must be a non-empty string') - if uid is not None and not isinstance(uid, six.integer_types): - raise InvalidParameterException('uid must be a non-empty integer') - if gid is not None and not isinstance(gid, six.integer_types): - raise InvalidParameterException('gid must be a non-empty integer') - if perm is not None and not isinstance(perm, six.integer_types): - raise InvalidParameterException('perm must be a non-empty integer') - - -class RestartPolicy(str, enum.Enum): - """ - Enumeration variables for the Restart Policy. Restart Policy may be 'Always', 'Never' or 'OnFailure' \n - RestartPolicy.Always \n - RestartPolicy.Never \n - RestartPolicy.OnFailure \n - """ - - def __str__(self): - return str(self.value) - - Always = "always" - Never = "no" - OnFailure = "on-failure" - - -class ROSDistro(str, enum.Enum): - """ - Enumeration variables for the Supported ROS Distros. ROS Distro may be one of: \n - ROSDistro.KINETIC ('kinetic') \n - ROSDistro.MELODIC ('melodic') \n - ROSDistro.NOETIC ('noetic') \n - """ - - def __str__(self): - return str(self.value) - - KINETIC = 'kinetic' - MELODIC = 'melodic' - NOETIC = 'noetic' - - -class Runtime(str, enum.Enum): - """ - Enumeration variables for the Supported Runtimes. Runtime may be 'cloud', or 'device' \n - Runtime.CLOUD \n - Runtime.DEVICE \n - """ - - def __str__(self): - return str(self.value) - - CLOUD = 'cloud' - DEVICE = 'device' diff --git a/rapyuta_io/clients/persistent_volumes.py b/rapyuta_io/clients/persistent_volumes.py deleted file mode 100644 index 8c18603e..00000000 --- a/rapyuta_io/clients/persistent_volumes.py +++ /dev/null @@ -1,348 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import -import enum - -from rapyuta_io.clients import ProvisionClient -from rapyuta_io.clients.deployment import DeploymentPhaseConstants, _poll_till_ready -from rapyuta_io.clients.package import ProvisionConfiguration, Runtime -from rapyuta_io.clients.plan import Plan -from rapyuta_io.utils import ObjDict, to_objdict, InvalidParameterException, DeploymentNotRunningException -from rapyuta_io.utils.settings import DEPLOYMENT_STATUS_RETRY_COUNT, DEFAULT_SLEEP_INTERVAL -from rapyuta_io.utils.partials import PartialMixin - -import six - -VOLUME_COMPONENT = 'volumeComponent' - - -class DiskType(str, enum.Enum): - """ - Enumeration variables for the Volume Type. The type may be 'Default' or 'SSD' \n - DiskType.DEFAULT \n - DiskType.SSD - """ - - def __str__(self): - return str(self.value) - - SSD = 'ssd' - DEFAULT = 'ssd' - - -class DiskCapacity(int, enum.Enum): - """ - Enumeration variables for disk capacity. The type may be one of the following \n - DiskCapacity.GiB_4 \n - DiskCapacity.GiB_8 \n - DiskCapacity.GiB_16 \n - DiskCapacity.GiB_32 \n - DiskCapacity.GiB_64 \n - DiskCapacity.GiB_128 \n - DiskCapacity.GiB_256 \n - DiskCapacity.GiB_512 \n - """ - - def __str__(self): - return str(self.value) - - GiB_4 = 4 - GiB_8 = 8 - GiB_16 = 16 - GiB_32 = 32 - GiB_64 = 64 - GiB_128 = 128 - GiB_256 = 256 - GiB_512 = 512 - - -class VolumeInstanceStatus(ObjDict): - """ - VolumeInstanceStatus class - - :ivar deploymentId: Deployment Id. - :ivar name: Volume instance name. - :ivar packageId: Package Id. - :ivar status: Deployment status - :ivar phase: Deployment phase - :ivar errors: Deployment errors - :ivar componentInfo: List containing the deployment components and their status. - :ivar dependentDeploymentStatus: Dependent deployment status. - :ivar packageDependencyStatus: Package dependency status. - - """ - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - -class PersistentVolumes(ObjDict): - """ - PersistentVolumes class represents a a persistent volume package. It contains methods to create - persistent volume instance and listing all the instances. - - :ivar packageId: Id of the package. - :ivar packageName: Package name. - :ivar packageVersion: Version of the package. - :ivar apiVersion: Package API Version. - :ivar bindable: Package is bindable or not. - :ivar description: Description of the package. - :ivar category: Package category. - :ivar plans: List of plans associated with the package. - :vartype plans: list(:py:class:`~rapyuta_io.clients.plan.Plan`) - :ivar isPublic: Boolean denoting whether the package is public or not. - :ivar status: Status of the package. - :ivar tags: Tags associated with the package. - :ivar buildGeneration: Build generation. - :ivar ownerProject: Owner project guid. - :ivar creator: Creator user guid. - :ivar CreatedAt: Date of creation. - :ivar UpdatedAt: Date of updation. - :ivar DeletedAt: Date of deletion. - - """ - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - plan = Plan(to_objdict(self.plans[0])) - self.plans = [plan] - - def _update_auth_token(self, objects): - for obj in objects: - setattr(obj, '_host', self._host) - setattr(obj, '_auth_token', self._auth_token) - setattr(obj, '_project', self._project) - return - - def create_volume_instance(self, name, capacity, disk_type=DiskType.DEFAULT, retry_limit=0): - """ - Create a volume instance - - :param name: name of the volume instance - :type name: str - :param capacity: disk capacity of the volume instance - :type capacity: enum :py:class:`~DiskCapacity` - :param disk_type: Type of disk to be deployed. Allowed values are - default or ssd - :type disk_type: enum :py:class:`~DiskType` - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :returns: volume instance - :raises: :py:class:`InvalidParameterException`: If the disk type and volume capacity - parameters are missing or invalid. - :raises: :py:class:`APIError`: If the api return an error, the status code is - anything other than 200/201 - - Following example demonstrates how to create a volume instance - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.persistent_volumes import DiskType, DiskCapacity - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> pv = client.get_persistent_volume() - >>> pv.create_volume_instance(name='myVolume', capacity=DiskCapacity.GiB_32, disk_type=DiskType.SSD) - - """ - if disk_type not in list(DiskType.__members__.values()): - raise InvalidParameterException('disk_type must be of rapyuta_io.clients.persistent_volumes.DiskType') - # supporting integer values for backward compatibility - if capacity not in list(DiskCapacity.__members__.values()) or not(isinstance(capacity, DiskCapacity) - or isinstance(capacity, six.integer_types)): - raise InvalidParameterException('capacity must be one of ' - 'rapyuta_io.clients.persistent_volumes.DiskCapacity') - disk_capacity = capacity if isinstance(capacity, six.integer_types) else capacity.value - - disk_payload = {'name': name, 'runtime': Runtime.CLOUD, 'capacity': disk_capacity, 'diskType': disk_type} - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - response = provision_client.create_disk(disk_payload, retry_limit) - disk = provision_client.get_disk(response['guid'], retry_limit) - volume_instance = provision_client.deployment_status(disk['internalDeploymentGUID'], retry_limit) - volume_instance = VolumeInstance(to_objdict(volume_instance)) - self._update_auth_token([volume_instance]) - volume_instance.is_partial = False - return volume_instance - - def get_volume_instance(self, volume_instance_id, retry_limit=0): - """ - Get a volume instance - - :param volume_instance_id: Volume instance Id - :type volume_instance_id: string - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: return instance of class :py:class:`VolumeInstance`: - :raises: :py:class:`APIError`: If the api return an error, the status code is - anything other than 200/201 - - - Following example demonstrates how to a volume instance - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> persistent_volume = client.get_persistent_volume() - >>> volume_instance = persistent_volume.get_volume_instance('instance_id') - - """ - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - instance = provision_client.deployment_status(volume_instance_id, retry_limit) - volume_instance = VolumeInstance(to_objdict(instance)) - self._update_auth_token([volume_instance]) - volume_instance.is_partial = False - return volume_instance - - def get_all_volume_instances(self, phases=None, retry_limit=0, deploymentGUIDs=None): - """ - Get all persistent volume instances - - :param phases: optional parameter to filter out the deployments based on current deployment - :type phases: list(DeploymentPhaseConstants) - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :returns: List of volume instances - :raises: :py:class:`APIError`: If the api return an error, the status code is - anything other than 200/201 - - Following example demonstrates how to create a volume instance - - >>> from rapyuta_io import Client, DeploymentPhaseConstants - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> pv = client.get_persistent_volume() - >>> pv.get_all_volume_instances() - >>> volume_deployments_list_filtered_by_phase = pv.get_all_volume_instances(phases= - >>> [DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING]) - - """ - - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - disks = provision_client.list_disk(deploymentGUIDs, retry_limit) - volumes = list() - for disk in disks: - volume_instance = provision_client.deployment_status(disk['internalDeploymentGUID'], retry_limit) - volume_instance = VolumeInstance(to_objdict(volume_instance)) - if phases is None or volume_instance.phase in phases: - volumes.append(volume_instance) - self._update_auth_token(volumes) - return volumes - - -class VolumeInstance(PartialMixin, ObjDict): - """ - VolumeInstance class represents a running Persistent Volume. \n - Variables marked as (full-only) are only available on a full object. Use `refresh()` to convert a - partial object into a full one. - - :ivar deploymentId: Deployment Id. - :ivar name: Deployment name. - :ivar packageId: Package Id. - :ivar packageName: Package Name. - :ivar packageAPIVersion: Package API Version. - :ivar planId: Plan Id. - :ivar bindable: Deployment is bindable or not. - :ivar labels: (full-only) Labels associated with the deployment. - :ivar parameters: (full-only) Deployment parameters. - :ivar componentInfo: (full-only) List of component details. - :ivar componentInstanceIds: (full-only) List of component instance ids. - :ivar dependentDeployments: (full-only) List of dependent deployments. - :ivar dependentDeploymentStatus: (full-only) Dependent deployments status details. - :ivar packageDependencyStatus: (full-only) Package dependency status details. - :ivar coreNetworks: (full-only) Routed and Native network details. - :ivar phase: Phase of the deployment. - :vartype phase: :py:class:`~rapyuta_io.clients.deployment.DeploymentPhaseConstants` - :ivar status: (full-only) Status of the deployment. - :vartype status: :py:class:`~rapyuta_io.clients.deployment.DeploymentStatusConstants` - :ivar provisionContext: (full-only) Context set during provisioning. - :ivar currentGeneration: (full-only) Build generation number. - :ivar errors: (full-only) List of errors. - :ivar inUse: Deployment is in use or not. - :ivar ownerProject: Owner project guid. - :ivar creator: Creator user guid. - :ivar CreatedAt: Date of creation. - :ivar UpdatedAt: Date of updation. - :ivar DeletedAt: Date of deletion. - - """ - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - def get_status(self, retry_limit=0): - """ - Get the status of volume instance - - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :returns: instance of class :py:class:`DeploymentStatus`: - :raises: :py:class:`APIError`: If the api return an error, the status code is - anything other than 200/201 - - Following example demonstrates how to get a deployment status - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> persistent_volume = client.get_persistent_volume() - >>> volume_instance = persistent_volume.get_volume_instance('instance_id') - >>> volume_instance.get_status() - - """ - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - instance_status = provision_client.deployment_status(self.deploymentId, retry_limit) - return VolumeInstanceStatus(to_objdict(instance_status)) - - def refresh(self): - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - full_volume_instance = provision_client.deployment_status(self.deploymentId, retry_limit=0) - for key, value in six.iteritems(full_volume_instance): - setattr(self, key, to_objdict(value)) - self.is_partial = False - - def poll_deployment_till_ready(self, retry_count=DEPLOYMENT_STATUS_RETRY_COUNT, - sleep_interval=DEFAULT_SLEEP_INTERVAL): - """ - - Wait for the deployment to be ready - - :param retry_count: Optional parameter to specify the retries. Default value is 15 - :param sleep_interval: Optional parameter to specify the interval between retries. - Default value is 6 Sec. - :return: instance of class :py:class:`VolumeInstanceStatus`: - :raises: :py:class:`APIError`: If service binding api return an error, the status code is - anything other than 200/201 - :raises: :py:class:`DeploymentNotRunningException`: If the deployment's state might not - progress due to errors - :raises: :py:class:`RetriesExhausted`: If number of polling retries exhausted before the - deployment could succeed or fail. - - Following example demonstrates use of poll_deployment_till_ready. - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import (DeploymentNotRunningException, - ... RetriesExhausted) - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> persistent_volume = client.get_persistent_volume() - >>> volume_instance = persistent_volume.get_volume_instance('instance_id') - >>> try: - ... vol_status = volume_instance.poll_deployment_till_ready(sleep_interval=20) - ... print vol_status - ... except RetriesExhausted as e: - ... print e, 'Retry again?' - ... except DeploymentNotRunningException as e: - ... print e, e.deployment_status - - """ - return _poll_till_ready(self, retry_count, sleep_interval) - - def destroy_volume_instance(self, retry_limit=0): - """ - Destroy a volume instance - - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :returns: True if volume is destroyed is successfully, False otherwise - :raises: :py:class:`APIError`: If the api return an error, the status code is - anything other than 200/201 - """ - provision_client = ProvisionClient(self._host, self._auth_token, self._project) - disks = provision_client.list_disk([self.deploymentId], retry_limit) - if len(disks): - return provision_client.delete_disk(disks[0]['guid'], retry_limit) diff --git a/rapyuta_io/clients/plan.py b/rapyuta_io/clients/plan.py deleted file mode 100644 index a79832c2..00000000 --- a/rapyuta_io/clients/plan.py +++ /dev/null @@ -1,124 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import - -import six - -from rapyuta_io.utils import ObjDict, ComponentNotFoundException -from rapyuta_io.utils.utils import is_empty - - -class Plan(ObjDict): - """ - Plan class represents a plan in the package. Member variables of the class represent the - properties of the plan. - - :ivar planId: (full-only) Plan Id. - :ivar planName: (full-only) Plan Name. - :ivar packageId: (full-only) Package Id. - :ivar description: Plan Description. - :ivar singleton: (full-only) Boolean representing whether the plan is singelton or not. - :ivar inboundROSInterfaces: (full-only) Dictionary containing inbound ROS interfaces information. - :ivar dependentDeployments: (full-only) List of other dependent deployments. - :ivar components: (full-only) Dictionary containing components such as executables. - :ivar internalComponents: (full-only) Dictionary containing internal components information. - :ivar metadata: List containing plan metadata. - :ivar CreatedAt: (full-only) Date of creation. - :ivar UpdatedAt: (full-only) Date of updation. - :ivar DeletedAt: (full-only) Date of deletion. - - """ - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - if 'planId' in self: - self.planId = self.planId - else: - self.planId = self.id - self._component_id_map = dict() - self._map_component_id_with_name() - self._needs_alias = None - - def _map_component_id_with_name(self): - if not hasattr(self, 'internalComponents'): - raise ComponentNotFoundException('Internal components not found for the plan: %s(%s)' - % (self.planName, self.planId)) - for component in self.internalComponents: - self._component_id_map[component['componentName']] = component['componentId'] - - def get_component_id(self, component_name): - component_id = self._component_id_map.get(component_name) - if is_empty(component_id): - raise ComponentNotFoundException('Component %s is not found' % component_name) - return component_id - - def get_component_by_name(self, component_name): - for component in self.components.components: - if component.name == component_name: - return component - return None - - def _has_inbound_targeted(self): - try: - if hasattr(self.inboundROSInterfaces.inboundROSInterfaces, 'anyIncomingScopedOrTargetedRosConfig') and \ - self.inboundROSInterfaces.inboundROSInterfaces.anyIncomingScopedOrTargetedRosConfig: - return True - for topic in self.inboundROSInterfaces.inboundROSInterfaces.topics: - if isinstance(topic.targeted, bool) and topic.targeted: - return True - if isinstance(topic.scoped, six.string_types): - if topic.scoped.lower() == 'true': - return True - for service in self.inboundROSInterfaces.inboundROSInterfaces.services: - if isinstance(service.targeted, bool) and service.targeted: - return True - if isinstance(service.scoped, six.string_types): - if service.scoped.lower() == 'true': - return True - for action in self.inboundROSInterfaces.inboundROSInterfaces.actions: - if isinstance(action.targeted, bool) and action.targeted: - return True - if isinstance(action.scoped, six.string_types): - if action.scoped.lower() == 'true': - return True - except AttributeError: - pass - return False - - def _has_oubound_scoped_targeted(self): - if not hasattr(self, 'components'): - return False - if not hasattr(self.components, 'components'): - return False - for component in self.components.components: - if not hasattr(component, 'ros'): - continue - for entity in ['topics', 'services', 'actions']: - entity_list = getattr(component.ros, entity, []) - for element in entity_list: - if hasattr(element, 'targeted'): - if isinstance(element.targeted, bool) and element.targeted: - return True - if isinstance(element.targeted, six.string_types): - if element.scoped.lower() == 'true': - return True - if hasattr(element, 'scoped'): - if isinstance(element.scoped, bool) and element.scoped: - return True - if isinstance(element.scoped, six.string_types): - if element.scoped.lower() == 'true': - return True - return False - - def needs_aliases(self): - if self._needs_alias is not None: - return self._needs_alias - self._needs_alias = (self._has_inbound_targeted() or self._has_oubound_scoped_targeted()) - return self._needs_alias - - def validate(self): - for component in self.config.component_parameters.keys(): - for param in self.config.component_parameters[component]: - if self.config.component_parameters[component][param] is None: - raise ValueError( - "Value of parameter %s of component %s not provided" % ( - param, component)) diff --git a/rapyuta_io/clients/project.py b/rapyuta_io/clients/project.py index 56a2988f..477c6557 100644 --- a/rapyuta_io/clients/project.py +++ b/rapyuta_io/clients/project.py @@ -1,15 +1,13 @@ from __future__ import absolute_import -import re import enum +import re import six from rapyuta_io.clients.organization import Organization -from rapyuta_io.utils import RestClient, InvalidParameterException +from rapyuta_io.utils import InvalidParameterException from rapyuta_io.utils.object_converter import ObjBase, list_field, nested_field -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.utils import create_auth_header, get_api_response_data project_name_regex = re.compile('^[a-z0-9-]{3,15}$') @@ -18,8 +16,6 @@ class Project(ObjBase): """ Project is an organizational unit and all the resources must belong to a Project. - :ivar id: id of the Project - :vartype id: int :ivar guid: guid of the Project :vartype guid: str :ivar created_at: creation time of the Project @@ -33,11 +29,7 @@ class Project(ObjBase): :ivar creator: GUID of the User that created the Project :vartype creator: str :ivar users: Users that have access to the Project - :vartype users: list(:py:class:`~rapyuta_io.clients.project.User`) - :ivar organization: Organization that the project belongs to - :vartype organization: :py:class:`~rapyuta_io.clients.organization.Organization` """ - PROJECT_PATH = '/api/project' def __init__(self, name, organization_guid=None): self.validate(name, organization_guid) @@ -55,27 +47,6 @@ def __init__(self, name, organization_guid=None): setattr(org, 'guid', organization_guid) self.organization = org - def get_deserialize_map(self): - return { - 'id': 'ID', - 'guid': 'guid', - 'created_at': 'CreatedAt', - 'updated_at': 'UpdatedAt', - 'deleted_at': 'DeletedAt', - 'name': 'name', - 'creator': 'creator', - 'users': list_field('users', User), - 'organization': nested_field('organization', Organization) - } - - def get_serialize_map(self): - serialized_project = { - 'name': 'name', - } - if self.organization is not None: - serialized_project['organization'] = 'organization' - return serialized_project - @staticmethod def validate(name, organization_guid): if not isinstance(name, str): @@ -88,27 +59,23 @@ def validate(name, organization_guid): if not project_name_regex.match(name): raise InvalidParameterException('name can have alphabets, numbers or - only') - def delete(self): - - """ - Delete the project using the project object. - - Following example demonstrates how to delete a project using project object: - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> project = client.get_project(guid='project-id') - >>> project.delete() - - """ + def get_deserialize_map(self): + return { + 'guid': 'guid', + 'created_at': 'createdAt', + 'updated_at': 'updatedAt', + 'deleted_at': 'deletedAt', + 'name': 'name', + 'creator': 'creatorGUID', + } - if not (hasattr(self, '_core_api_host') and hasattr(self, '_auth_token')): - raise InvalidParameterException('Project must be created first') - url = self._core_api_host + self.PROJECT_PATH + '/delete' - headers = create_auth_header(self._auth_token, self.guid) - payload = {'guid': self.guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) + def get_serialize_map(self): + serialized_project = { + 'name': 'name', + } + if self.organization is not None: + serialized_project['organization'] = 'organization' + return serialized_project class User(ObjBase): @@ -132,6 +99,7 @@ class User(ObjBase): :ivar organizations: List of organizations that the user is part of :vartype organizations: list(:py:class:`~rapyuta_io.clients.organization.Organization`) """ + def __init__(self): self.guid = None self.first_name = None diff --git a/rapyuta_io/clients/provision_client.py b/rapyuta_io/clients/provision_client.py deleted file mode 100644 index ce957765..00000000 --- a/rapyuta_io/clients/provision_client.py +++ /dev/null @@ -1,93 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import -from rapyuta_io.clients.api_client import CatalogConfig -from rapyuta_io.utils import ResourceNotFoundError, ServiceBindingError -from rapyuta_io.utils.rest_client import RestClient, HttpMethod -from rapyuta_io.utils.settings import * -from rapyuta_io.utils.utils import response_validator -from six.moves import map - - -class ProvisionClient(CatalogConfig): - def __init__(self, catalog_api_host, auth_token, project): - CatalogConfig.__init__(self, catalog_api_host, auth_token, project) - self._api_path = PROVISION_API_PATH - - def _get_api_path(self): - return self._catalog_api_host + self._api_path - - def _execute_api(self, url, method, payload=None, query_params=None, retry_limit=0): - response = RestClient(url).method(method).headers(self._headers) \ - .retry(retry_limit).query_param(query_params).execute(payload=payload) - return response - - @response_validator(True) - def provision(self, payload, retry_limit): - url = self._get_api_path() + "/" + payload['instance_id'] - response = self._execute_api(url, HttpMethod.PUT, payload, retry_limit=retry_limit) - return response - - @response_validator(return_value=True) - def deprovision(self, deployment_id, plan_id, service_id, retry_limit): - path = '{}?plan_id={}&service_id={}&accepts_incomplete=false' \ - .format(deployment_id, plan_id, service_id) - url = self._get_api_path() + "/" + path - response = self._execute_api(url, HttpMethod.DELETE, retry_limit=retry_limit) - return response - - @response_validator(True, errors={404: ServiceBindingError}) - def service_binding(self, deployment_id, plan_id, service_id, binding_id, retry_limit): - payload = dict(service_id=service_id, plan_id=plan_id) - path = '/{}/service_bindings/{}'.format(deployment_id, binding_id) - url = self._get_api_path() + path - return self._execute_api(url, HttpMethod.PUT, payload, retry_limit=retry_limit) - - @response_validator(errors={404: ResourceNotFoundError}, return_value=True) - def service_unbinding(self, deployment_id, plan_id, service_id, binding_id, retry_limit): - path = '/{}/service_bindings/{}?service_id={}&plan_id={}'.format(deployment_id, binding_id, - service_id, plan_id) - url = self._get_api_path() + path - response = self._execute_api(url, HttpMethod.DELETE, retry_limit=retry_limit) - return response - - @response_validator(True) - def deployments(self, service_id, phases, retry_limit): - query_params = {'package_uid': service_id} - if phases: - query_params['phase'] = list(map(str, phases)) - path = '/deployment/list' - url = self._catalog_api_host + path - return self._execute_api(url, HttpMethod.GET, retry_limit=retry_limit, query_params=query_params) - - @response_validator(True) - def deployment_status(self, deployment_id, retry_limit): - path = '/serviceinstance/{}'.format(deployment_id) - url = self._catalog_api_host + path - return self._execute_api(url, HttpMethod.GET, retry_limit=retry_limit) - - @response_validator(True) - def create_disk(self, payload, retry_limit): - path = '/disk' - url = self._catalog_api_host + path - return self._execute_api(url, HttpMethod.POST, payload=payload, retry_limit=retry_limit) - - @response_validator(True) - def get_disk(self, disk_guid, retry_limit): - path = '/disk/{}'.format(disk_guid) - url = self._catalog_api_host + path - return self._execute_api(url, HttpMethod.GET, retry_limit=retry_limit) - - @response_validator(True) - def list_disk(self, deploymentGUIDs=None, retry_limit=0): - path = '/disk' - query_params = {} - if deploymentGUIDs: - query_params['deployment_guid'] = deploymentGUIDs - url = self._catalog_api_host + path - return self._execute_api(url, HttpMethod.GET, query_params=query_params, retry_limit=retry_limit) - - @response_validator(True) - def delete_disk(self, disk_guid, retry_limit): - path = '/disk/{}'.format(disk_guid) - url = self._catalog_api_host + path - return self._execute_api(url, HttpMethod.DELETE, retry_limit=retry_limit) \ No newline at end of file diff --git a/rapyuta_io/clients/routed_network.py b/rapyuta_io/clients/routed_network.py deleted file mode 100644 index 4a319796..00000000 --- a/rapyuta_io/clients/routed_network.py +++ /dev/null @@ -1,153 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import -import six - -from rapyuta_io.clients.deployment import _poll_till_ready -from rapyuta_io.utils import ObjDict -from rapyuta_io.utils import RestClient -from rapyuta_io.utils import to_objdict -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.utils import create_auth_header, get_api_response_data -from rapyuta_io.utils.error import InvalidParameterException -from rapyuta_io.utils.partials import PartialMixin -from rapyuta_io.clients.common_models import Limits - -class RoutedNetwork(PartialMixin, ObjDict): - """ - RoutedNetwork represents Routed Network. \n - Variables marked as (full-only) are only available on a full object. Use `refresh()` to convert a - partial object into a full one. - - :ivar name: Name of RoutedNetwork. - :vartype name: str - :ivar guid: GUID - :vartype guid: str - :ivar runtime: Runtime of RoutedNetwork - :vartype runtime: :py:class:`~rapyuta_io.clients.package.Runtime` - :ivar rosDistro: ROSDistro of RoutedNetwork - :vartype rosDistro: :py:class:`~rapyuta_io.clients.package.ROSDistro` - :ivar shared: Whether the network can be shared. - :vartype shared: bool - :ivar parameters: parameters of the routed network - :vartype parameters: :py:class:`~rapyuta_io.clients.routed_network.Parameters` - :ivar phase: Deployment phase - :vartype phase: :py:class:`~rapyuta_io.clients.deployment.DeploymentPhaseConstants` - :ivar status: (full-only) Deployment status - :vartype status: :py:class:`~rapyuta_io.clients.deployment.DeploymentStatusConstants` - :ivar error_code: Deployment errors - :ivar internalDeploymentGUID: guid of the internal deployment - :vartype internalDeploymentGUID: str - :ivar internalDeploymentStatus: Internal deployment status of the routed network. Has attributes: phase, - status (full-only), and errors. - :vartype internalDeploymentStatus: :py:class:`~rapyuta_io.clients.common_models.InternalDeploymentStatus` - :ivar ownerProject: Owner project guid. - :vartype ownerProject: str - :ivar creator: Creator user guid. - :vartype creator: str - :ivar CreatedAt: Date of creation. - :vartype CreatedAt: str - :ivar UpdatedAt: Date of updation. - :vartype UpdatedAt: str - :ivar DeletedAt: Date of deletion. - :vartype DeletedAt: str - """ - - ROUTED_NETWORK_PATH = 'routednetwork' - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - def poll_routed_network_till_ready(self, retry_count=120, sleep_interval=5): - # TODO: implement and use DeploymentPollerMixin. see _poll_till_ready - """ - - Wait for the routed network to be ready - - :param retry_count: Optional parameter to specify the retries. Default value is 120 - :param sleep_interval: Optional parameter to specify the interval between retries. - Default value is 5 Sec. - :return: instance of class :py:class:`~rapyuta_io.clients.common_models.InternalDeploymentStatus`: - :raises: :py:class:`APIError`: If service binding api return an error, the status code is - anything other than 200/201 - :raises: :py:class:`DeploymentNotRunningException`: If the deployment’s state might not - progress due to errors. - :raises: :py:class:`RetriesExhausted`: If number of polling retries exhausted before the - deployment could succeed or fail. - - Following example demonstrates use of poll_routed_network_till_ready: - - >>> from rapyuta_io import Client - >>> from rapyuta_io.utils.error import (DeploymentNotRunningException, - ... RetriesExhausted) - >>> client = Client(auth_token='auth_token', project="project_guid") - >>> routed_network = client.get_routed_network('network-guid') - >>> try: - ... network_status = routed_network.poll_routed_network_till_ready() - ... print network_status - ... except RetriesExhausted as e: - ... print e, 'Retry again?' - ... except DeploymentNotRunningException as e: - ... print e, e.deployment_status - - """ - _poll_till_ready(self, retry_count, sleep_interval) - return self - - def get_status(self): - routed_network = RoutedNetwork(to_objdict(self._get_full_resource())) - internal_deployment_status = routed_network.internalDeploymentStatus - internal_deployment_status.errors = routed_network.get_error_code() - return internal_deployment_status - - def _get_full_resource(self): - url = '{}/{}/{}'.format(self._host, self.ROUTED_NETWORK_PATH, self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.GET).headers(headers).execute() - return get_api_response_data(response, parse_full=True) - - def refresh(self): - full_network = self._get_full_resource() - for key, value in six.iteritems(full_network): - setattr(self, key, to_objdict(value)) - self.is_partial = False - - def delete(self): - - """ - Delete the routed network using the routed network object. - - Following example demonstrates how to delete a routed network using routed network object: - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> routed_network = client.get_routed_network(network_guid='network_guid') - >>> routed_network.delete() - - """ - - url = '{}/{}/{}'.format(self._host, self.ROUTED_NETWORK_PATH, self.guid) - headers = create_auth_header(self._auth_token, self._project) - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute() - get_api_response_data(response, parse_full=True) - self.clear() - return True - - def get_error_code(self): - return self.internalDeploymentStatus.error_code if hasattr(self.internalDeploymentStatus, 'error_code') else [] - - -class Parameters(ObjDict): - """ - Parameters represents Routed Network Parameters - - :ivar limits: Values corresponding to limits of the parameters - :vartype limits: :py:class:`~rapyuta_io.clients.routed_network.RoutedNetworkLimits` - - :param limits: Values corresponding to limits of the parameters - :type limits: :py:class:`~rapyuta_io.clients.routed_network.RoutedNetworkLimits` - """ - - def __init__(self, limits = None): - super(ObjDict, self).__init__(limits=limits) - - diff --git a/rapyuta_io/clients/secret.py b/rapyuta_io/clients/secret.py deleted file mode 100644 index 4c45ffd7..00000000 --- a/rapyuta_io/clients/secret.py +++ /dev/null @@ -1,179 +0,0 @@ -from __future__ import absolute_import -import base64 -import json -import six -from abc import ABCMeta, abstractmethod -import re -import enum - -from rapyuta_io.utils import RestClient, InvalidParameterException -from rapyuta_io.utils.object_converter import ObjBase, enum_field -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.utils import create_auth_header, get_api_response_data - -DOCKER_HUB_REGISTRY = 'https://index.docker.io/v1/' - -secret_name_regex = re.compile('^[a-z0-9][a-z0-9-]+[a-z0-9]$') - - -class Secret(ObjBase): - """ - Secret is a resource that let's the IO Platform access private resources (Git or Docker Repositories) from - third-party providers. - - :ivar name: Name of the Secret - :ivar guid: GUID of the Secret - :ivar secret_type: Type of the Secret - :ivar created_at: Creation Time of the Secret - :ivar creator: Create of the Secret - :param name: Name of the Secret - :type name: str - :param secret_config: Secret Configuration - :type secret_config: Union[:py:class:`~rapyuta_io.clients.secret.SecretConfigDocker`] - """ - SECRET_PATH = '/api/secret' - - def __init__(self, name, secret_config): - self.validate(name, secret_config) - self.name = name - self._secret_config = secret_config - self.secret_type = secret_config.get_type() - self.guid = None - self.created_at = None - self.creator = None - - @staticmethod - def validate(name, secret_config): - if not isinstance(name, six.string_types): - raise InvalidParameterException('name must be a string') - length = len(name) - if length < 3 or length > 253: - raise InvalidParameterException('length of name must be between 3 and 253 characters') - if not secret_name_regex.match(name): - raise InvalidParameterException('name must consist of lower case alphanumeric characters or - and must ' + - 'start and end with an alphanumeric character') - if not isinstance(secret_config, _SecretConfigBase): - raise InvalidParameterException( - 'secret_config must be of type SourceSecretBasicConfig, SourceSecretSSHConfig or DockerSecretConfig') - - def get_deserialize_map(self): - return { - 'created_at': 'CreatedAt', - 'guid': 'guid', - 'name': 'name', - 'creator': 'creator', - 'project_guid': 'projectGUID', - 'secret_type': enum_field('type', SecretType), - } - - def get_serialize_map(self): - return { - 'type': 'secret_type', - 'name': 'name', - 'data': '_secret_config', - } - - def delete(self): - - """ - Delete the secret using the secret object. - - Following example demonstrates how to delete a secret using secret object: - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> secret = client.get_secret(guid='secret-id') - >>> secret.delete() - - """ - - if not (hasattr(self, '_core_api_host') and hasattr(self, '_auth_token')): - raise InvalidParameterException('Secret must be created first') - url = self._core_api_host + self.SECRET_PATH + '/delete' - headers = create_auth_header(self._auth_token, self._project) - payload = {'guid': self.guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - get_api_response_data(response, parse_full=True) - - -class SecretType(str, enum.Enum): - """ - SecretType is a Enum type defining different types of Secrets possible on the Platform. - - SecretType can be any of the following types \n - - SecretType.DOCKER \n - SecretType.SOURCE_BASIC_AUTH \n - SecretType.SOURCE_SSH_AUTH \n - """ - - def __str__(self): - return str(self.value) - - DOCKER = 'kubernetes.io/dockercfg' - - -class _SecretConfigBase(six.with_metaclass(ABCMeta, ObjBase)): - """ - SecretConfigBase is an abstract class that implements that defines abstract methods for all types of SecretConfig - classes. - """ - - @abstractmethod - def get_type(self): - pass - - def get_deserialize_map(self): - pass - - def get_serialize_map(self): - pass - - -class SecretConfigDocker(_SecretConfigBase): - """ - SecretConfigDocker represents Docker Secret for Docker registries. This type of secrets can be used to access - private Docker repositories for either pulling base images or pushing the images from Builds. - - :param username: Username for the Container Registry - :type username: str - :param password: Password/Token for the Container Registry - :type password: str - :param registry: The URL for the Container Registry, by default it uses DockerHub Registry - :type registry: str - """ - - def __init__(self, username, password, email, registry=DOCKER_HUB_REGISTRY): - self.validate(username, password, email, registry) - self.username = username - self.password = password - self.email = email - self.registry = registry - - @staticmethod - def validate(username, password, email, registry): - if not isinstance(username, six.string_types) or username == '': - raise InvalidParameterException('username cannot be empty') - if not isinstance(password, six.string_types) or password == '': - raise InvalidParameterException('password cannot be empty') - if not isinstance(email, six.string_types) or email == '': - raise InvalidParameterException('email cannot be empty') - if not isinstance(registry, six.string_types) or registry == '': - raise InvalidParameterException('registry cannot be empty') - - @classmethod - def get_type(cls): - return SecretType.DOCKER - - def serialize(self): - config = json.dumps({ - self.registry: { - 'username': self.username, - 'password': self.password, - 'email': self.email, - 'auth': base64.b64encode('{}:{}'.format(self.username, self.password).encode()).decode() - } - }) - return { - '.dockercfg': base64.b64encode(config.encode()).decode() - } diff --git a/rapyuta_io/clients/static_route.py b/rapyuta_io/clients/static_route.py deleted file mode 100644 index cf2dc594..00000000 --- a/rapyuta_io/clients/static_route.py +++ /dev/null @@ -1,49 +0,0 @@ -from __future__ import absolute_import -from rapyuta_io.utils import ObjDict -from rapyuta_io.utils.utils import create_auth_header, get_api_response_data -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils import RestClient - - -class StaticRoute(ObjDict): - """ - StaticRoute class represents an instance of a static route. It contains methods to delete static route. - - :ivar CreatedAt: Date of creation - :ivar DeletedAt: Date of deletion - :ivar ID: ID of the static route - :ivar creator: User guid who created the static route - :ivar metadata: Metadata associated with the static route - :ivar projectGUID: GUID of the project the static route is to be created in - :ivar urlPrefix: Prefix/subdomain of the static route - :ivar urlString: Full static route URL - """ - - STATIC_ROUTE_PATH = '/api/staticroute' - - def __init__(self, *args, **kwargs): - super(ObjDict, self).__init__(*args, **kwargs) - - def delete(self): - """ - Delete static route - - :return: True or False - :rtype: bool - - Following example demonstrates how to delete a static route - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> static_route = client.get_all_static_routes()[0] - >>> result = static_route.delete() - """ - url = self._core_api_host + self.STATIC_ROUTE_PATH + '/delete' - headers = create_auth_header(self._auth_token, self._project) - payload = {"guid": self.guid} - response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute(payload) - response_data = get_api_response_data(response, parse_full=True) - if response_data['success']: - self.clear() - return True - return False diff --git a/rapyuta_io/clients/validation_schema.py b/rapyuta_io/clients/validation_schema.py deleted file mode 100644 index 396ad049..00000000 --- a/rapyuta_io/clients/validation_schema.py +++ /dev/null @@ -1,44 +0,0 @@ -UPDATE_DEPLOYMENT_SCHEMA = { - "type": "object", - "properties": { - "deployment_id": {"type": "string", "minLength": 1}, - "service_id": {"type": "string", "minLength": 1}, - "plan_id": {"type": "string", "minLength": 1}, - "context": { - "type": "object", - "properties": { - "component_context": { - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "type": "object", - "properties": { - "update_deployment": {"type": "boolean"}, - "component": { - "type": "object", - "properties": { - "executables": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": {"type": "string", "minLength": 1}, - "name": {"type": "string", "minLength": 1} - }, - "required": ["id", "name", "docker"] - } - } - }, - "required": ["executables"] - } - }, - "required": ["update_deployment", "component"] - } - } - } - }, - "required": ["component_context"] - } - }, - "required": ["deployment_id", "service_id", "plan_id", "context"] -} diff --git a/rapyuta_io/rio_client.py b/rapyuta_io/rio_client.py index a4c9ec0e..e4b5bf7e 100644 --- a/rapyuta_io/rio_client.py +++ b/rapyuta_io/rio_client.py @@ -7,25 +7,18 @@ import six from rapyuta_io.clients import DeviceManagerClient, _ParamserverClient -from rapyuta_io.clients.build import Build, BuildStatus from rapyuta_io.clients.catalog_client import CatalogClient from rapyuta_io.clients.core_api_client import CoreAPIClient -from rapyuta_io.clients.deployment import Deployment from rapyuta_io.clients.device import Device from rapyuta_io.clients.metrics import ListMetricsRequest, ListTagKeysRequest, ListTagValuesRequest, Metric, \ MetricFunction, MetricOperation, QueryMetricsRequest, QueryMetricsResponse, Tags -from rapyuta_io.clients.native_network import NativeNetwork -from rapyuta_io.clients.package import Package, ROSDistro, RestartPolicy, Runtime -from rapyuta_io.clients.persistent_volumes import VolumeInstance -from rapyuta_io.clients.project import Project from rapyuta_io.clients.rip_client import AuthTokenLevel, RIPClient from rapyuta_io.clients.rosbag import ROSBagBlob, ROSBagBlobStatus, ROSBagJob, ROSBagJobStatus -from rapyuta_io.clients.routed_network import Parameters, RoutedNetwork -from rapyuta_io.clients.secret import Secret from rapyuta_io.clients.user_group import UserGroup -from rapyuta_io.utils import InvalidAuthTokenException, InvalidParameterException, to_objdict -from rapyuta_io.utils.settings import VOLUME_PACKAGE_ID, default_host_config -from rapyuta_io.utils.utils import get_api_response_data, valid_list_elements +from rapyuta_io.utils import InvalidAuthTokenException, \ + InvalidParameterException +from rapyuta_io.utils.settings import default_host_config +from rapyuta_io.utils.utils import valid_list_elements class Client(object): @@ -104,252 +97,12 @@ def set_project(self, project_guid): :param project_guid: GUID of the Project - Following example demonstrates how to set the Project for the Client. - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.project import Project - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> project = client.create_project(Project('secret-guid')) - >>> client.set_project(project.guid) - >>> persistent_volume = client.get_persistent_volume() - """ self._catalog_client.set_project(project_guid) self._core_api_client.set_project(project_guid) self._dmClient.set_project(project_guid) self._paramserver_client.set_project(project_guid) - def get_persistent_volume(self, retry_limit=0): - """ - Get persistent volume class - - :param retry_limit: Number of retry attempts to be carried out if any failures occurs during the API call. - :type retry_limit: int - :return: :py:class:`PersistentVolumes` class. - :raises: :py:class:`APIError`: If the API returns an error, a status code - of anything other than 200/201 is returned. - - Following example demonstrates how to get a persistent volume - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> persistent_volume = client.get_persistent_volume() - - """ - volume = self._catalog_client.get_package(VOLUME_PACKAGE_ID, retry_limit) - self._add_auth_token(volume) - return volume - - def get_package(self, package_id, retry_limit=0): - - """ - Get package class - - :param package_id: Package ID - :type package_id: string - :param retry_limit: Number of retry attempts to be carried out if any failures occurs \ - during the API call. - :type retry_limit: int - :return: an instance of :py:class:`~clients.catalog.Package` class. - :raises: :py:class:`PackageNotFound`: If the given package id is not found. - :raises: :py:class:`ComponentNotFoundException`: If the plan inside the package does - not have components. - :raises: :py:class:`APIError`: If the API returns an error, a status code - of anything other than 200/201 is returned. - - - Following example demonstrates how to get the packages - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> package = client.get_package('test_package_id') - - """ - - pkg = self._catalog_client.get_package(package_id, retry_limit) - self._add_auth_token(pkg) - return pkg - - def get_all_packages(self, retry_limit=0, name=None, version=None): - - """ - Get list of all packages created by the user. - - :param retry_limit: Number of retry attempts to be carried out if any failures occurs \ - during the API call. - :type retry_limit: int - :param name: Optional parameter to filter the packages based on substring of the package name. - :type name: str - :param version: Optional parameter to filter the packages based on the version of the package. - :type version: str - :return: List of all packages. Each package is an instance of - :py:class:`~clients.catalog.Package` class. - :raises: :py:class:`APIError`: If the API returns an error, a status code - of anything other than 200/201 is returned. - - - Following example demonstrates how to get the packages - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> package_list = client.get_all_packages() - >>> pacakge_list_filter_by_name = client.get_all_packages(name='test-name') - >>> pacakge_list_filter_by_version = client.get_all_packages(version='v0.0.0') - - """ - - if name and not isinstance(name, six.string_types): - raise InvalidParameterException('name must be a string') - if version and not isinstance(version, six.string_types): - raise InvalidParameterException('version must be a string') - package_list = self._catalog_client.get_all_packages(retry_limit) - packages = list() - for package in package_list['services']: - if package['id'] == VOLUME_PACKAGE_ID: - continue - package = Package(to_objdict(package)) - self._add_auth_token(package) - packages.append(package) - if name: - packages = [p for p in packages if p.packageName.find(name) != -1] - if version: - packages = [p for p in packages if p.packageVersion == version] - return packages - - def delete_package(self, package_id): - - """ - Delete a package. - - :param package_id: ID of the package to be deleted. - :type package_id: str - - Following example demonstrates how to delete a package. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.delete_package('package_id') - - """ - - if not (isinstance(package_id, str) or isinstance(package_id, six.string_types)) or package_id == '': - raise InvalidParameterException("package_id must be a non-empty string") - response = self._catalog_client.delete_package(package_id) - get_api_response_data(response, parse_full=True) - - def get_deployment(self, deployment_id, retry_limit=0): - """ - Get a deployment - - :param deployment_id: Deployment ID - :type deployment_id: string - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: instance of class :py:class:`Deployment`: - :raises: :py:class:`APIError`: If the API returns an error, a status code of - anything other than 200/201 is returned. - - - - Following example demonstrates how to get all the deployments. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> deployment = client.get_deployment('deployment_id') - - """ - deployment = self._catalog_client.get_deployment(deployment_id, retry_limit) - deployment = Deployment(to_objdict(deployment)) - self._add_auth_token(deployment) - deployment.is_partial = False - return deployment - - def get_volume_instance(self, instance_id, retry_limit=0): - """ - Get a volume instance - - :param instance_id: Volume instance Id - :type instance_id: string - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: instance of class :py:class:`VolumeInstance`: - :raises: :py:class:`APIError`: If the API returns an error, a status code - of anything other than 200/201 is returned. - - Following example demonstrates how to get a volume instance. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> deployments = client.get_volume_instance('instance_id') - - """ - volume_instance = self._catalog_client.get_deployment(instance_id, retry_limit) - volume_instance = VolumeInstance(to_objdict(volume_instance)) - self._add_auth_token(volume_instance) - volume_instance.is_partial = False - return volume_instance - - def get_all_deployments(self, phases=None, device_id='', retry_limit=0): - """ - Get all deployments created by the user. - - :param phases: optional parameter to filter out the deployments based on current deployment - :type phases: list(DeploymentPhaseConstants) - :param device_id: optional parameter to filter out the deployments based on uid of the corresponding device - :type device_id: str - :param retry_limit: Optional parameter to specify the number of retry attempts to be - carried out if any failures occurs during the API call. - :type retry_limit: int - :return: list of instances of class :py:class:`Deployment`: - :raises: :py:class:`APIError`: If the API returns an error, a status code - of anything other than 200/201 is returned. - - Following example demonstrates how to get all the deployments. - - >>> from rapyuta_io import Client, DeploymentPhaseConstants - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> deployments = client.get_all_deployments() - >>> deployments_list_filtered_by_phase = client.get_all_deployments(phases= - >>> [DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING]) - >>> deployments_list_filtered_by_device_id = client.get_all_deployments( - >>> device_id='') - - """ - if not isinstance(device_id, six.string_types): - raise InvalidParameterException('invalid deviceID') - - deployment_list = self._catalog_client.deployment_list(phases, device_id, retry_limit) - deployments = list() - for deployment in deployment_list: - if deployment['packageId'] == VOLUME_PACKAGE_ID: - continue - deployment_object = Deployment(to_objdict(deployment)) - # todo: remove volume instances - self._add_auth_token(deployment_object) - deployments.append(deployment_object) - return deployments - - def update_deployment(self, payload, retry_limit=0): - """ - Update a deployment - - :type payload: json - :type retry_limit: int - :return: list of instances of class :py:class:`Deployment`: - :raises: :py:class:`APIError`: If the API returns an error, a status code - of anything other than 200/201 is returned. - - The following example demonstrates how to update a deployment - - >>> from rapyuta_io import Client, DeploymentPhaseConstants - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> status = client.update_deployment(payload) - - """ - return self._catalog_client.update_deployment(payload, retry_limit) - def get_authenticated_user(self): """ Get details for authenticated User. @@ -470,7 +223,7 @@ def delete_device(self, device_id): Following example demonstrates how to delete a device. - >>> from rapyuta_io import Client, ROSDistro + >>> from rapyuta_io import Client >>> client = Client(auth_token='auth_token', project='project_guid') >>> client.delete_device('device-id') """ @@ -510,86 +263,6 @@ def toggle_features(self, device_id, features, config=None): return self._dmClient.patch_daemons(device_id, data) - def create_package_from_manifest(self, manifest_filepath, retry_limit=0): - """ - Create package from a manifest file - - :param manifest_filepath: File path of the manifest - :type manifest_filepath: string - :param retry_limit: No of retry attempts to be carried out if any failures occurs\ - during the API call. - :type retry_limit: int - :raises: :py:class:`~utils.error.ConflictError`: Package already exists. - :raises: :py:class:`~utils.error.BadRequestError`: Invalid package details. - :return: dict containing package details - - **Copy the below json to listener.json file** \n - Following example demonstrates how to use create_package_from_manifest. - - .. code-block:: JSON - - { "packageVersion": "v1.0.0", "plans": [{"singleton": false, - "name": "default", "inboundROSInterfaces": {"services": [], "topics": [], - "actions": []}, "dependentDeployments": [], "components": [{"executables": [{"cmd": - ["roslaunch listener listener.launch"], "name": "listenerExec"}], - "name": "default", "parameters": [], "architecture": "arm32v7", "requiredRuntime": - "device", "ros": {"services": [], "topics": [], "isROS": true, "actions": []}, - "description": ""}], "exposedParameters": [], "metadata": {}}], "name": "listener", - "apiVersion": "v1.0.0", "description": "listener arm32v7 sdk test package" } - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> package_details = client.create_package_from_manifest('listener.json') - - """ - manifest = self._get_manifest_from_file(manifest_filepath) - return self._catalog_client.create_package(manifest, retry_limit) - - @staticmethod - def _get_manifest_from_file(manifest_filepath): - with open(manifest_filepath, 'r') as f: - return json.load(f) - - def create_package(self, manifest, retry_limit=0): - """ - Create package from manifest dict. - - :param manifest: dict containing package details - :type manifest: dict - :param retry_limit: No of retry attempts to be carried out if any failures occurs\ - during the API call. - :type retry_limit: int - :raises: :py:class:`~utils.error.ConflictError`: Package already exists. - :raises: :py:class:`~utils.error.BadRequestError`: Invalid package details. - :return: dict containing package details - - Following example demonstrates how to use create_package. - - >>> manifest = {'packageVersion': 'v1.0.0', - 'plans': ['singleton': False, - 'name': 'default', - 'inboundROSInterfaces': {'services': [], 'topics': [], 'actions': []}, - 'dependentDeployments': [], - 'components': [{'executables': [{'cmd': ['roslaunch listener listener.launch'], - 'name': 'listenerExec'}], - 'name': 'default', - 'parameters': [], - 'architecture': 'arm32v7', - 'requiredRuntime': 'device', - 'ros': {'services': [], 'topics': [], 'isROS': True, 'actions': []}, - 'description': ''}], - 'exposedParameters': [], - 'metadata': {}}], - 'name': 'listener', - 'apiVersion': 'v1.0.0', - 'description': 'listener arm32v7 sdk test package'} - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> package_details = client.create_package(manifest) - - """ - return self._catalog_client.create_package(manifest, retry_limit) - def upload_configurations(self, rootdir, tree_names=None, delete_existing_trees=False, as_folder=False): """ Traverses rootdir and uploads configurations following the same directory structure. @@ -681,408 +354,6 @@ def apply_parameters(self, device_list, tree_names=None, retry_limit=0): """ return self._dmClient.apply_parameters(device_list, tree_names, retry_limit) - def get_all_static_routes(self): - """ - List all static routes in a project. - - :return: Instances of :py:class:`~StaticRoute` class. - - Following example demonstrates how to list all static routes - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> static_routes = client.get_all_static_routes() - """ - return self._core_api_client.get_all_static_routes() - - def get_static_route(self, route_guid): - """ - Get static routes by its guid - - :param route_guid: GUID string of a :py:class:`~StaticRoute` class. - :type route_guid: str - :return: Instance of :py:class:`~StaticRoute` class. - - Following example demonstrates how to get a static route - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> static_route_guid = client.get_all_static_routes()[0]['guid'] - >>> static_route = client.get_static_route(static_route_guid) - """ - return self._core_api_client.get_static_route(route_guid) - - def get_static_route_by_name(self, name): - """ - Get static routes by its name - - :param name: Name (urlPrefix) of the :py:class:`~StaticRoute` instance. - :type name: str - :return: Instance of :py:class:`~StaticRoute` class or None if it doesn't exist - - Following example demonstrates how to get a static route by its name/url prefix - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> static_route = client.get_static_route_by_name('example-route') - """ - return self._core_api_client.get_static_route_by_name(name) - - def create_static_route(self, name): - """ - Create static route of a certain name - - :param name: Name of the static route. It should follow ^[a-z][a-z0-9-]*$ and should not contain black listed keyword and be of length between 4 and 64 - :type name: str - :return: Instance of :py:class:`~StaticRoute` class. - - Following example demonstrates how to create a static route. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> static_route = client.create_static_route('example-route') - """ - return self._core_api_client.create_static_route(name) - - def delete_static_route(self, route_guid): - """ - Delete static route by its guid - - :param route_guid: GUID string of a :py:class:`~StaticRoute` class. - :type route_guid: str - - Following example demonstrates how to delete a static route - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> static_route_guid = client.get_all_static_routes()[0]['guid'] - >>> client.delete_static_route(static_route_guid) - """ - return self._core_api_client.delete_static_route(route_guid) - - def get_routed_network(self, network_guid): - """ - Get routed network for the guid - - :param network_guid: guid of routed network - :type network_guid: str - :return: Instance of :py:class:`~rapyuta_io.clients.routed_network.RoutedNetwork` class. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> routed_network = client.get_routed_network(network_guid) - - """ - routed_network = self._catalog_client.get_routed_network(network_guid) - routed_network = RoutedNetwork(to_objdict(routed_network)) - routed_network.phase = routed_network.internalDeploymentStatus.phase - routed_network.status = routed_network.internalDeploymentStatus.status - routed_network.error_code = routed_network.get_error_code() - self._add_auth_token(routed_network) - routed_network.is_partial = False - return routed_network - - def get_all_routed_networks(self): - """ - List routed network - - :return: List instance of :py:class:`~rapyuta_io.clients.routed_network.RoutedNetwork` class. - """ - - routed_networks = [] - networks = self._catalog_client.list_routed_network() - for routed_network in networks: - routed_network = RoutedNetwork(to_objdict(routed_network)) - internal_deployment_status = routed_network.internalDeploymentStatus - routed_network.phase = internal_deployment_status.phase - routed_network.error_code = routed_network.get_error_code() - self._add_auth_token(routed_network) - routed_networks.append(routed_network) - return routed_networks - - def delete_routed_network(self, network_guid): - - """ - Delete a routed network using its network_guid - - :param network_guid: Routed Network GUID - :type network_guid: str - - Following example demonstrates how to delete a routed network under a project - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.delete_routed_network('network_guid') - - """ - if not network_guid or not isinstance(network_guid, six.string_types): - raise InvalidParameterException('guid needs to be a non empty string') - self._catalog_client.delete_routed_network(network_guid) - - def create_cloud_routed_network(self, name, ros_distro, shared, parameters=None): - - """ - Create a routed network - - :param name: Name of the routed network. - :type name: str - :param ros_distro: ros ditro of the runtime. - :type ros_distro: enum :py:class:`~rapyuta_io.clients.package.ROSDistro` - :param shared: Whether the network should be shared. - :type shared: bool - :param parameters: parameters of the routed network - :type parameters: :py:class:`~rapyuta_io.clients.routed_network.Parameters` - :return: Instance of :py:class:`~rapyuta_io.clients.routed_network.RoutedNetwork` class. - - Following example demonstrates how to create a routed network. - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.package import ROSDistro - >>> from rapyuta_io.clients.routed_network import Parameters, RoutedNetworkLimits - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> parameters = Parameters(RoutedNetworkLimits.SMALL) - >>> routed_network = client.create_cloud_routed_network('network_name', ROSDistro.KINETIC, True, - ... parameters=parameters) - """ - if ros_distro not in list(ROSDistro.__members__.values()): - raise InvalidParameterException('ROSDistro must be one of rapyuta_io.clients.package.ROSDistro') - - if parameters and not isinstance(parameters, Parameters): - raise InvalidParameterException('parameters must be of type rapyuta_io.clients.routed_network.Parameters') - - parameters = parameters.to_dict() if parameters else {} - - routed_network = self._catalog_client.create_routed_network(name=name, runtime=Runtime.CLOUD, - rosDistro=ros_distro, - shared=shared, parameters=parameters) - routed_network = RoutedNetwork(to_objdict(routed_network)) - return self.get_routed_network(routed_network.guid) - - def create_device_routed_network(self, name, ros_distro, shared, - device, network_interface, restart_policy=RestartPolicy.Always): - - """ - Create a routed network - - :param name: Name of the routed network. - :type name: str - :param ros_distro: ros ditro of the runtime. - :type ros_distro: enum :py:class:`~rapyuta_io.clients.package.ROSDistro` - :param shared: Whether the network should be shared. - :type shared: bool - :param device: device on which the routed network is deployed. - :type device: Instance of :py:class:`~Device` class. - :param network_interface: network interface to which routed network is binded. - :type network_interface: str - :param restart_policy: restart policy of routed network. - :type restart_policy: enum :py:class:`~rapyuta_io.clients.package.RestartPolicy` - :return: Instance of :py:class:`~rapyuta_io.clients.routed_network.RoutedNetwork` class. - - Following example demonstrates how to create a routed network. - - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.package import ROSDistro - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> routed_network = client.create_device_routed_network('network_name', - >>> ROSDistro.KINETIC, True) - """ - - parameters = {} - if ros_distro not in list(ROSDistro.__members__.values()): - raise InvalidParameterException('ROSDistro must be one of rapyuta_io.clients.package.ROSDistro') - - if not isinstance(device, Device): - raise InvalidParameterException('device must be of type rapyuta_io.clients.device.Device') - - ip_interfaces = device.ip_interfaces or {} - if network_interface not in list(ip_interfaces.keys()): - raise InvalidParameterException('NETWORK_INTERFACE should be in {}'.format(list(ip_interfaces.keys()))) - - if restart_policy not in list(RestartPolicy.__members__.values()): - raise InvalidParameterException('RestartPolicy must be one of rapyuta_io.clients.package.RestartPolicy') - - parameters['device_id'] = device.uuid - parameters['NETWORK_INTERFACE'] = network_interface - parameters['restart_policy'] = restart_policy - - routed_network = self._catalog_client.create_routed_network(name=name, runtime=Runtime.DEVICE, - rosDistro=ros_distro, - shared=shared, parameters=parameters) - routed_network = RoutedNetwork(to_objdict(routed_network)) - return self.get_routed_network(routed_network.guid) - - def create_build(self, build, refresh=True): - - """ - Create a new build - - :param build: Info about the build to be created - :type build: :py:class:`~rapyuta_io.clients.build.Build` - :param refresh: Whether the build needs to be refreshed - :type refresh: bool - :return: Instance of :py:class:`~rapyuta_io.clients.build.Build` class. - - Following example demonstrates how to create a build. - - >>> from rapyuta_io import Client, ROSDistro, Build, SimulationOptions, BuildOptions, CatkinOption, GithubWebhook - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> simulationOptions = SimulationOptions(False) - >>> buildOptions = BuildOptions(catkinOptions=[CatkinOption(rosPkgs='talker')]) - >>> webhooks = [GithubWebhook(workflowName='test.yaml', accessToken='github_access_token')] - >>> build = Build(buildName='test-build', - ... strategyType='Source', - ... repository='https://github.com/rapyuta-robotics/io_tutorials.git', - ... architecture='amd64', - ... rosDistro='melodic', - ... isRos=True, - ... contextDir='talk/talker', - ... simulationOptions=simulationOptions, - ... buildOptions=buildOptions, - ... buildWebhooks=webhooks) - >>> build = client.create_build(build) - >>> build.poll_build_till_ready() - - """ - if not isinstance(build, Build): - raise InvalidParameterException("build must be non-empty and of type " - "rapyuta_io.clients.build.Build") - response = self._catalog_client.create_build(build) - build['guid'] = response.get('guid') - self._add_auth_token(build) - if refresh: - build.refresh() - return build - - def get_build(self, guid, include_build_requests=False): - - """ - Get a build based on the guid. - - :param guid: GUID of the build - :type guid: str - :param include_build_requests: Whether to include build request in the response - :type include_build_requests: bool - :return: Instance of :py:class:`~rapyuta_io.clients.build.Build` class. - - Following example demonstrates how to get a build. - - 1. Get build without including build requests. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.get_build('build-guid') - - 2. Get build including the build requests. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.get_build('build-guid', include_build_requests=True) - - """ - if not isinstance(include_build_requests, bool): - raise InvalidParameterException('include_build_requests must be of bool type') - build = self._catalog_client.get_build(guid, include_build_requests) - build = Build._deserialize(build) - self._add_auth_token(build) - build.is_partial = False - return build - - def list_builds(self, statuses=None): - - """ - List builds based on the passed query params - - :param statuses: statuses based on which the list build response will be filtered. - :type statuses: list(:py:class:`~rapyuta_io.clients.build.BuildStatus`) - :return: list(:py:class:`~rapyuta_io.clients.build.Build`) - - Following example demonstrates how to list builds. - - 1. List all builds present in the project. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> build = client.list_builds() - - 2. List builds based on their statuses. - - >>> from rapyuta_io import Client, BuildStatus - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> builds = client.list_builds(statuses=[BuildStatus.COMPLETE, BuildStatus.BUILD_FAILED, - ... BuildStatus.BUILD_IN_PROGRESS]) - """ - if statuses is not None: - BuildStatus.validate(statuses) - builds = self._catalog_client.list_builds(statuses) - build_list = [] - for build in builds: - build = Build._deserialize(build) - self._add_auth_token(build) - build_list.append(build) - return build_list - - def delete_build(self, guid): - - """ - Delete a build. - - :param guid: GUID of the build to be deleted - :type guid: str - - Following example demonstrates how to delete a build. - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.delete_build('build-guid') - """ - response = self._catalog_client.delete_build(guid=guid) - get_api_response_data(response, parse_full=True) - - def trigger_build(self, buildOperation): - - """ - Trigger a new build request for a particular build - - :param buildOperation: Info of the operation to be performed on the build. - :type buildOperation: :py:class:`~rapyuta_io.clients.buildoperation.BuildOperation` - - Following example demonstrates how to trigger a new build request for a build: - - >>> from rapyuta_io import Client, BuildOperationInfo, BuildOperation - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> request = BuildOperation([BuildOperationInfo('build-guid', triggerName='trigger-name')]) - >>> response = client.trigger_build(request) - >>> for resp in response['buildOperationResponse']: - ... if not resp['success']: - ... print resp['buildGUID'], resp['error'] - ... else: - ... print resp['buildGUID'], resp['buildGenerationNumber'] - - """ - return self._catalog_client.trigger_build(buildOperation=buildOperation) - - def rollback_build(self, buildOperation): - - """ - Rollback the build to a previously created build request - - :param buildOperation: Info of the operation to be performed on the build. - :type buildOperation: :py:class:`~rapyuta_io.clients.buildoperation.BuildOperation` - - Following example demonstrates how to rollback a build: - - >>> from rapyuta_io import Client, BuildOperationInfo, BuildOperation - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> request = BuildOperation([BuildOperationInfo('build-guid', 1)]) - >>> response = client.rollback_build(request) - >>> for resp in response['buildOperationResponse']: - ... if not resp['success']: - ... print resp['buildGUID'], resp['error'] - ... else: - ... print resp['buildGUID'], resp['buildGenerationNumber'] - - """ - return self._catalog_client.rollback_build(buildOperation=buildOperation) - def get_rosbag_job(self, guid): """ Get ROSBag Job @@ -1118,10 +389,8 @@ def create_rosbag_job(self, rosbag_job): >>> from rapyuta_io import Client >>> from rapyuta_io.clients.rosbag import ROSBagJob, ROSBagOptions >>> client = Client(auth_token='auth_token', project='project_guid') - >>> deployment = client.get_deployment('deployment_id') - >>> component_instance_id = deployment.get_component_instance_id('comp-name') >>> rosbag_options = ROSBagOptions(all_topics=True) - >>> rosbag_job = ROSBagJob(deployment_id=deployment.deploymentId, + >>> rosbag_job = ROSBagJob(deployment_id="deployment-id", ... component_instance_id=component_instance_id, ... rosbag_options=rosbag_options, name='name') >>> rosbag_job = client.create_rosbag_job(rosbag_job) @@ -1390,295 +659,6 @@ def delete_rosbag_blob(self, guid): raise InvalidParameterException('guid needs to non empty string') self._catalog_client.delete_rosbag_blob(guid) - def create_project(self, project): - """ - Create a new Project - - :param project: Project object - :type project: :py:class:`~rapyuta_io.clients.project.Project` - :rtype: :py:class:`~rapyuta_io.clients.project.Project` - - Following example demonstrates the use of this method for creating a new Project. - - >>> from rapyuta_io.clients.project import Project - >>> client = Client(auth_token='auth_token') - >>> proj = Project('project-name') - >>> client.create_project(proj) - - Following example demonstrates the use of this method for creating a new Project in a different organization. - Please do note that the user should be a part of the organization where the project is to be created. - - >>> from rapyuta_io.clients.project import Project - >>> client = Client(auth_token='auth_token') - >>> proj = Project('project-name', 'org-guid') - >>> client.create_project(proj) - - """ - if not isinstance(project, Project): - raise InvalidParameterException("project must be non-empty and of type " - "rapyuta_io.clients.project.Project") - return self._core_api_client.create_project(project) - - def get_project(self, guid): - """ - Get a Project from its GUID. - - :param guid: Project's GUID - :type guid: str - :rtype: :py:class:`~rapyuta_io.clients.project.Project` - - Following example demonstrates how a Project can be fetched using this method. - - >>> client = Client(auth_token='auth_token') - >>> client.get_project('project-guid') - - """ - return self._core_api_client.get_project(guid) - - def list_projects(self): - """ - Get a list of all the Projects. - - :return: A list of all available Projects for the user. - :rtype: list(:py:class:`~rapyuta_io.clients.project.Project`) - - Following example demonstrates how to fetch the list of all Projects. - - >>> client = Client(auth_token='auth_token') - >>> client.list_projects() - - """ - return self._core_api_client.list_projects() - - def delete_project(self, guid): - """ - Delete a Project from the platform. - - :param guid: Project's GUID - :type guid: str - - Following example demonstrates how to delete a Project using this method. - - >>> client = Client(auth_token='auth_token') - >>> client.delete_project('project-guid') - - """ - return self._core_api_client.delete_project(guid) - - def add_user_to_project(self, project_guid, user_guid): - - """ - Creator of a Project can add a User belonging to the same Organization into the Project. - - :param project_guid: Project's GUID - :type project_guid: str - :param user_guid: User's GUID - :type user_guid: str - - Following example demonstrates how to add a User to a Project using this method. - - >>> client = Client(auth_token='auth_token') - >>> client.add_user_to_project(project_guid='project_guid', user_guid='user_guid') - - """ - return self._core_api_client.add_user_to_project(project_guid=project_guid, user_guid=user_guid) - - def remove_user_from_project(self, project_guid, user_guid): - - """ - Creator of a Project can remove a User from the Project. - - :param project_guid: Project's GUID - :type project_guid: str - :param user_guid: User's GUID - :type user_guid: str - - Following example demonstrates how to remove a User from a Project using this method. - - >>> client = Client(auth_token='auth_token') - >>> client.remove_user_from_project(project_guid='project_guid', user_guid='user_guid') - - """ - return self._core_api_client.remove_user_from_project(project_guid=project_guid, user_guid=user_guid) - - def create_secret(self, secret): - """ - Create a new Secret on the Platform under the project. - - :param secret: Secret object - :type secret: :py:class:`~rapyuta_io.clients.secret.Secret` - :rtype: :py:class:`~rapyuta_io.clients.secret.Secret` - - Following example demonstrates the use of this method for creating a new Secret. - - >>> from rapyuta_io.clients.secret import Secret, SecretConfigSourceBasicAuth - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> secret_config = SecretConfigSourceBasicAuth('user', 'password') - >>> secret = Secret('secret-name', secret_config) - >>> client.create_secret(secret) - - """ - if not isinstance(secret, Secret): - raise InvalidParameterException("secret must be non-empty and of type " - "rapyuta_io.clients.secret.Secret") - return self._core_api_client.create_secret(secret) - - def list_secrets(self): - """ - List all the Secrets under the Project on the Platform. - - :return: A list of all available Secrets under the selected Project. - :rtype: list(:py:class:`~rapyuta_io.clients.secret.Secret`) - - Following example demonstrates how to fetch the list of all Secrets. - - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.list_secrets() - - """ - return self._core_api_client.list_secrets() - - def get_secret(self, guid): - """ - Get a Secret using its GUID. - - :param guid: Secret's GUID - :type guid: str - :rtype: :py:class:`~rapyuta_io.clients.secret.Secret` - - Following example demonstrates how a Secret can be fetched using this method. - - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.get_secret('secret-guid') - - """ - return self._core_api_client.get_secret(guid) - - def update_secret(self, guid, secret): - """ - Update an existing Secret on the Platform under the project. - - :param guid: Secret's GUID - :type guid: str - :param secret: Secret object - :type secret: :py:class:`~rapyuta_io.clients.secret.Secret` - :rtype: :py:class:`~rapyuta_io.clients.secret.Secret` - - Following example demonstrates the use of this method for creating a new Secret. - - >>> from rapyuta_io.clients.secret import Secret, SecretConfigSourceBasicAuth - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> secret_config = SecretConfigSourceBasicAuth('user', 'new_password') - >>> secret = Secret('secret-name', secret_config) - >>> client.update_secret('secret-guid', secret) - - """ - if not isinstance(secret, Secret): - raise InvalidParameterException("secret must be non-empty and of type " - "rapyuta_io.clients.secret.Secret") - return self._core_api_client.update_secret(guid, secret) - - def delete_secret(self, guid): - """ - Delete a secret using its GUID. - - :param guid: Project's GUID - :type guid: str - - Following example demonstrates how to delete a Secret using this method. - - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.delete_secret('secret-guid') - - """ - return self._core_api_client.delete_secret(guid) - - def get_native_network(self, network_guid): - """ - Get a native network using its network_guid - - :param network_guid: native network GUID - :type network_guid: str - :rtype: :py:class:`~rapyuta_io.clients.native_network.NativeNetwork` - - Following example demonstrates how a native network can be fetched using this method - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> native_network = client.get_native_network('network_guid') - """ - if not network_guid or not isinstance(network_guid, six.string_types): - raise InvalidParameterException('guid needs to be a non empty string') - - native_network = self._catalog_client.get_native_network(network_guid) - native_network = NativeNetwork.deserialize(native_network) - self._add_auth_token(native_network) - native_network.is_partial = False - return native_network - - def list_native_networks(self): - """ - Lists all the native networks under a project - - :return: A list of all available native networks under the Project. - :rtype: List(:py:class:`~rapyuta_io.clients.native_network.NativeNetwork`) - - Following example demonstartes how to list all the native networks under a project - - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> native_networks = client.list_native_networks() - """ - native_networks = [] - networks = self._catalog_client.list_native_network() - for native_network in networks: - native_network = NativeNetwork.deserialize(native_network) - self._add_auth_token(native_network) - native_networks.append(native_network) - return native_networks - - def create_native_network(self, native_network): - """ - Creates a new native network - - :param native_network: Native Network object - :type native_network: :py:class:`~rapyuta_io.clients.native_network.NativeNetwork` - :rtype: :py:class:`~rapyuta_io.clients.native_network.NativeNetwork` - - Following example demonstrates how to create a native network under a project - >>> from rapyuta_io import Client - >>> from rapyuta_io.clients.native_network import NativeNetwork,Parameters,NativeNetworkLimits - >>> from rapyuta_io.clients.package import Runtime, ROSDistro - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> parameters = Parameters(NativeNetworkLimits.SMALL) - >>> native_network = NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.KINETIC, - ... parameters=parameters) - >>> native_network = client.create_native_network(native_network) - """ - if not isinstance(native_network, NativeNetwork): - raise InvalidParameterException("native_network must be non-empty and of type " - "rapyuta_io.clients.native_network.NativeNetwork") - native_network_response = self._catalog_client.create_native_network(native_network) - return self.get_native_network(native_network_response['guid']) - - def delete_native_network(self, network_guid): - - """ - Delete a native network using its network_guid - - :param network_guid: Native Network GUID - :type network_guid: str - - Following example demonstrates how to delete a native network under a project - >>> from rapyuta_io import Client - >>> client = Client(auth_token='auth_token', project='project_guid') - >>> client.delete_native_network('network_guid') - - """ - if not network_guid or not isinstance(network_guid, six.string_types): - raise InvalidParameterException('guid needs to be a non empty string') - self._catalog_client.delete_native_network(network_guid) - def query_metrics(self, query_metrics_request): """ Query and fetch metrics diff --git a/rapyuta_io/utils/settings.py b/rapyuta_io/utils/settings.py index 935ca718..9608135b 100644 --- a/rapyuta_io/utils/settings.py +++ b/rapyuta_io/utils/settings.py @@ -3,7 +3,7 @@ default_host_config = { "core_api_host": "https://gaapiserver.apps.okd4v2.prod.rapyuta.io", "catalog_host": "https://gacatalog.apps.okd4v2.prod.rapyuta.io", - "rip_host": "https://garip.apps.okd4v2.prod.rapyuta.io" + "rip_host": "https://garip.apps.okd4v2.prod.rapyuta.io", } # Paramserver APIs diff --git a/sdk_test/config.json.example b/sdk_test/config.json.example index 968932c3..9c567aaa 100644 --- a/sdk_test/config.json.example +++ b/sdk_test/config.json.example @@ -1,6 +1,7 @@ { "catalog_host": "https://v14catalog.az39.rapyuta.io", "core_api_host": "https://qaapiserver.az39.rapyuta.io", + "v2_api_host": "https://qaapi.rapyuta.io", "hwil_host": "https://hwil.rapyuta.io", "hwil_user": "USER", "hwil_password": "PASSWORD", @@ -8,21 +9,13 @@ "organization_guid": "org_guid", "devices": [ { - "name": "NAME", - "runtime": "RUNTIME", - "ip": "IP_ADDRESS", - "arch": "ARCHITECTURE", - "distro": "ROS_DISTRIBUTION" + "name": "docker-compose-amd64", + "runtime": "Dockercompose", + "ip": "10.224.4.5", + "arch": "amd64", + "distro": "melodic" } ], - "git": { - "ssh-key": "SSH_KEY" - }, - "docker": { - "username": "DOCKER_USERNAME", - "password": "DOCKER_PASSWORD", - "email": "DOCKER_EMAIL" - }, "test_files": [], "worker_threads": 1 } diff --git a/sdk_test/config.py b/sdk_test/config.py index d8be9335..a9db85cf 100644 --- a/sdk_test/config.py +++ b/sdk_test/config.py @@ -6,11 +6,12 @@ import six from six.moves import filter -from rapyuta_io import Client, SecretConfigDocker, \ - DeviceArch, Secret, Project +from rapyuta_io import Client, DeviceArch from rapyuta_io.utils.error import InvalidParameterException from rapyuta_io.utils.utils import create_auth_header, \ prepend_bearer_to_auth_token, generate_random_value +from sdk_test.util import get_logger +from sdk_test.v2_client import V2Client class _Singleton(type): @@ -62,15 +63,7 @@ class Configuration(six.with_metaclass(_Singleton, object)): "arch": "ARCHITECTURE", "distro": "ROS_DISTRO" } - ], - "git": { - "ssh-key": "SSH_KEY" - }, - "docker": { - "username": "DOCKER_USERNAME", - "password": "DOCKER_PASSWORD", - "email": "DOCKER_EMAIL" - } + ] } """ @@ -86,16 +79,17 @@ def __init__(self, file_path=None): self.catalog_server = self._config['catalog_host'] self.api_server = self._config['core_api_host'] self.hwil_server = self._config['hwil_host'] - self._project = None + self.v2_server = self._config['v2_api_host'] + self._project_guid = None self.test_files = self._config['test_files'] if not isinstance(self.test_files, list): raise InvalidParameterException('test_files must be a list of test file names') self.worker_threads = self._config['worker_threads'] self.organization_guid = self._config.get('organization_guid') + self.logger = get_logger() + self.v2_client = V2Client(self._config['auth_token'], self.v2_server) def validate(self): - # if len(self.get_device_configs(arch=DeviceArch.AMD64, runtime='Preinstalled')) != 1: - # raise InvalidConfig('One amd64 device with Preinstalled runtime is required') if len(self.get_device_configs(arch=DeviceArch.AMD64, runtime='Dockercompose')) != 1: raise InvalidConfig('One amd64 device with Docker Compose runtime is required') @@ -112,14 +106,19 @@ def get_auth_token(self): def create_project(self): # Project name needs to be between 3 and 15 Characters name = 'test-{}'.format(generate_random_value(8)) - self._project = self.client.create_project( - Project(name, organization_guid=self.organization_guid)) - self.set_project(self._project.guid) + self._project_guid = self.v2_client.create_project({ + 'metadata': { + 'name': name, + 'organizationGUID': self.organization_guid + }, + }) + self.logger.info('Created project: {}'.format(name)) + self.set_project(self._project_guid) def delete_project(self): - if self._project is None: + if self._project_guid is None: return - self._project.delete() + self.v2_client.delete_project(self._project_guid) def set_project(self, project_guid): self._config['project'] = project_guid @@ -153,22 +152,6 @@ def set_devices(self, devices): else: self._devices = list(filter(filter_devices_by_name(), devices)) - def create_secrets(self): - docker = self._config['docker'] - docker_secret = self.client.create_secret(Secret('docker-secret', SecretConfigDocker( - docker['username'], docker['password'], docker['email']))) - self._secrets = {'docker': docker_secret} - - def delete_secrets(self): - for secret in self._secrets.values(): - secret.delete() - - def get_secret(self, secret_type): - """ - Returns the Secret Object based on the secret_type ('git' and 'docker'). - """ - return self._secrets[secret_type] - class InvalidConfig(Exception): def __init__(self, msg=None): diff --git a/sdk_test/coreapi/project_test.py b/sdk_test/coreapi/project_test.py deleted file mode 100644 index 8b28be4f..00000000 --- a/sdk_test/coreapi/project_test.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import absolute_import - -import unittest - -from rapyuta_io import Client, Project, Secret, SecretConfigDocker -from rapyuta_io.utils import BadRequestError -from rapyuta_io.utils.utils import generate_random_value -from sdk_test.config import Configuration -from sdk_test.util import get_logger - - -class TestProject(unittest.TestCase): - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - - def tearDown(self): - if hasattr(self, 'project'): - self.config.client.delete_project(self.project.guid) - - def test_create_project(self): - p = Project( - 'test-{}'.format(generate_random_value(5)), - organization_guid=self.config.organization_guid - ) - self.project = self.config.client.create_project(p) - self.assertIsInstance(self.project, Project) - self.assertIsNotNone(self.project.guid) - self.assertIsNotNone(self.project.creator) - self.assertIsNotNone(self.project.created_at) - self.assertIsNotNone(self.project.users) - - def test_list_project(self): - p = Project( - 'test-{}'.format(generate_random_value(5)), - organization_guid=self.config.organization_guid - ) - self.project = self.config.client.create_project(p) - project_list = self.config.client.list_projects() - project_list = [p for p in project_list if p.guid == self.project.guid] - self.assertEqual(len(project_list), 1) - - def test_client_without_project(self): - auth = self.config.get_auth_token() - client = Client(auth) - self.assertRaises( - BadRequestError, - lambda: client.create_secret( - Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - ) - ) diff --git a/sdk_test/coreapi/query_metrics_test.py b/sdk_test/coreapi/query_metrics_test.py deleted file mode 100644 index 88cc55fe..00000000 --- a/sdk_test/coreapi/query_metrics_test.py +++ /dev/null @@ -1,94 +0,0 @@ -import pytz -from datetime import datetime, timedelta -from unittest import TestCase - - -from rapyuta_io import DeviceArch -from rapyuta_io.clients.device import SystemMetric, QoS -from rapyuta_io.clients.metrics import QueryMetricsRequest, MetricFunction, MetricOperation, \ - StepInterval, SortOrder, ListMetricsRequest, Entity, ListTagKeysRequest, ListTagValuesRequest -from rapyuta_io.utils.error import ConflictError -from sdk_test.config import Configuration -from sdk_test.util import get_logger -from time import sleep - - -class MetricsTests(TestCase): - DEVICE = None - WAIT_TIME = 120 - - @classmethod - def setUpClass(cls): - config = Configuration() - logger = get_logger() - device = config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose')[0] - try: - logger.info('subscribing to metrics') - device.subscribe_metric(SystemMetric.CPU, QoS.LOW) - logger.info('waiting for {} seconds '.format(cls.WAIT_TIME)) - sleep(cls.WAIT_TIME) - except ConflictError: - get_logger().info('metrics is info already subscribed') - cls.DEVICE = device - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.to_datetime = datetime.now(pytz.UTC) - self.from_datetime = self.to_datetime - timedelta(days=1) - - @classmethod - def tearDownClass(cls): - cls.DEVICE.unsubscribe_metric(SystemMetric.CPU) - - def assert_column_object_fields(self, columns): - self.assertTrue(self.has_value_for_attribute(columns, 'name')) - self.assertTrue(all(map(lambda x: getattr(x, 'function') if x.name != 'timestamp' else True, - columns))) - self.assertTrue(all(map(lambda x: getattr(x, 'metric_group') if x.name != 'timestamp' else True, - columns))) - for col in columns: - if getattr(col, 'tag_names'): - self.assertTrue(len(col.tag_names) == len(col.tag_values)) - - @staticmethod - def has_value_for_attribute(response, field): - return all(map(lambda x: getattr(x, field), response)) - - def test_query_metrics(self): - metrics = [MetricOperation(MetricFunction.COUNT, 'cpu.usage_system'), - MetricOperation(MetricFunction.PERCENTILE_95, 'cpu.usage_idle')] - query_metrics_request = QueryMetricsRequest(self.from_datetime, self.to_datetime, StepInterval.ONE_MINUTE, - metrics, groupby=['device_id'], sort=SortOrder.DESC) - metrics_response = self.config.client.query_metrics(query_metrics_request) - - self.assert_column_object_fields(metrics_response.columns) - self.assertTrue(len(metrics_response.columns)) - self.assertTrue(len(metrics_response.rows) == len(metrics_response.columns)) - - def test_list_metrics(self): - list_metrics_query = ListMetricsRequest(Entity.PROJECT, self.config._project.guid, - self.from_datetime, self.to_datetime) - metrics = self.config.client.list_metrics(list_metrics_query) - - self.assertTrue(len(metrics)) - self.assertTrue(self.has_value_for_attribute(metrics, 'metric_group')) - self.assertTrue(self.has_value_for_attribute(metrics, 'metric_names')) - - def test_list_tag_keys(self): - list_tag_keys_query = ListTagKeysRequest(Entity.PROJECT, self.config._project.guid, - self.from_datetime, self.to_datetime) - tag_keys = self.config.client.list_tag_keys(list_tag_keys_query) - - self.assertTrue(len(tag_keys)) - self.assertTrue(self.has_value_for_attribute(tag_keys, 'tags')) - self.assertTrue(self.has_value_for_attribute(tag_keys, 'metric_group')) - - def test_list_tag_values(self): - tag = 'cpu' - list_tag_values_query = ListTagValuesRequest(Entity.PROJECT, self.config._project.guid, tag, - self.from_datetime, self.to_datetime) - tag_values = self.config.client.list_tag_values(list_tag_values_query) - - self.assertTrue(len(tag_values)) - self.assertTrue(isinstance(tag_values, list)) diff --git a/sdk_test/coreapi/secret_test.py b/sdk_test/coreapi/secret_test.py deleted file mode 100644 index 79ae3244..00000000 --- a/sdk_test/coreapi/secret_test.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import absolute_import -import unittest - -from rapyuta_io import Secret, SecretConfigDocker -from sdk_test.config import Configuration -from sdk_test.util import get_logger - - -class TestSecret(unittest.TestCase): - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - - def tearDown(self): - if hasattr(self, 'secret'): - self.config.client.delete_secret(self.secret.guid) - - def assertSecret(self, secret): - self.assertIsInstance(secret, Secret) - self.assertIsNotNone(secret.guid) - self.assertIsNotNone(secret.creator) - self.assertIsNotNone(secret.created_at) - self.assertIsNotNone(secret.secret_type) - - - def test_create_secret_docker(self): - self.secret = self.config.client.create_secret(Secret('docker-test', SecretConfigDocker('user','pass', 'email'))) - self.assertSecret(self.secret) - - def test_list_secret_docker(self): - self.secret = self.config.client.create_secret(Secret('docker-test', SecretConfigDocker('user','pass', 'email'))) - secret_list = self.config.client.list_secrets() - secret_list = [s for s in secret_list if s.guid == self.secret.guid] - self.assertEqual(len(secret_list), 1) - - def test_update_secret_source_docker(self): - self.secret = self.config.client.create_secret(Secret('docker-test', SecretConfigDocker('user','pass', 'email'))) - self.secret = self.config.client.update_secret(self.secret.guid, Secret('docker-test', SecretConfigDocker('user1','pass1', 'email1'))) - self.assertSecret(self.secret) - diff --git a/sdk_test/coreapi/user_test.py b/sdk_test/coreapi/user_test.py index 960bf000..ecdbb840 100644 --- a/sdk_test/coreapi/user_test.py +++ b/sdk_test/coreapi/user_test.py @@ -1,7 +1,6 @@ from __future__ import absolute_import import unittest -from rapyuta_io import Project from rapyuta_io.clients.organization import Organization, Country, OrganizationState from rapyuta_io.clients.project import User, UserState from sdk_test.config import Configuration @@ -17,8 +16,6 @@ def test_get_authenticated_user_details(self): self.user = self.config.client.get_authenticated_user() self.assertIsInstance(self.user, User) - for project in self.user.projects: - self.assertIsInstance(project, Project) self.assertIsInstance(self.user.organization, Organization) self.assertIsInstance(self.user.organization.country, Country) diff --git a/sdk_test/coreapi/usergroup_test.py b/sdk_test/coreapi/usergroup_test.py index 899cc5bc..b1efa496 100644 --- a/sdk_test/coreapi/usergroup_test.py +++ b/sdk_test/coreapi/usergroup_test.py @@ -1,6 +1,6 @@ import unittest -from rapyuta_io import UserGroup, Project +from rapyuta_io import UserGroup from sdk_test.config import Configuration @@ -82,12 +82,16 @@ def test_update_usergroup(self): payload = self.usergroup_create_payload self.usergroup = self.config.client.create_usergroup(self.config.organization_guid, payload) - p = Project(self.PROJECT_NAME, organization_guid=self.config.organization_guid) - self.project = self.config.client.create_project(p) + self.project_guid = self.config.v2_client.create_project({ + 'metadata': { + 'name': self.PROJECT_NAME, + 'organizationGUID': self.config.organization_guid + }, + }) upload_payload = self.usergroup_upload_payload upload_payload['update']['projects']['add'].append({ - 'guid': self.project.guid + 'guid': self.project_guid }) self.usergroup = self.config.client.update_usergroup(self.config.organization_guid, self.usergroup.guid, @@ -96,8 +100,3 @@ def test_update_usergroup(self): self.assertEqual(len(self.usergroup.projects), 1) self.assertEqual(len(self.usergroup.admins), 1) self.assertEqual(len(self.usergroup.members), 1) - - - - - diff --git a/sdk_test/device/deployment_test.py b/sdk_test/device/deployment_test.py deleted file mode 100644 index 7b16e04f..00000000 --- a/sdk_test/device/deployment_test.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import absolute_import -from sdk_test.config import Configuration -from sdk_test.device.device_test import DeviceTest -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, add_package, delete_package, get_package -from rapyuta_io import DeviceArch -from rapyuta_io.utils import DeploymentRunningException - - -class TestDeployment(DeviceTest, PackageTest): - TALKER_DOCKER_MANIFEST = 'talker-docker.json' - TALKER_DOCKER_PACKAGE = 'test-deployment-talker-docker-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.TALKER_DOCKER_MANIFEST, cls.TALKER_DOCKER_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.TALKER_DOCKER_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - # Assumption: We only have one amd64 device with Docker runtime. - devices = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose') - self.device = devices[0] - - def tearDown(self): - pass - - def assert_get_deployments(self, deployment_id): - self.logger.info('Asserting if some deployment is present') - deployments = self.device.get_deployments() - self.assertGreater(len(deployments), 0) - deployment_exists = False - for deployment in deployments: - if deployment_id == deployment['io_deployment_id']: - deployment_exists = True - break - self.assertTrue(deployment_exists, 'Current deployment should be present') - - def test_get_deployment(self): - self.routed_network = self.create_cloud_routed_network('talker-routed-network') - self.package = get_package(self.TALKER_DOCKER_PACKAGE) - self.provision_config = self.package.get_provision_configuration() - self.provision_config.add_device('default', self.device) - self.provision_config.add_routed_network(self.routed_network) - - self.logger.info('Deploying talker package') - self.talker_deployment = self.deploy_package(self.package, self.provision_config) - self.talker_deployment.poll_deployment_till_ready(sleep_interval=20, retry_count=10) - self.assert_get_deployments(self.talker_deployment.deploymentId) - - self.deprovision_all_deployments([self.talker_deployment]) - self.routed_network.delete() - self.package = None - - def test_device_refresh(self): - partial_device = [d for d in self.config.client.get_all_devices() if d.uuid == self.device.uuid][0] - self.assertTrue(partial_device.is_partial) - with self.assertRaises(AttributeError): - partial_device.host - partial_device.refresh() - self.assertFalse(partial_device.is_partial) - self.assertTrue(partial_device.host) diff --git a/sdk_test/device/topic_test.py b/sdk_test/device/topic_test.py deleted file mode 100644 index 7d6d9c4a..00000000 --- a/sdk_test/device/topic_test.py +++ /dev/null @@ -1,163 +0,0 @@ -from __future__ import absolute_import - -from rapyuta_io import TopicKind, DeviceArch -from rapyuta_io.clients.device import QoS -from rapyuta_io.utils import BadRequestError -from sdk_test.config import Configuration -from sdk_test.device.device_test import DeviceTest -from sdk_test.util import get_logger, start_roscore, stop_roscore -import six - - -class TestTopic(DeviceTest): - - @classmethod - def setUpClass(cls): - config = Configuration() - devices = config.get_devices(arch=DeviceArch.AMD64, runtime="Preinstalled") - start_roscore(devices[0]) - - @classmethod - def tearDownClass(cls): - config = Configuration() - devices = config.get_devices(arch=DeviceArch.AMD64, runtime="Preinstalled") - stop_roscore(devices[0]) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - # Assumption: We only have one amd64 device with Preinstalled runtime. - self.device = self.config.get_devices(arch=DeviceArch.AMD64, runtime="Preinstalled")[0] - - def assert_topic_subscription_status(self, topic, subscription_status): - if subscription_status.get('subscribed_success', None): - self.assertIn(topic, subscription_status.get('subscribed_success'), - 'Topic %s not found on the subscribed list' % topic) - self.logger.info('Topic %s subscribed successfully' % topic) - return - elif subscription_status.get('subscribed_error', None): - error = subscription_status.get('subscribed_error')[0][topic] - self.logger.info('Topic subscription failed due to %s' % error) - - raise AssertionError('Topic subscription failed for the topic: %s' % topic) - - def assert_topic_unsubscription_status(self, topic, unsubscription_status): - if unsubscription_status.get('unsubscribed_success', None): - self.assertIn(topic, unsubscription_status.get('unsubscribed_success'), - 'Topic %s not found on the unsubscribed list' % topic) - self.logger.info('Topic %s unsubscribed successfully' % topic) - return - elif unsubscription_status.get('unsubscribed_error', None): - error = unsubscription_status.get('unsubscribed_error')[0] - self.logger.error('Topic unsubscription failed due to %s' % error) - - raise AssertionError('Topic unsubscription failed for the topic: %s' % topic) - - def assert_topic_status(self, topic_status, topic, kind): - self.logger.info('Asserting subscribed topic is present on the subscription status') - if isinstance(kind, TopicKind): - kind = kind.value - for topic_dict in topic_status.Subscribed[kind.lower()]: - if topic == topic_dict['name']: - self.logger.info('Topic %s is in the subscribed list' % topic) - return - self.logger.error('Topic %s is not found in the subscribed list' % topic) - raise AssertionError('%s topic is not in subscribed list' % topic) - - def subscribe_any_topic(self): - topics = self.device.topics() - self.assertNotEqual(0, len(topics), 'Topics should not be empty') - topic = topics[0] - subscription_status = self.device.subscribe_topic(topic, QoS.MEDIUM.value, TopicKind.LOG) - topic_status = self.device.topic_status() - self.assert_topic_subscription_status(topic, subscription_status) - self.assert_topic_status(topic_status, topic, TopicKind.LOG) - return topic - - def test_topics(self): - self.logger.info('Started device topics test') - self.logger.info('Getting topic lists') - topics = self.device.topics() - self.assertTrue(isinstance(topics, list)) - for topic in topics: - self.assertTrue(isinstance(topic, six.string_types)) - self.logger.info(topics) - - def test_topic_status(self): - self.logger.info('Getting topic status') - topic_status = self.device.topic_status() - self.assertTrue(isinstance(topic_status.Subscribed.metric, list)) - self.assertTrue(isinstance(topic_status.Subscribed.log, list)) - self.assertTrue(isinstance(topic_status.Unsubscribed, list)) - self.logger.info(topic_status) - - def test_subscribe_topic(self): - self.logger.info('Subscribing for a valid topic') - self.subscribe_any_topic() - - def test_subscribe_unknown_topic(self): - self.logger.info('Subscribing for unknown topic') - unknown_topic = '/unknow_topic' - with self.assertRaises(AssertionError): - subscription_status = self.device.subscribe_topic(unknown_topic, QoS.HIGH.value, TopicKind.METRIC) - self.assert_topic_subscription_status(unknown_topic, subscription_status) - - def test_unsubscribe_topic(self): - self.logger.info('Unsubscribing valid topic') - topic_status = self.device.topic_status() - topic = None - if len(topic_status.Subscribed.metric) > 0: - topic = topic_status.Subscribed.metric[0].get('name') - kind = TopicKind.METRIC - elif len(topic_status.Subscribed.log) > 0: - topic = topic_status.Subscribed.log[0]['name'] - kind = TopicKind.LOG - - if not topic: - topic = self.subscribe_any_topic() - kind = TopicKind.LOG - - unsubscription_status = self.device.unsubscribe_topic(topic, kind) - self.assert_topic_unsubscription_status(topic, unsubscription_status) - - def test_unsubscribe_unknown_topic(self): - self.logger.info('Unsubscribing invalid topic') - unknown_topic = '/unknow_topic' - with self.assertRaises(AssertionError): - unsubscription_status = self.device.unsubscribe_topic(unknown_topic, TopicKind.METRIC) - self.assert_topic_unsubscription_status(unknown_topic, unsubscription_status) - - def test_subscribe_topic_with_fields_override(self): - self.logger.info('Subscribing for unknown topic') - unknown_topic = '/unknow_topic' - subscription_status = self.device.subscribe_topic(unknown_topic, QoS.HIGH.value, TopicKind.METRIC, - whitelist_field=['randomfieldoverride'], - fail_on_topic_inexistence=False) - self.assert_topic_subscription_status(unknown_topic, subscription_status) - - def test_subscribe_topic_with_tags_fields_override(self): - self.logger.info('Subscribing for unknown topic') - unknown_topic = '/unknow_topic' - subscription_status = self.device.subscribe_topic(unknown_topic, QoS.HIGH.value, TopicKind.METRIC, - whitelist_field=['randomfieldoverride'], - whitelist_tag=['randomtags'], - fail_on_topic_inexistence=False) - self.assert_topic_subscription_status(unknown_topic, subscription_status) - - def test_subscribe_topic_with_tags_override_error(self): - self.logger.info('Subscribing for unknown topic') - unknown_topic = '/unknow_topic' - self.with_error('', BadRequestError, self.device.subscribe_topic, unknown_topic, QoS.HIGH.value, - TopicKind.METRIC, - whitelist_tag=['randomtags', 1, 2], - whitelist_field=['randomfieldoverride'], - fail_on_topic_inexistence=False) - - def test_subscribe_topic_with_fields_override_error(self): - self.logger.info('Subscribing for unknown topic') - unknown_topic = '/unknow_topic' - self.with_error('', BadRequestError, self.device.subscribe_topic, unknown_topic, QoS.HIGH.value, - TopicKind.METRIC, - whitelist_field=['randomfieldoverride', 1, 2], - whitelist_tag=['randomtags'], - fail_on_topic_inexistence=False) diff --git a/sdk_test/jsons/builds/listener.json b/sdk_test/jsons/builds/listener.json deleted file mode 100644 index d8e3348d..00000000 --- a/sdk_test/jsons/builds/listener.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "buildName": "listener", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "strategyType": "Source", - "architecture": "amd64", - "isRos": true, - "rosDistro": "melodic", - "contextDir": "talk/listener" -} diff --git a/sdk_test/jsons/builds/pingpong.json b/sdk_test/jsons/builds/pingpong.json deleted file mode 100644 index d2473abd..00000000 --- a/sdk_test/jsons/builds/pingpong.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "buildName": "pingpong", - "secret": "git", - "repository": "ssh://git@bitbucket.org/rapyutians/io_test_scenarios#rapyutaio", - "strategyType": "Source", - "architecture": "amd64", - "isRos": true, - "rosDistro": "melodic", - "buildOptions": { - "catkinOptions": [ - { - "rosPkgs": "pingpong" - } - ] - } -} \ No newline at end of file diff --git a/sdk_test/jsons/builds/talker-noetic.json b/sdk_test/jsons/builds/talker-noetic.json deleted file mode 100644 index d6f8798a..00000000 --- a/sdk_test/jsons/builds/talker-noetic.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "buildName": "talker-noetic", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "branch": "master", - "strategyType": "Source", - "architecture": "amd64", - "isRos": true, - "rosDistro": "noetic", - "contextDir": "talk/talker3" -} diff --git a/sdk_test/jsons/builds/talker.json b/sdk_test/jsons/builds/talker.json deleted file mode 100644 index 43444f2a..00000000 --- a/sdk_test/jsons/builds/talker.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "buildName": "talker", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "strategyType": "Source", - "architecture": "amd64", - "isRos": true, - "rosDistro": "melodic", - "contextDir": "talk/talker" -} diff --git a/sdk_test/jsons/builds/throttle-latch-build.json b/sdk_test/jsons/builds/throttle-latch-build.json deleted file mode 100644 index 275a5529..00000000 --- a/sdk_test/jsons/builds/throttle-latch-build.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "branch": "master", - "buildName": "throttle-latch-build", - "strategyType": "Source", - "contextDir": "talk/throttle_latch", - "repository": "https://github.com/rapyuta-robotics/io_tutorials", - "architecture": "amd64", - "isRos": true, - "triggerName": "", - "tagName": "", - "rosDistro": "melodic", - "simulationOptions": { - "simulation": false - } -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/cloud-non-ros.json b/sdk_test/jsons/packages/cloud-non-ros.json deleted file mode 100644 index 86198652..00000000 --- a/sdk_test/jsons/packages/cloud-non-ros.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "cloud-non-ros", - "packageVersion": "v1.0.0", - "description": "cloud-non-ros sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [ - { - "name": "ep1", - "exposeExternally": true, - "port": 443, - "targetPort": 5000, - "proto": "HTTPS" - } - ] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": false - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "exec", - "cmd": [], - "docker": "hitesh99/simpleflask:v1" - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/cloud-transform.json b/sdk_test/jsons/packages/cloud-transform.json deleted file mode 100644 index c18a13dc..00000000 --- a/sdk_test/jsons/packages/cloud-transform.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "cloud-transform", - "packageVersion": "v1.0.0", - "description": "cloud-transform sdk test package", - "plans": [ - { - "name": "Plan1", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [ - { - "name": "telemetry_decorated", - "qos": "max", - "scoped": "False", - "targeted": "False" - } - ], - "services": [], - "actions": [], - "isROS": true - }, - "requiredRuntime": "cloud", - "executables": [ - { - "name": "CloudTransfExec", - "gitExecutable": { - "repository": "https://github.com/bhuvanchandra/ros_string_decorator_py.git", - "strategyType": "Source", - "dockerFilePath": "", - "contextDir": "" - }, - "cmd": [ - "roslaunch string_decorator string_decorator.launch" - ] - } - ], - "parameters": [], - "architecture": "amd64" - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/delete-package-using-client.json b/sdk_test/jsons/packages/delete-package-using-client.json deleted file mode 100644 index c139df03..00000000 --- a/sdk_test/jsons/packages/delete-package-using-client.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "delete-package-using-client", - "packageVersion": "v1.0.0", - "description": "cloud-non-ros sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [ - { - "name": "ep1", - "exposeExternally": true, - "port": 443, - "targetPort": 5000, - "proto": "HTTPS" - } - ] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": false - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "exec", - "cmd": [], - "docker": "hitesh99/simpleflask:v1" - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/delete-package.json b/sdk_test/jsons/packages/delete-package.json deleted file mode 100644 index ac5f55e0..00000000 --- a/sdk_test/jsons/packages/delete-package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "delete-package", - "packageVersion": "v1.0.0", - "description": "cloud-non-ros sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [ - { - "name": "ep1", - "exposeExternally": true, - "port": 443, - "targetPort": 5000, - "proto": "HTTPS" - } - ] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": false - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "exec", - "cmd": [], - "docker": "hitesh99/simpleflask:v1" - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/device-volume.json b/sdk_test/jsons/packages/device-volume.json deleted file mode 100644 index e512f9d5..00000000 --- a/sdk_test/jsons/packages/device-volume.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "device-volume", - "packageVersion": "v1.0.0", - "description": "device-volume sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": false - }, - "requiredRuntime": "device", - "architecture": "amd64", - "executables": [ - { - "name": "nginx", - "simulationOptions": { - "simulation": false - }, - "cmd": [ ], - "docker": "nginx:alpine" - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/fast-talker-device-docker-with-rosbags.json b/sdk_test/jsons/packages/fast-talker-device-docker-with-rosbags.json deleted file mode 100644 index b500e3fa..00000000 --- a/sdk_test/jsons/packages/fast-talker-device-docker-with-rosbags.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "test-package", - "packageVersion": "v1.0.0", - "description": "", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": { - - }, - "singleton": false, - "components": [ - { - "name": "talker-fast-device", - "description": "", - "ros": { - "topics": [ - - ], - "services": [ - - ], - "actions": [ - - ], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "restart_policy": "always", - "architecture": "amd64", - "executables": [ - { - "name": "talker", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch talker talker.launch" - ] - } - ], - "parameters": [ - { - "default": "100000", - "name": "RATE", - "description": "" - } - ], - "rosBagJobDefs": [ - { - "name": "continuous_upload_type", - "recordOptions": { - "allTopics": true, - "maxSplits": 5, - "maxSplitSize": 10 - }, - "uploadOptions": { - "uploadType": "Continuous", - "maxUploadRate": 5242880, - "purgeAfter": false - } - } - ] - } - ], - "includePackages": [ - - ], - "dependentDeployments": [ - - ], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": false - }, - "exposedParameters": [ - - ] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/inbound-incoming-scoped-targeted.json b/sdk_test/jsons/packages/inbound-incoming-scoped-targeted.json deleted file mode 100644 index 28898da7..00000000 --- a/sdk_test/jsons/packages/inbound-incoming-scoped-targeted.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "apiVersion": "2.0.0", - "name": "inbound-incoming-scoped-targeted", - "packageVersion": "v1.0.0", - "description": "Package contains single component", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "listener", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "ListenerExec", - "simulationOptions": { - "simulation": false - }, - "gitExecutable": { - "repository": "https://github.com/rapyuta/io_tutorials", - "strategyType": "Source", - "dockerFilePath": "", - "contextDir": "" - }, - "buildOptions": { - "catkinOptions": [] - }, - "cmd": [ - "roslaunch listener listener.launch" - ] - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": true - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/latching-pkg.json b/sdk_test/jsons/packages/latching-pkg.json deleted file mode 100644 index ea1620a0..00000000 --- a/sdk_test/jsons/packages/latching-pkg.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "latching-pkg", - "packageVersion": "v1.0.0", - "description": "", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": { - - }, - "singleton": false, - "components": [ - { - "name": "latching-component", - "description": "", - "ros": { - "topics": [ - - ], - "services": [ - - ], - "actions": [ - - ], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "restart_policy": "always", - "architecture": "amd64", - "executables": [ - { - "name": "latching-executable", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch latching latch.launch" - ] - } - ], - "parameters": [ - - ], - "rosBagJobDefs": [ - - ] - } - ], - "includePackages": [ - - ], - "dependentDeployments": [ - - ], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": false - }, - "exposedParameters": [ - - ] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/listener-docker.json b/sdk_test/jsons/packages/listener-docker.json deleted file mode 100644 index 5c0f9801..00000000 --- a/sdk_test/jsons/packages/listener-docker.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "listener-docker", - "packageVersion": "v1.0.0", - "description": "listener-docker sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "architecture": "amd64", - "executables": [ - { - "name": "listenerExec", - "gitExecutable": { - "repository": "https://github.com/bhuvanchandra/listener_py.git", - "strategyType": "Source", - "dockerFilePath": "", - "contextDir": "" - }, - "cmd": [ - "roslaunch listener listener.launch" - ] - } - ], - "parameters": [ - { - "default": "/telemetry_decorated", - "name": "topic_name", - "description": "config param.." - } - ] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/listener.json b/sdk_test/jsons/packages/listener.json deleted file mode 100644 index 0eae6e78..00000000 --- a/sdk_test/jsons/packages/listener.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "listener", - "packageVersion": "v1.0.0", - "description": "listener amd64 sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "architecture": "amd64", - "executables": [ - { - "name": "listenerExec", - "cmd": [ - "roslaunch listener listener.launch" - ] - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/nginx-multi-component.json b/sdk_test/jsons/packages/nginx-multi-component.json deleted file mode 100644 index e310f7c1..00000000 --- a/sdk_test/jsons/packages/nginx-multi-component.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "name": "nginx-multi-component", - "packageVersion": "v1.0.0", - "description": "", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": { }, - "singleton": false, - "components": [ - { - "name": "nginx2", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [ - { - "name": "test2", - "exposeExternally": true, - "port": 443, - "targetPort": 80, - "proto": "HTTPS" - } - ] - }, - "ros": { - "topics": [ ], - "services": [ ], - "actions": [ ], - "isROS": false - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "nginx2", - "simulationOptions": { - "simulation": false - }, - "cmd": [ ], - "docker": "nginx:alpine" - } - ], - "parameters": [ ] - }, - { - "name": "nginx", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [ - { - "name": "test", - "exposeExternally": true, - "port": 443, - "targetPort": 80, - "proto": "HTTPS" - } - ] - }, - "ros": { - "topics": [ ], - "services": [ ], - "actions": [ ], - "isROS": false - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "nginx2", - "simulationOptions": { - "simulation": false - }, - "cmd": [ ], - "docker": "nginx:alpine" - } - ], - "parameters": [ ] - } - ], - "dependentDeployments": [ ], - "inboundROSInterfaces": { - "topics": [ ], - "services": [ ], - "actions": [ ] - }, - "exposedParameters": [ ] - } - ] -} diff --git a/sdk_test/jsons/packages/nginx-single-component.json b/sdk_test/jsons/packages/nginx-single-component.json deleted file mode 100644 index fccb634e..00000000 --- a/sdk_test/jsons/packages/nginx-single-component.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "plans": [ - { - "singleton": false, - "name": "default", - "inboundROSInterfaces": { - "topics": [ ], - "services": [ ], - "actions": [ ] - }, - "dependentDeployments": [ ], - "components": [ - { - "executables": [ - { - "name": "nginx", - "simulationOptions": { - "simulation": false - }, - "cmd": [ ], - "docker": "nginx:alpine" - } - ], - "cloudInfra": { - "endpoints": [ - { - "targetPort": 80, - "proto": "HTTPS", - "exposeExternally": true, - "name": "test", - "port": 443 - } - ], - "replicas": 1 - }, - "name": "nginx", - "parameters": [ ], - "architecture": "amd64", - "requiredRuntime": "cloud", - "ros": { - "services": [ ], - "topics": [ ], - "isROS": false, - "actions": [ ] - }, - "description": "" - } - ], - "exposedParameters": [ ], - "metadata": { } - } - ], - "description": "", - "bindable": true, - "packageVersion": "v1.0.0", - "name": "nginx-single-component" -} diff --git a/sdk_test/jsons/packages/no-scoped-targeted.json b/sdk_test/jsons/packages/no-scoped-targeted.json deleted file mode 100644 index 42be2922..00000000 --- a/sdk_test/jsons/packages/no-scoped-targeted.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "no-scoped-targeted", - "packageVersion": "v1.0.0", - "description": "no targeted or scoped sdk test package", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [ - { - "name": "topic_B", - "qos": "low", - "compression": "snappy", - "scoped": false, - "targeted": false - }, - { - "name": "topic_A", - "qos": "low", - "compression": "", - "scoped": false, - "targeted": false - } - ], - "services": [ - { - "name": "srv_B", - "compression": "snappy", - "scoped": false - }, - { - "name": "srv_A", - "compression": "", - "scoped": false - } - ], - "actions": [ - { - "name": "actionA", - "compression": "", - "scoped": false - } - ], - "isROS": true - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "docker_exec", - "cmd": [ - "/bin/bash", - "-c", - "sleep 10000" - ], - "docker": "ubuntu" - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/pv-reader.json b/sdk_test/jsons/packages/pv-reader.json deleted file mode 100644 index 932dc76b..00000000 --- a/sdk_test/jsons/packages/pv-reader.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "pv-reader", - "packageVersion": "v1.0.0", - "description": "pv-reader sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": false - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "CompReaderExec", - "gitExecutable": { - "repository": "https://github.com/hiteshsethi/go-reader-writer", - "strategyType": "Docker", - "dockerFilePath": "Dockerfile.reader", - "contextDir": "" - }, - "cmd": [] - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/rosbag-talker-cloud.json b/sdk_test/jsons/packages/rosbag-talker-cloud.json deleted file mode 100644 index f83ede78..00000000 --- a/sdk_test/jsons/packages/rosbag-talker-cloud.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "test-rosbag-talker-cloud-pkg", - "packageVersion": "v1.0.0", - "description": "", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "talker-cloud", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "talker", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch talker talker.launch" - ] - } - ], - "parameters": [], - "rosBagJobDefs": [ - { - "name":"test-rosbag-defs", - "recordOptions":{ - "topics":[ - "/telemetry" - ] - } - } - ] - } - ], - "dependentDeployments": [], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": false - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/scoped-cloud.json b/sdk_test/jsons/packages/scoped-cloud.json deleted file mode 100644 index 1816491c..00000000 --- a/sdk_test/jsons/packages/scoped-cloud.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "scoped-cloud", - "packageVersion": "v1.0.0", - "description": "scoped sdk test package", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "cloudping", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [ - { - "name": "ping", - "qos": "low", - "compression": "", - "scoped": true, - "targeted": false - } - ], - "services": [], - "actions": [], - "isROS": true - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "cloudy", - "cmd": [] - } - ], - "parameters": [ - { - "default": "pingpong", - "name": "ROS_PKG", - "description": "" - }, - { - "default": "pingst.launch", - "name": "ROS_LAUNCH_FILE", - "description": "" - } - ] - } - ], - "includePackages": [], - "dependentDeployments": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/scoped-targeted.json b/sdk_test/jsons/packages/scoped-targeted.json deleted file mode 100644 index 84412bb2..00000000 --- a/sdk_test/jsons/packages/scoped-targeted.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "name": "scoped-targeted", - "packageVersion": "v1.0.0", - "description": "scoped and targeted sdk test package", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "cloudping", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [ - { - "name": "ping", - "qos": "low", - "compression": "", - "scoped": true, - "targeted": false - } - ], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "cloudy", - "cmd": [] - } - ], - "parameters": [ - { - "default": "pingpong", - "name": "ROS_PKG", - "description": "" - }, - { - "default": "pingst.launch", - "name": "ROS_LAUNCH_FILE", - "description": "" - } - ] - }, - { - "name": "devicepong", - "description": "", - "ros": { - "topics": [ - { - "name": "pong", - "qos": "low", - "compression": "", - "scoped": false, - "targeted": true - } - ], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "architecture": "amd64", - "executables": [ - { - "name": "divvy", - "cmd": [ - "roslaunch pingpong pongst.launch" - ] - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/talker-cloud-device.json b/sdk_test/jsons/packages/talker-cloud-device.json deleted file mode 100644 index 1170c4e9..00000000 --- a/sdk_test/jsons/packages/talker-cloud-device.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "name": "talker-cloud-device", - "packageVersion": "v1.0.0", - "description": "", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "talker-device", - "description": "", - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "restart_policy": "no", - "architecture": "amd64", - "executables": [ - { - "name": "talker", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch talker talker.launch" - ] - } - ], - "parameters": [], - "rosBagJobDefs": [] - }, - { - "name": "talker-cloud", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "talker", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch talker talker.launch" - ] - } - ], - "parameters": [], - "rosBagJobDefs": [] - } - ], - "dependentDeployments": [], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": false - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/talker-cloud.json b/sdk_test/jsons/packages/talker-cloud.json deleted file mode 100644 index d4cfe9fc..00000000 --- a/sdk_test/jsons/packages/talker-cloud.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "apiVersion": "2.0.0", - "name": "talker-cloud", - "packageVersion": "v1.0.0", - "description": "Package contains single component", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "talker", - "description": "", - "cloudInfra": { - "replicas": 1, - "endpoints": [] - }, - "ros": { - "topics": [ - { - "name": "/telemetry", - "qos": "low", - "compression": "", - "scoped": false, - "targeted": true - } - ], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "TalkerExec", - "simulationOptions": { - "simulation": false - }, - "gitExecutable": { - "repository": "https://github.com/rapyuta/io_tutorials", - "strategyType": "Source", - "dockerFilePath": "", - "contextDir": "" - }, - "buildOptions": { - "catkinOptions": [] - }, - "cmd": [ - "rostopic pub -r 10 /listener/telemetry std_msgs/String rapyuta" - ] - } - ], - "parameters": [] - } - ], - "dependentDeployments": [], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": false - }, - "exposedParameters": [] - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/talker-docker.json b/sdk_test/jsons/packages/talker-docker.json deleted file mode 100644 index b2da872b..00000000 --- a/sdk_test/jsons/packages/talker-docker.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "talker-docker", - "packageVersion": "v1.0.0", - "description": "talker-docker sdk test package", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "ros": { - "topics": [ - { - "name": "/telemetry", - "qos": "med", - "scoped": "False", - "targeted": "False" - } - ], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "restart_policy": "no", - "architecture": "amd64", - "executables": [ - { - "name": "talkerExec", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch talker talker.launch" - ], - "docker": "quay.io/rapyuta/io_tutorials:latest" - } - ], - "parameters": [], - "rosBagJobDefs": [] - } - ], - "includePackages": [], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/talker-noetic.json b/sdk_test/jsons/packages/talker-noetic.json deleted file mode 100644 index 3d7b58c7..00000000 --- a/sdk_test/jsons/packages/talker-noetic.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "talker noetic", - "packageVersion": "v1.0.0", - "description": "noetic talker test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "talker", - "description": "", - "ros": { - "topics": [ - { - "name": "telemetry", - "qos": "low", - "scoped": false, - "targeted": false - } - ], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "noetic" - }, - "requiredRuntime": "cloud", - "architecture": "amd64", - "executables": [ - { - "name": "talkerExec", - "cmd": [ - "roslaunch talker3 talker.launch" - ] - } - ], - "parameters": [ - { - "default": "telemetry", - "name": "topic_name", - "description": "" - } - ] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/talker.json b/sdk_test/jsons/packages/talker.json deleted file mode 100644 index 62695edb..00000000 --- a/sdk_test/jsons/packages/talker.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "talker", - "packageVersion": "v1.0.0", - "description": "talker amd64 sdk test package", - "plans": [ - { - "name": "default", - "metadata": {}, - "singleton": false, - "components": [ - { - "name": "default", - "description": "", - "ros": { - "topics": [ - { - "name": "telemetry", - "qos": "low", - "scoped": false, - "targeted": false - } - ], - "services": [], - "actions": [], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "architecture": "amd64", - "executables": [ - { - "name": "talkerExec", - "cmd": [ - "roslaunch talker talker.launch" - ] - } - ], - "parameters": [ - { - "default": "telemetry", - "name": "topic_name", - "description": "" - } - ] - } - ], - "dependentDeployments": [], - "exposedParameters": [], - "inboundROSInterfaces": { - "topics": [], - "services": [], - "actions": [] - } - } - ] -} \ No newline at end of file diff --git a/sdk_test/jsons/packages/throttling-pkg.json b/sdk_test/jsons/packages/throttling-pkg.json deleted file mode 100644 index 6bde1c22..00000000 --- a/sdk_test/jsons/packages/throttling-pkg.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "throttling-pkg", - "packageVersion": "v1.0.0", - "description": "", - "bindable": true, - "plans": [ - { - "name": "default", - "metadata": { - - }, - "singleton": false, - "components": [ - { - "name": "throttling-component", - "description": "", - "ros": { - "topics": [ - - ], - "services": [ - - ], - "actions": [ - - ], - "isROS": true, - "ros_distro": "melodic" - }, - "requiredRuntime": "device", - "restart_policy": "always", - "architecture": "amd64", - "executables": [ - { - "name": "throttling-executable", - "simulationOptions": { - "simulation": false - }, - "cmd": [ - "roslaunch throttling throttle.launch" - ] - } - ], - "parameters": [ - - ], - "rosBagJobDefs": [ - - ] - } - ], - "includePackages": [ - - ], - "dependentDeployments": [ - - ], - "inboundROSInterfaces": { - "anyIncomingScopedOrTargetedRosConfig": false - }, - "exposedParameters": [ - - ] - } - ] -} \ No newline at end of file diff --git a/sdk_test/openshift/sdk-config.sample.yaml b/sdk_test/openshift/sdk-config.sample.yaml index 8baad6f0..9a70d1c8 100644 --- a/sdk_test/openshift/sdk-config.sample.yaml +++ b/sdk_test/openshift/sdk-config.sample.yaml @@ -8,25 +8,13 @@ data: { "catalog_host": "https://qacatalog.az39.rapyuta.io", "core_api_host": "https://qaapiserver.az39.rapyuta.io", + "v2_api_host": "https://qaapi.rapyuta.io", "hwil_host": "https://hwil.rapyuta.io", "hwil_user": "ansible", "hwil_password": "HWIL_PASSWORD", - "auth_token": "AUTH_TOKEN", + "auth_token": "Bearer AUTH_TOKEN", + "organization_guid": "org_guid", "devices": [ - { - "name": "supervisord-arm32", - "runtime": "Preinstalled", - "ip": "10.91.1.14", - "arch": "arm32v7", - "distro": "kinetic" - }, - { - "name": "docker-compose-arm32", - "runtime": "Dockercompose", - "ip": "10.91.1.15", - "arch": "arm32v7", - "distro": "kinetic" - }, { "name": "docker-compose-amd64", "runtime": "Dockercompose", @@ -35,7 +23,4 @@ data: "distro": "melodic" } ], - "git": { - "ssh-key": "SSH_KEY" - } } diff --git a/sdk_test/package/__init__.py b/sdk_test/package/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/sdk_test/package/cloud_non_ros_test.py b/sdk_test/package/cloud_non_ros_test.py deleted file mode 100644 index 1386529b..00000000 --- a/sdk_test/package/cloud_non_ros_test.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import absolute_import -import json - -from rapyuta_io.utils import RestClient -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, add_package, delete_package, get_package - - -class TestCloudNonRosWithEndpoint(PackageTest): - ENDPOINT_NAME = 'ep1' - - CLOUD_NON_ROS_MANIFEST = 'cloud-non-ros.json' - CLOUD_NON_ROS_PACKAGE = 'test-cloud-non-ros-with-endpoint' - - @classmethod - def setUpClass(cls): - add_package(cls.CLOUD_NON_ROS_MANIFEST, cls.CLOUD_NON_ROS_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.CLOUD_NON_ROS_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.package = get_package(self.CLOUD_NON_ROS_PACKAGE) - self.provision_config = self.package.get_provision_configuration() - self.cnr_deployment = None - - def tearDown(self): - self.deprovision_all_deployments([self.cnr_deployment]) - - def validate_service_endpoint(self, endpoint_name, endpoint): - self.logger.info('Checking the status of endpoint(%s) - %s' % (endpoint_name, endpoint)) - self.assert_endpoint_url(endpoint) - self.logger.info('%s: Valid endpoint url' % endpoint) - endpoint = endpoint + '/status' - response = RestClient(endpoint).execute() - self.assertEqual(response.status_code, 200) - self.assertEqual(json.loads(response.text)['status'], 'running') - self.logger.info('Endpoint %s returns status running' % endpoint) - - def test_cloud_non_ros(self): - self.logger.info('Testing cloud non ros with endpoint and replicas') - self.cnr_deployment = self.deploy_package(self.package, self.provision_config) - self.cnr_deployment.poll_deployment_till_ready() - - binding_obj = self.get_service_binding(self.cnr_deployment) - - for internal_component in self.package.plans[0].internalComponents: - component_id = internal_component.componentId - component = binding_obj.credentials.components.get(component_id) - if component: - self.logger.info('Getting network endpoints from service binding') - network_endpoints = component.networkEndpoints - self.logger.info('Fetching the status of "%s" endpoint' % self.ENDPOINT_NAME) - endpoint = network_endpoints.get(self.ENDPOINT_NAME) - self.validate_service_endpoint(self.ENDPOINT_NAME, endpoint) - - self.validate_package_refresh() - self.validate_deployment_refresh() - - def validate_package_refresh(self): - partial_package = [p for p in self.config.client.get_all_packages() if p.packageId == self.package.packageId][0] - self.assertTrue(partial_package.is_partial) - with self.assertRaises(AttributeError): - partial_package.ownerProject - partial_package.refresh() - self.assertFalse(partial_package.is_partial) - self.assertTrue(partial_package.ownerProject) - - def validate_deployment_refresh(self): - partial_deployment = [d for d in self.config.client.get_all_deployments() - if d.deploymentId == self.cnr_deployment.deploymentId][0] - self.assertTrue(partial_deployment.is_partial) - with self.assertRaises(AttributeError): - partial_deployment.parameters - partial_deployment.refresh() - self.assertFalse(partial_deployment.is_partial) - self.assertTrue(partial_deployment.parameters) diff --git a/sdk_test/package/cloud_scoped_targeted_test.py b/sdk_test/package/cloud_scoped_targeted_test.py deleted file mode 100644 index 075b0a70..00000000 --- a/sdk_test/package/cloud_scoped_targeted_test.py +++ /dev/null @@ -1,114 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import - -from rapyuta_io import DeviceArch -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, start_roscore, stop_roscore, add_package, delete_package, \ - get_package, add_cloud_native_network, add_cloud_routed_network, delete_native_network, delete_routed_network, \ - get_routed_network, get_native_network - - -class TestScopedTargeted(PackageTest): - ST_DEV_COMP = "devicepong" - - SCOPED_TARGETED_PACKAGE = 'test-scoped-targeted' - SCOPED_TARGETED_MANIFEST = 'scoped-targeted.json' - - SCOPED_CLOUD_PACKAGE = 'test-scoped-cloud' - SCOPED_CLOUD_MANIFEST = 'scoped-cloud.json' - - NO_SCOPED_TARGETED_PACKAGE = 'test-no-scoped-targeted' - NO_SCOPED_TARGETED_MANIFEST = 'no-scoped-targeted.json' - - @classmethod - def setUpClass(cls): - add_package(cls.SCOPED_TARGETED_MANIFEST, cls.SCOPED_TARGETED_PACKAGE - , build_map={ - 'cloudping': {'cloudy': ('pingpong-build', 'pingpong.json')} - }) - add_package(cls.SCOPED_CLOUD_MANIFEST, cls.SCOPED_CLOUD_PACKAGE, - build_map={ - 'cloudping': {'cloudy': ('pingpong-build', 'pingpong.json')} - }) - add_package(cls.NO_SCOPED_TARGETED_MANIFEST, cls.NO_SCOPED_TARGETED_PACKAGE) - config = Configuration() - devices = config.get_devices(arch=DeviceArch.AMD64, runtime="Preinstalled") - start_roscore(devices[0]) - add_cloud_native_network('cloud_scoped_targeted_native_network') - add_cloud_routed_network('cloud_scoped_targeted_routed_network') - - @classmethod - def tearDownClass(cls): - config = Configuration() - devices = config.get_devices(arch=DeviceArch.AMD64, runtime="Preinstalled") - stop_roscore(devices[0]) - delete_package(cls.SCOPED_TARGETED_PACKAGE, False) - delete_package(cls.SCOPED_CLOUD_PACKAGE) - delete_package(cls.NO_SCOPED_TARGETED_PACKAGE) - delete_native_network('cloud_scoped_targeted_native_network') - delete_routed_network('cloud_scoped_targeted_routed_network') - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - devices = self.config.get_devices(arch=DeviceArch.AMD64, runtime="Preinstalled") - self.device = devices[0] - self.deployments = [] - self.non_st_dep_deployment = None - self.routed_network = get_routed_network('cloud_scoped_targeted_routed_network') - self.native_network = get_native_network('cloud_scoped_targeted_native_network') - - def tearDown(self): - self.deprovision_all_deployments(self.deployments) - - def test_scoped_targeted_package(self): - package = get_package(self.SCOPED_TARGETED_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_device(self.ST_DEV_COMP, self.device, ignore_device_config=['network_interface']) - provision_config.add_routed_networks([self.routed_network]) - deployment = self.deploy_package(package, provision_config) - deployment.poll_deployment_till_ready() - self.assert_deployment_status(deployment) - self.deployments.append(deployment) - - def test_scoped_cloud_package_ros_namespace(self): - package = get_package(self.SCOPED_CLOUD_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_native_network(self.native_network) - provision_config.set_component_alias("cloudping", "cloudping", True) - deployment = self.deploy_package(package, provision_config) - deployment.poll_deployment_till_ready() - comp_id = deployment.componentInfo[0].componentID - self.assert_deployment_status_st_check(deployment, comp_id, "cloudping", True) - self.assert_deployment_status(deployment) - self.deployments.append(deployment) - - def test_scoped_targeted_package_non_alias_depends(self): - package = get_package(self.SCOPED_TARGETED_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_device(self.ST_DEV_COMP, self.device, ignore_device_config=['network_interface']) - provision_config.add_routed_networks([self.routed_network]) - comp_id = provision_config.plan.get_component_id(self.ST_DEV_COMP) - st_deployment = self.deploy_package(package, provision_config) - st_deployment.poll_deployment_till_ready() - self.deployments.append(st_deployment) - self.assert_deployment_status(st_deployment) - self.assert_deployment_status_st_check(st_deployment, comp_id, self.device.name, False) - package = get_package(self.NO_SCOPED_TARGETED_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_dependent_deployment(st_deployment) - provision_config.add_routed_network(self.routed_network) - non_st_dep_deployment = self.deploy_package(package, provision_config) - self.assert_dependent_deployment(non_st_dep_deployment, [st_deployment]) - non_st_dep_deployment.poll_deployment_till_ready() - self.assert_deployment_status(non_st_dep_deployment) - self.deployments.append(non_st_dep_deployment) - - def assert_deployment_status_st_check(self, deployment, component, alias, set_ros_namespace): - componentobj = getattr(deployment.parameters, component) - self.assertEqual(componentobj.bridge_params.alias, alias) - self.assertEqual(componentobj.bridge_params.setROSNamespace, set_ros_namespace) - self.logger.info('Deployment %s(%s) started successfully' % (deployment.name, - deployment.packageId)) diff --git a/sdk_test/package/cloud_transform_test.py b/sdk_test/package/cloud_transform_test.py deleted file mode 100644 index c6659148..00000000 --- a/sdk_test/package/cloud_transform_test.py +++ /dev/null @@ -1,99 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import -from rapyuta_io import DeviceArch -from rapyuta_io.clients.package import RestartPolicy -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, get_package, add_package, delete_package -import six - - -class TestCloudTransform(PackageTest): - - TALKER_MANIFEST = 'talker.json' - LISTENER_MANIFEST = 'listener.json' - CLOUD_TRANSFORM_MANIFEST = 'cloud-transform.json' - - TALKER_PACKAGE = 'test-cloud-transform-talker' - LISTENER_PACKAGE = 'test-cloud-transform-listener' - CLOUD_TRANSFORM_PACKAGE = 'test-cloud-transform-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.TALKER_MANIFEST, cls.TALKER_PACKAGE) - add_package(cls.LISTENER_MANIFEST, cls.LISTENER_PACKAGE) - add_package(cls.CLOUD_TRANSFORM_MANIFEST, cls.CLOUD_TRANSFORM_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.TALKER_PACKAGE) - delete_package(cls.LISTENER_PACKAGE) - delete_package(cls.CLOUD_TRANSFORM_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - devices = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Preinstalled') - self.device = devices[0] - self.routed_network = self.create_cloud_routed_network('cloud_transform_network') - - self.talker_deployment = None - self.cloud_transform_deployment = None - self.listener_deployment = None - - def tearDown(self): - self.deprovision_all_deployments([self.talker_deployment, self.cloud_transform_deployment, - self.listener_deployment]) - self.routed_network.delete() - - def assert_deployment_list_with_device_id(self): - dev_id = self.device.get('uuid') - filtered_deployments = self.config.client.get_all_deployments(device_id=dev_id) - filtered_deployment_ids = map(lambda dep: dep['deploymentId'], filtered_deployments) - device_deployments = self.device.get_deployments() - device_deployment_ids = map(lambda dep: dep['io_deployment_id'], device_deployments) - six.assertCountEqual(self, filtered_deployment_ids, device_deployment_ids, 'both deployments should match') - - def deploy_talker_package(self): - package = get_package(self.TALKER_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_device('default', self.device, ignore_device_config=['network_interface']) - provision_config.add_routed_network(self.routed_network) - self.logger.info('Deploying talker package') - self.talker_deployment = self.deploy_package(package, provision_config) - - def deploy_cloud_transform_package(self): - package = get_package(self.CLOUD_TRANSFORM_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_routed_network(self.routed_network) - provision_config.add_dependent_deployment(self.talker_deployment) - self.logger.info('Deploying cloud transform package') - self.cloud_transform_deployment = self.deploy_package(package, provision_config) - self.assert_dependent_deployment(self.cloud_transform_deployment, [self.talker_deployment]) - - def deploy_listener_package(self, restart_policy): - package = get_package(self.LISTENER_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_device('default', self.device, ignore_device_config=['network_interface']) - provision_config.add_routed_network(self.routed_network) - provision_config.add_dependent_deployment(self.cloud_transform_deployment) - provision_config.add_restart_policy('default', restart_policy) - self.listener_deployment = self.deploy_package(package, provision_config) - self.assert_dependent_deployment(self.listener_deployment, [self.cloud_transform_deployment]) - - def test_deploy_cloud_transform(self): - self.deploy_talker_package() - self.talker_deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.talker_deployment) - self.deploy_cloud_transform_package() - self.cloud_transform_deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.cloud_transform_deployment) - self.deploy_listener_package(RestartPolicy.Always) - self.listener_deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.listener_deployment) - listener_provision_config = get_package(self.LISTENER_PACKAGE).get_provision_configuration() - listener_component_id = listener_provision_config.plan.get_component_id("default") - component_context = self.listener_deployment.provisionContext.component_context[listener_component_id] - self.assertEqual(component_context.component_override.restart_policy, RestartPolicy.Always) - self.assert_deployment_list_with_device_id() diff --git a/sdk_test/package/configuration_tests.py b/sdk_test/package/configuration_tests.py deleted file mode 100644 index 3154941f..00000000 --- a/sdk_test/package/configuration_tests.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import absolute_import -from unittest import TestCase - -import os -import hashlib - -from tempfile import mkdtemp -from shutil import rmtree, copyfile - -from sdk_test.config import Configuration - - -class ConfigurationTestCase(TestCase): - def setUp(self): - self.config = Configuration() - self.tmp_dir = mkdtemp() - self.download_tmp_dir = mkdtemp() - self.config_dir = 'test_files' - self.config_dir_path = os.path.join(os.path.dirname(__file__), '..', self.config_dir) - self.tree_names = ['warehouse'] - self.files = ( - 'warehouse', - 'warehouse/device.yaml', - 'warehouse/lena.png', - 'warehouse/robot_type', - 'warehouse/robot_type/magni', - 'warehouse/robot_type/magni/lena.png', - 'warehouse/robot_type/magni/device.yaml', - ) - - def tearDown(self): - rmtree(self.tmp_dir) - rmtree(self.download_tmp_dir) - - def setup_local_configuration_structure(self): - for file in self.files: - if '.' not in file: - os.mkdir(os.path.join(self.tmp_dir, file)) - else: - filename = os.path.basename(file) - src_file = os.path.join(self.config_dir_path, filename) - dst_file = os.path.join(self.tmp_dir, os.path.dirname(file), filename) - copyfile(src_file, dst_file) - - @staticmethod - def list_flatten_dir(tmp_dir): - flatten_file_struct = set() - for root, dirs, files in os.walk(tmp_dir): - dir_name = root[len(tmp_dir)+1:] - if dir_name: - flatten_file_struct.add(dir_name) - for filename in files: - flatten_file_struct.add(os.path.join(dir_name, filename)) - return flatten_file_struct - - def assert_checksum(self, src_file, dst_file): - with open(src_file, mode='rb') as f: - src_hash = hashlib.md5(f.read()) - src_hex_digest = src_hash.hexdigest() - with open(dst_file, mode='rb') as f: - dst_hash = hashlib.md5(f.read()) - dst_hex_digest = dst_hash.hexdigest() - self.assertEqual(src_hex_digest, dst_hex_digest) - - def assert_files(self): - upload_file_structure = set(self.files) - download_file_structure = self.list_flatten_dir(self.download_tmp_dir) - self.assertEqual(upload_file_structure, download_file_structure) - files = [x for x in self.files if '.' in x] - for filename in files: - self.assert_checksum(os.path.join(self.tmp_dir, filename), os.path.join(self.download_tmp_dir, filename)) - - def test_upload_configuration(self): - self.setup_local_configuration_structure() - self.config.client.upload_configurations(self.tmp_dir, self.tree_names, delete_existing_trees=True) - self.config.client.download_configurations(self.download_tmp_dir, self.tree_names) - self.assert_files() diff --git a/sdk_test/package/create_package_test.py b/sdk_test/package/create_package_test.py deleted file mode 100644 index cf8d2f8b..00000000 --- a/sdk_test/package/create_package_test.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import absolute_import -import unittest - -from rapyuta_io.utils.error import BadRequestError -from sdk_test.config import Configuration - - -class CreatePackage(unittest.TestCase): - - def setUp(self): - self.config = Configuration() - - def test_create_package_fails_for_bad_request(self): - invalid_manifest = { - "apiVersion": "v1.0.0", - "packageVersion": "v1.0.0", - "plans": [ - { - "components": [ - { - "name": "default", - "description": "", - "executables": [ - { - "name": "listenerExec", - "cmd": [ - "roslaunch listener listener.launch" - ] - } - ], - } - ], - } - ] - } - with self.assertRaises(BadRequestError): - self.config.client.create_package(invalid_manifest) - - diff --git a/sdk_test/package/delete_package_test.py b/sdk_test/package/delete_package_test.py deleted file mode 100644 index dc3047ca..00000000 --- a/sdk_test/package/delete_package_test.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import absolute_import -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from rapyuta_io.utils.error import PackageNotFound -from sdk_test.util import add_package, get_package - - -class DeletePackage(PackageTest): - - DELETE_MANIFEST = 'delete-package.json' - DELETE_PACKAGE = 'test-delete-package' - - def setUp(self): - self.config = Configuration() - add_package(self.DELETE_MANIFEST, self.DELETE_PACKAGE) - - def test_delete_package_using_package_object(self): - package = get_package(self.DELETE_PACKAGE) - packageId = package.packageId - package.delete() - expected_err_msg = 'Package not found' - with self.assertRaises(PackageNotFound) as e: - self.config.client.get_package(packageId) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_delete_package_using_client(self): - package = get_package(self.DELETE_PACKAGE) - packageId = package.packageId - self.config.client.delete_package(package_id=packageId) - expected_err_msg = 'Package not found' - with self.assertRaises(PackageNotFound) as e: - self.config.client.get_package(packageId) - self.assertEqual(str(e.exception), expected_err_msg) diff --git a/sdk_test/package/deployment_test.py b/sdk_test/package/deployment_test.py deleted file mode 100644 index fd3e8ba8..00000000 --- a/sdk_test/package/deployment_test.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import absolute_import - -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, add_package, delete_package, get_package -from rapyuta_io.clients.deployment import DeploymentPhaseConstants -from rapyuta_io.utils.error import BadRequestError - -class UpdateDeployment(PackageTest): - - CLOUD_NON_ROS_MANIFEST = 'cloud-non-ros.json' - CLOUD_NON_ROS_PACKAGE = 'test-cloud-non-ros-package' - CLOUD_NON_ROS_DEPLOYMENT = 'test-cloud-non-ros-deployment' - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - add_package(self.CLOUD_NON_ROS_MANIFEST,self.CLOUD_NON_ROS_PACKAGE) - self.cloud_non_ros_pkg = get_package(self.CLOUD_NON_ROS_PACKAGE) - provision_configuration = self.cloud_non_ros_pkg.get_provision_configuration() - self.deployment = self.deploy_package(self.cloud_non_ros_pkg, provision_configuration) - self.deployment.poll_deployment_till_ready() - - def tearDown(self): - self.deprovision_all_deployments([self.deployment]) - delete_package(self.CLOUD_NON_ROS_PACKAGE) - - def test_update_deployment_provisioning_deployment(self): - component_context = get_component_context(self.deployment.get("componentInfo", {})) - payload = { - "service_id": self.deployment["packageId"], - "plan_id": self.deployment["planId"], - "deployment_id": self.deployment["deploymentId"], - "context": { - "component_context": component_context - } - } - self.config.client.update_deployment(payload) - deployment = self.config.client.get_deployment(self.deployment["deploymentId"]) - self.assertEqual(deployment["phase"], DeploymentPhaseConstants.PROVISIONING) - - # tries to update deployment which is in provisioning state - with self.assertRaises(BadRequestError): - self.config.client.update_deployment(payload) - - def test_update_deployment_success(self): - self.logger.info("Started update deployment") - component_context = get_component_context(self.deployment.get("componentInfo", {})) - payload = { - "service_id": self.deployment["packageId"], - "plan_id": self.deployment["planId"], - "deployment_id": self.deployment["deploymentId"], - "context": { - "component_context": component_context - } - } - self.config.client.update_deployment(payload) - deployment = self.config.client.get_deployment(self.deployment["deploymentId"]) - self.assertEqual(deployment["phase"], DeploymentPhaseConstants.PROVISIONING) - - deployment.poll_deployment_till_ready() - deployment = self.config.client.get_deployment(self.deployment["deploymentId"]) - self.assertEqual(deployment["phase"], DeploymentPhaseConstants.SUCCEEDED) - -def get_component_context(component_info): - result = {} - for component in component_info: - comp = {} - executables = [] - executableMetaData = component.get("executableMetaData", []) or [] - for exec in executableMetaData: - # Component will be considered only if any of its executables is docker or build - if not (exec.get("docker") or exec.get("buildGUID")): - continue - executable = {} - if exec.get("buildGUID"): - executable["buildGUID"] = exec["buildGUID"] - if exec.get("docker"): - executable["docker"] = exec["docker"] - - executable["id"] = exec.get("id", "") - executable["name"] = exec.get("name", "") - executables.append(executable) - - if len(executables) > 0: - result[component["componentID"]] = comp - comp["component"] = {"executables": executables} - comp["update_deployment"] = True - - return result \ No newline at end of file diff --git a/sdk_test/package/get_all_package_test.py b/sdk_test/package/get_all_package_test.py deleted file mode 100644 index a4cdf3dd..00000000 --- a/sdk_test/package/get_all_package_test.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import absolute_import -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import add_package, delete_package, get_logger -from rapyuta_io.clients.package import Package - - -class GetAllPackage(PackageTest): - - CLOUD_NON_ROS_MANIFEST = 'cloud-non-ros.json' - CLOUD_NON_ROS_PACKAGE = 'test-get-all-packages-cloud-non-ros-pkg' - NGINX_SINGLE_COMPONENT_MANIFEST = 'nginx-single-component.json' - NGINX_SINGLE_COMPONENT_PACKAGE = 'test-get-all-packages-nginx-single-component-pkg' - PACKAGE_VERSION = 'v1.0.0' - - @classmethod - def setUpClass(cls): - add_package(cls.CLOUD_NON_ROS_MANIFEST, cls.CLOUD_NON_ROS_PACKAGE) - add_package(cls.NGINX_SINGLE_COMPONENT_MANIFEST, cls.NGINX_SINGLE_COMPONENT_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.CLOUD_NON_ROS_PACKAGE) - delete_package(cls.NGINX_SINGLE_COMPONENT_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - - def assert_package_exists(self, package_list, name, version=None): - for package in package_list: - if package.packageName == name and (not version or package.packageVersion): - return - - return self.fail("package not found in the list") - - def test_get_all_packages_no_filter_parameters(self): - packages = self.config.client.get_all_packages() - for pkg in packages: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assert_package_exists(package_list=packages, name=self.NGINX_SINGLE_COMPONENT_PACKAGE) - self.assert_package_exists(package_list=packages, name=self.CLOUD_NON_ROS_PACKAGE) - - def test_get_all_packages_filter_name_only(self): - name = self.CLOUD_NON_ROS_PACKAGE - packages = self.config.client.get_all_packages(name=name) - for pkg in packages: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assert_package_exists(package_list=packages, name=name) - - def test_get_all_packages_filter_version_only(self): - version = self.PACKAGE_VERSION - packages = self.config.client.get_all_packages(version=version) - for pkg in packages: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assertEqual(pkg.packageVersion, version) - self.assert_package_exists(package_list=packages, name=self.NGINX_SINGLE_COMPONENT_PACKAGE) - self.assert_package_exists(package_list=packages, name=self.CLOUD_NON_ROS_PACKAGE) - - def test_get_all_packages_filter_name_filter_version(self): - name = self.NGINX_SINGLE_COMPONENT_PACKAGE - version = self.PACKAGE_VERSION - packages = self.config.client.get_all_packages(name=name, version=version) - for pkg in packages: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assert_package_exists(package_list=packages, name=self.NGINX_SINGLE_COMPONENT_PACKAGE, version=version) - diff --git a/sdk_test/package/inbound_incoming_scoped_targeted_test.py b/sdk_test/package/inbound_incoming_scoped_targeted_test.py deleted file mode 100644 index 1882a7a0..00000000 --- a/sdk_test/package/inbound_incoming_scoped_targeted_test.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import absolute_import -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, add_package, delete_package, get_package - - -class InboundIncomingScopedTargetedTestCase(PackageTest): - - INBOUND_INCOMING_SCOPED_TARGETED_MANIFEST = 'inbound-incoming-scoped-targeted.json' - TALKER_CLOUD_MANIFEST = 'talker-cloud.json' - - INBOUND_INCOMING_SCOPED_TARGETED_PACKAGE = 'test-inbound-incoming-scoped-targeted-pkg' - TALKER_CLOUD_PACKAGE = 'test-inbound-incoming-scoped-targeted-talker-cloud-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.INBOUND_INCOMING_SCOPED_TARGETED_MANIFEST, - cls.INBOUND_INCOMING_SCOPED_TARGETED_PACKAGE) - add_package(cls.TALKER_CLOUD_MANIFEST, - cls.TALKER_CLOUD_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.INBOUND_INCOMING_SCOPED_TARGETED_PACKAGE) - delete_package(cls.TALKER_CLOUD_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.talker_deployment = None - self.listener_deployment = None - self.routed_network = self.create_cloud_routed_network('cloud_routed_network') - - def tearDown(self): - self.deprovision_all_deployments([self.talker_deployment, self.listener_deployment]) - self.routed_network.delete() - - def deploy_inbound_incoming_scoped_targeted_listener_package(self): - package = get_package(self.INBOUND_INCOMING_SCOPED_TARGETED_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_routed_network(self.routed_network) - self.logger.info('Deploying listener package') - self.listener_deployment = self.deploy_package(package, provision_config) - self.listener_deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.listener_deployment) - - def test_inbound_incoming_scoped_targeted(self): - self.deploy_inbound_incoming_scoped_targeted_listener_package() - package = get_package(self.TALKER_CLOUD_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_routed_network(self.routed_network) - self.logger.info('Deploying talker package') - self.talker_deployment = self.deploy_package(package, provision_config) - self.talker_deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.talker_deployment) diff --git a/sdk_test/package/native_network_tests.py b/sdk_test/package/native_network_tests.py deleted file mode 100644 index 9831f9e7..00000000 --- a/sdk_test/package/native_network_tests.py +++ /dev/null @@ -1,198 +0,0 @@ -from __future__ import absolute_import -from rapyuta_io import DeploymentStatusConstants, DeviceArch -from rapyuta_io.clients.deployment import DeploymentPhaseConstants -from rapyuta_io.clients.native_network import NativeNetwork, Parameters -from rapyuta_io.clients.common_models import Limits -from rapyuta_io.clients.package import Runtime, ROSDistro -from rapyuta_io.utils.utils import generate_random_value -from sdk_test.config import Configuration -from sdk_test.device.device_test import DeviceTest -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, get_package, add_package, delete_package - -NETWORK_INTERFACE = 'network_interface' - - -class NativeNetworkTest(PackageTest, DeviceTest): - native_network = None - - TALKER_CLOUD_MANIFEST = 'talker-cloud.json' - TALKER_CLOUD_PACKAGE = 'test-native-network-talker-cloud-pkg' - TALKER_DEVICE_MANIFEST = 'talker-docker.json' - TALKER_DEVICE_PACKAGE = 'test-native-network-talker-device-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.TALKER_CLOUD_MANIFEST, cls.TALKER_CLOUD_PACKAGE) - add_package(cls.TALKER_DEVICE_MANIFEST, cls.TALKER_DEVICE_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.TALKER_CLOUD_PACKAGE) - delete_package(cls.TALKER_DEVICE_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.name = 'net-' + generate_random_value() - self.ros_distro = ROSDistro.MELODIC - self.runtime = Runtime.CLOUD - self.parameters = Parameters(Limits(cpu=1, memory=1024)) - self.device_runtime = Runtime.DEVICE - self.docker_device = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose')[0] - self.docker_device.refresh() - self.device_parameters = Parameters(limits=None, device=self.docker_device, network_interface='docker0') - - def add_network_interface_config_variable(self, device): - self.logger.info('Adding network interface config variable') - config_vars = device.get_config_variables() - for config_var in config_vars: - if config_var.key == NETWORK_INTERFACE: - config_var.value = 'docker0' - device.update_config_variable(config_var) - return - device.add_config_variable(NETWORK_INTERFACE, 'docker0') - self.logger.info('Added network interface config variable') - - def delete_network_interface_config_variable(self, device): - self.logger.info('Removing network interface config variable') - config_vars = device.get_config_variables() - for config_var in config_vars: - if config_var.key == NETWORK_INTERFACE: - device.delete_config_variable(config_id=config_var.id) - break - self.logger.info('Removed network interface config variable') - - def assert_native_network_status(self): - self.logger.info('Checking the deployment status of the native network {}'.format(self.name)) - status = self.native_network.get_status() - self.assertEqual(status.status, DeploymentStatusConstants.RUNNING.value) - self.assertEqual(status.phase, DeploymentPhaseConstants.SUCCEEDED.value) - self.logger.info('native network %s(%s) started successfully' % (self.native_network.name, - self.native_network.guid)) - - def assert_native_network_fields(self, native_network): - self.logger.info('comparing the details the native network {} just fetched'.format(self.name)) - self.assertEqual(self.native_network.name, native_network.name) - self.assertEqual(self.native_network.runtime, native_network.runtime) - self.assertEqual(self.native_network.ros_distro, native_network.ros_distro) - self.assertEqual(self.native_network.parameters.limits.cpu, native_network.parameters.limits.cpu) - self.assertEqual(self.native_network.parameters.limits.memory, native_network.parameters.limits.memory) - self.assertEqual(self.native_network.created_at, native_network.created_at) - self.assertEqual(self.native_network.updated_at, native_network.updated_at) - self.assertEqual(self.native_network.guid, native_network.guid) - self.assertEqual(self.native_network.owner_project, native_network.owner_project) - self.assertEqual(self.native_network.creator, native_network.creator) - self.assertEqual(self.native_network.internal_deployment_guid, - native_network.internal_deployment_guid) - self.assertEqual(self.native_network.internal_deployment_status.phase, - native_network.internal_deployment_status.phase) - self.logger.info('successfully checked the contents of the native network'.format(self.name)) - - def assert_native_network_present_in_list(self, all_native_network): - self.logger.info('Checking the presence of native network {}'.format(self.name)) - guid = self.native_network.guid - native_network_list = list(filter(lambda network: network.guid == guid, all_native_network)) - self.logger.info('Checking if only one native native network with id {} is present' - .format(self.native_network.guid)) - native_network = native_network_list[0] - self.assertEqual(len(native_network_list), 1) - self.assertEqual(self.native_network.name, native_network.name) - self.logger.info('native network {} present'.format(self.name)) - - def assert_native_network_stopped(self): - self.logger.info('Checking if the native network {} stopped'.format(self.name)) - guid = self.native_network.guid - all_native_network = self.config.client.list_native_networks() - native_network = list(filter(lambda network: network.guid == guid, all_native_network))[0] - self.assertEqual(native_network.internal_deployment_status.phase, - DeploymentPhaseConstants.DEPLOYMENT_STOPPED.value) - self.logger.info('native network {} stopped'.format(self.name)) - - def validate_refresh(self, guid): - partial_net = [n for n in self.config.client.list_native_networks() if n.guid == guid][0] - self.assertTrue(partial_net.is_partial) - self.assertFalse(partial_net.internal_deployment_status.status) - partial_net.refresh() - self.assertFalse(partial_net.is_partial) - self.assertTrue(partial_net.internal_deployment_status.status) - - def test_01_create_native_network(self): - self.logger.info('creating native network {}'.format(self.name)) - native_network_payload = NativeNetwork(self.name, self.runtime, self.ros_distro, self.parameters) - self.native_network = self.config.client.create_native_network(native_network_payload) - self.logger.info('polling till the native network {} is ready'.format(self.name)) - self.native_network.poll_native_network_till_ready() - self.__class__.native_network = self.config.client.get_native_network(self.native_network.guid) - self.assert_native_network_status() - self.validate_refresh(self.native_network.guid) - - def test_02_get_native_network(self): - self.logger.info('fetching the native network {} just created'.format(self.name)) - guid = self.native_network.guid - native_network = self.config.client.get_native_network(guid) - self.assert_native_network_fields(native_network) - - def test_03_list_native_networks(self): - self.logger.info('fetching the list of all the native networks') - all_native_network = self.config.client.list_native_networks() - self.assert_native_network_present_in_list(all_native_network) - - def test_04_add_native_network_to_package(self): - self.logger.info('Started creating package talker component') - app_package = get_package(self.TALKER_CLOUD_PACKAGE) - prov_config = app_package.get_provision_configuration() - self.logger.info('adding the native network {} to the provision configuration of the package'.format(self.name)) - prov_config.add_native_network(self.native_network) - guid = self.native_network.guid - self.assertEqual(prov_config.context['nativeNetworks'], [{"guid": guid}]) - self.logger.info('creating deployment') - deployment = app_package.provision("test_deployment", prov_config) - self.logger.info('polling till deployment is ready') - deployment.poll_deployment_till_ready() - self.logger.info('deployment is ready') - deployment.deprovision() - self.logger.info('de-provisioning the deployment') - - def test_05_delete_native_network(self): - self.logger.info('deleting the native network {}'.format(self.name)) - guid = self.native_network.guid - self.config.client.delete_native_network(guid) - self.assert_native_network_stopped() - - def test_06_create_device_native_network(self): - self.add_network_interface_config_variable(self.docker_device) - self.logger.info('Started creating device native network') - native_network_payload = NativeNetwork(self.name, Runtime.DEVICE, self.ros_distro, self.device_parameters) - self.native_network = self.config.client.create_native_network(native_network_payload) - guid = self.native_network.guid - self.logger.info('polling till the native network {} is ready'.format(self.name)) - self.native_network.poll_native_network_till_ready() - self.__class__.native_network = self.config.client.get_native_network(guid) - self.assert_native_network_status() - self.validate_refresh(guid) - self.assertEqual(self.native_network.runtime, self.device_runtime) - - self.add_network_interface_config_variable(self.docker_device) - self.logger.info('Started creating package talker component') - app_package = get_package(self.TALKER_DEVICE_PACKAGE) - prov_config = app_package.get_provision_configuration() - prov_config.add_device('default', self.docker_device) - prov_config.add_native_network(self.native_network, 'docker0') - self.assertEqual(prov_config.context['nativeNetworks'], [{"guid": guid, "bindParameters": - {"NETWORK_INTERFACE": 'docker0'}}]) - self.logger.info('creating deployment') - ignored_device_configs = ['ros_workspace', 'ros_distro'] - deployment = self.deploy_package(app_package, prov_config, device=self.docker_device, - ignored_device_configs=ignored_device_configs) - self.logger.info('polling till deployment is ready') - deployment.poll_deployment_till_ready(retry_count=50) - self.logger.info('deployment is ready') - deployment.deprovision() - self.logger.info('deprovisioned the deployment') - self.delete_network_interface_config_variable(self.docker_device) - - self.logger.info('Delete routed network with guid : %s' % guid) - self.config.client.delete_native_network(guid) - self.assert_native_network_stopped() - self.delete_network_interface_config_variable(self.docker_device) diff --git a/sdk_test/package/noetic_test.py b/sdk_test/package/noetic_test.py deleted file mode 100644 index e4d83c52..00000000 --- a/sdk_test/package/noetic_test.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import absolute_import - -from rapyuta_io.clients.package import ROSDistro - -from sdk_test.package.package_test import PackageTest -from sdk_test.config import Configuration -from sdk_test.util import get_logger, get_package, add_package, delete_package, \ - add_cloud_native_network, get_native_network, delete_native_network, delete_build - - -class NoeticTest(PackageTest): - - TALKER_MANIFEST = 'talker-noetic.json' - TALKER_BUILD = 'test-noetic-talker-build' - TALKER_PACKAGE = 'test-noetic-talker-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.TALKER_MANIFEST, cls.TALKER_PACKAGE, build_map={ - 'talker': {'talkerExec': (cls.TALKER_BUILD, cls.TALKER_MANIFEST)}, - }) - add_cloud_native_network('noetic_cloud_network', ros_distro=ROSDistro.NOETIC) - - @classmethod - def tearDownClass(cls): - delete_package(cls.TALKER_PACKAGE) - delete_native_network('noetic_cloud_network') - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.native_network = get_native_network('noetic_cloud_network') - self.deployments = [] - - def tearDown(self): - self.deprovision_all_deployments(self.deployments) - - def test_scoped_targeted_package(self): - package = get_package(self.TALKER_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_native_network(self.native_network) - deployment = self.deploy_package(package, provision_config) - deployment.poll_deployment_till_ready() - self.assert_deployment_status(deployment) - self.deployments.append(deployment) diff --git a/sdk_test/package/package_test.py b/sdk_test/package/package_test.py deleted file mode 100644 index 0815f104..00000000 --- a/sdk_test/package/package_test.py +++ /dev/null @@ -1,151 +0,0 @@ -from __future__ import absolute_import - -import re -import unittest - -import requests - -from rapyuta_io import DeploymentStatusConstants, ROSDistro -from rapyuta_io.clients.deployment import Deployment, DeploymentPhaseConstants -from rapyuta_io.clients.persistent_volumes import VolumeInstance, PersistentVolumes, DiskCapacity -from rapyuta_io.utils import to_objdict, RestClient -from rapyuta_io.utils.utils import generate_random_value - -DEFAULT_DISK_CAPACITY = DiskCapacity.GiB_32 - - -class PackageTest(unittest.TestCase): - - def setUp(self): - self.logger = None - - def get_persistent_volume(self): - persistent_volume = self.config.client.get_persistent_volume() - self.assertIsNotNone(persistent_volume, 'Persistent volume package should not be empty') - self.assertTrue(isinstance(persistent_volume, PersistentVolumes), - 'Object should be instance of PersistanceVolumes class') - self.assertEqual('io-public-persistent-volume', persistent_volume.packageId, - 'Package should be same') - return persistent_volume - - def get_persistent_volume_instance(self, instance_name, disk_capacity=DEFAULT_DISK_CAPACITY): - self.logger.info('Creating volume instance') - persistent_volume = self.get_persistent_volume() - volume_instance = persistent_volume.create_volume_instance(instance_name, disk_capacity) - self.assertTrue(isinstance(volume_instance, VolumeInstance), - 'Object should be an instance of VolumeInstance class') - return volume_instance - - def deploy_package(self, package, provision_config, device=None, ignored_device_configs=None): - self.logger.info('Started deploying the package %s' % package.packageName) - deployment_name = generate_random_value() - deployment = package.provision(deployment_name, provision_config) - self.assert_deployment(deployment) - self.assert_deployment_info(deployment_name, deployment, package) - self.assert_component_parameters(deployment, package.plans[0], device, - ignored_device_configs) - return deployment - - def assert_deployment(self, deployment): - self.assertTrue(isinstance(deployment, Deployment), - 'Object should be an instance of Deployment class') - self.logger.info('Package (%s) deployed (%s) successfully' - % (deployment.packageName, deployment.name)) - - def assert_component_parameters(self, deployment, plan, device=None, - ignored_device_configs=None): - if ignored_device_configs is None: - ignored_device_configs = [] - if device and device.get_runtime() == device.PRE_INSTALLED: - ignored_device_configs.append('rosbag_mount_path') - self.logger.info('Validating component parameters for the deployment: %s' % deployment.name) - for component in plan.internalComponents: - component_id = component.componentId - component_params = deployment.parameters[component_id] - self.assertEqual(component_params["component_id"], component_id) - if component.runtime == "device" and device: - for config_var in device.config_variables: - if config_var.key in ignored_device_configs: - continue - self.assertEqual(component_params[config_var.key], config_var.value) - self.assertEqual(component_params["device_id"], device.uuid) - - def assert_deployment_info(self, deployment_name, deployment, package): - self.logger.info('Validating deployment info for the deployment: %s(%s)' - % (deployment.name, deployment.packageName)) - self.assertEqual(deployment.name, deployment_name) - self.assertEqual(deployment.packageId, package.packageId) - self.assertEqual(deployment.packageName, package.packageName) - self.assertEqual(deployment.planId, package.plans[0].planId) - - def assert_dependent_deployment(self, deployment, dependent_deployments): - self.logger.info('Validating dependent deployment info for the deployment: %s(%s)' - % (deployment.name, deployment.packageName)) - dependent_deployment_id_list = list() - for dependent_deployment in deployment.dependentDeployments: - dependent_deployment_id_list.append(dependent_deployment["dependentDeploymentId"]) - - for dependent_deployment in dependent_deployments: - self.assertIn(dependent_deployment.deploymentId, dependent_deployment_id_list) - - def assert_deployment_status(self, deployment): - self.logger.info('Checking deployment status') - deployment_status = deployment.get_status() - self.assertEqual(deployment_status.status, DeploymentStatusConstants.RUNNING.value) - self.assertEqual(deployment_status.phase, DeploymentPhaseConstants.SUCCEEDED.value) - self.logger.info('Deployment %s(%s) started successfully' % (deployment.name, - deployment.packageId)) - - def assert_endpoint_url(self, url): - regex = re.compile( - r'^(?:http|ftp)s?://' - r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' - r'localhost|' - r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' - r'(?::\d+)?' - r'(?:/?|[/?]\S+)$', re.IGNORECASE) - self.assertRegexpMatches(url, regex, 'Endpoint should ben an url') - - def assert_static_route(self, url): - # Asserts that the URL (nginx server) is reachable and is returning a success status code - response = RestClient(url).execute() - self.assertEqual(response.status_code, requests.codes.OK) - - def get_service_binding(self, deployment): - self.logger.info('Sending binding request for the deployment %s' % deployment.name) - binding = deployment.get_service_binding() - self.assertIsNotNone(binding) - self.assertIn('credentials', binding, 'Credentials not found on the binding response') - if not bool(binding): - raise AssertionError('binding result should not empty') - binding_obj = to_objdict(binding) - return binding_obj - - def deprovision_all_deployments(self, deployments): - self.logger.info("Tear down!. Stopping all the deployments") - retry_count = 5 - total_deployments = 0 - success_count = 0 - for deployment in deployments: - if not deployment: - continue - try: - total_deployments = total_deployments + 1 - if isinstance(deployment, Deployment): - deployment.deprovision(retry_count) - self.logger.info('Deployment stopped: %s' % deployment.name) - success_count = success_count + 1 - elif isinstance(deployment, VolumeInstance): - deployment.destroy_volume_instance(retry_count) - self.logger.info('Volume instance deleted: %s' % deployment.name) - success_count = success_count + 1 - except Exception as err: - self.logger.error('Failed to stop deployment %s' % deployment.name) - self.logger.error(err) - self.logger.info('%d Deployment(s) were stopped out of %d' % (success_count, - total_deployments)) - - def create_cloud_routed_network(self, name, ros_distro=ROSDistro.KINETIC): - routed_network = self.config.client.create_cloud_routed_network(name, ros_distro, True) - routed_network.poll_routed_network_till_ready() - return routed_network diff --git a/sdk_test/package/rosbag_test.py b/sdk_test/package/rosbag_test.py deleted file mode 100644 index e58266bd..00000000 --- a/sdk_test/package/rosbag_test.py +++ /dev/null @@ -1,524 +0,0 @@ -# TODO(senapati): This test file is commented because it uses builds -# which is deprecated -# from __future__ import absolute_import -# -# import math -# import os -# import time -# from time import sleep -# -# from rapyuta_io import DeviceArch -# from rapyuta_io.clients.rosbag import ROSBagJob, ROSBagOptions, ROSBagJobStatus, UploadOptions, ROSBagUploadTypes, \ -# ROSBagOnDemandUploadOptions, ROSBagTimeRange, OverrideOptions, TopicOverrideInfo -# from rapyuta_io.utils.utils import generate_random_value -# from sdk_test.config import Configuration -# from sdk_test.package.package_test import PackageTest -# from sdk_test.util import get_logger, get_package, add_package, delete_package, add_build -# -# class ROSBagJobTest(PackageTest): -# deployment = None -# deployment_with_fast_talker = None -# deployment_with_throttling = None -# deployment_with_latching = None -# device_rosbag_job = None -# cloud_rosbag_job = None -# throttling_rosbag_job = None -# latching_rosbag_job = None -# continuous_upload_type_rosbag = None -# -# TALKER_MANIFEST = 'talker.json' -# TALKER_BUILD = 'test-rosbag-job-talker-pkg' -# -# TALKER_CLOUD_DEVICE_MANIFEST = 'talker-cloud-device.json' -# TALKER_CLOUD_DEVICE_PACKAGE = 'test-rosbag-job-talker-cloud-device-pkg' -# -# ROSBAG_TALKER_MANIFEST = 'rosbag-talker-cloud.json' -# ROSBAG_TALKER_PACKAGE = 'test-rosbag-talker-cloud-pkg' -# -# FAST_TALKER_DEVICE_WITH_ROSBAGS_MANIFEST = 'fast-talker-device-docker-with-rosbags.json' -# FAST_TALKER_DEVICE_WITH_ROSBAGS_PACKAGE = 'fast-talker-device-docker-with-rosbags-pkg' -# -# THROTTLE_LATCH_BUILD_MANIFEST = 'throttle-latch-build.json' -# THROTTLE_LATCH_BUILD_NAME = 'throttle-latch-build' -# -# THROTTLING_PACKAGE_MANIFEST = 'throttling-pkg.json' -# THROTTLING_PACKAGE_NAME = 'throttling-pkg' -# -# LATCHING_PACKAGE_MANIFEST = 'latching-pkg.json' -# LATCHING_PACKAGE_NAME = 'latching-pkg' -# -# @classmethod -# def setUpClass(cls): -# add_build(cls.TALKER_MANIFEST, cls.TALKER_BUILD) -# -# add_package(cls.TALKER_CLOUD_DEVICE_MANIFEST, cls.TALKER_CLOUD_DEVICE_PACKAGE, -# build_map={ -# 'talker-device': {'talker': ('talker-build', 'talker.json')}, -# 'talker-cloud': {'talker': ('talker-build', 'talker.json')}, -# }) -# add_package(cls.ROSBAG_TALKER_MANIFEST, cls.ROSBAG_TALKER_PACKAGE, -# build_map={ -# 'talker-cloud': {'talker': ('talker-build', 'talker.json')}, -# }) -# add_package(cls.FAST_TALKER_DEVICE_WITH_ROSBAGS_MANIFEST, cls.FAST_TALKER_DEVICE_WITH_ROSBAGS_PACKAGE, -# build_map={ -# 'talker-fast-device': {'talker': ('talker-build', 'talker.json')} -# }) -# add_build(cls.THROTTLE_LATCH_BUILD_MANIFEST, cls.THROTTLE_LATCH_BUILD_NAME) -# add_package(cls.THROTTLING_PACKAGE_MANIFEST, cls.THROTTLING_PACKAGE_NAME, -# build_map={ -# 'throttling-component': { -# 'throttling-executable': ('throttle-latch-build', 'throttle-latch-build.json')} -# }) -# add_package(cls.LATCHING_PACKAGE_MANIFEST, cls.LATCHING_PACKAGE_NAME, -# build_map={ -# 'latching-component': { -# 'latching-executable': ('throttle-latch-build', 'throttle-latch-build.json')} -# }) -# -# @classmethod -# def tearDownClass(cls): -# delete_package(cls.TALKER_CLOUD_DEVICE_PACKAGE, delete_builds=False) -# delete_package(cls.ROSBAG_TALKER_PACKAGE, delete_builds=False) -# delete_package(cls.FAST_TALKER_DEVICE_WITH_ROSBAGS_PACKAGE) -# delete_package(cls.THROTTLING_PACKAGE_NAME, delete_builds=False) -# delete_package(cls.LATCHING_PACKAGE_NAME) -# -# def setUp(self): -# self.config = Configuration() -# self.logger = get_logger() -# self.package = [ -# get_package(self.TALKER_CLOUD_DEVICE_PACKAGE), -# get_package(self.ROSBAG_TALKER_PACKAGE), -# get_package(self.FAST_TALKER_DEVICE_WITH_ROSBAGS_PACKAGE), -# get_package(self.THROTTLING_PACKAGE_NAME), -# get_package(self.LATCHING_PACKAGE_NAME) -# ] -# self.device = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose')[0] -# self.bag_filename = 'test.bag' -# self.rosbag_job_name = 'test-rosbag-defs' -# -# def tearDown(self): -# if os.path.exists(self.bag_filename): -# os.remove(self.bag_filename) -# -# def test_01_create_deployment_with_rosbag_jobs(self): -# self.logger.info('creating deployment with rosbag jobs') -# device_rosbag_job = ROSBagJob('device-init-job', ROSBagOptions(all_topics=True), -# upload_options=UploadOptions(upload_type=ROSBagUploadTypes.ON_STOP)) -# cloud_rosbag_job = ROSBagJob('cloud-init-job', ROSBagOptions(all_topics=True)) -# provision_config = self.package[0].get_provision_configuration() -# ignored_device_configs = ['network_interface'] -# provision_config.add_device('talker-device', self.device, ignore_device_config=ignored_device_configs) -# provision_config.add_rosbag_job('talker-device', device_rosbag_job) -# provision_config.add_rosbag_job('talker-cloud', cloud_rosbag_job) -# deployment = self.deploy_package(self.package[0], provision_config, -# ignored_device_configs=ignored_device_configs) -# deployment.poll_deployment_till_ready(retry_count=100, sleep_interval=5) -# self.__class__.deployment = self.config.client.get_deployment(deployment.deploymentId) -# self.assert_rosbag_jobs_present(self.deployment.deploymentId, [device_rosbag_job.name, cloud_rosbag_job.name], -# [ROSBagJobStatus.STARTING, ROSBagJobStatus.RUNNING]) -# self.assert_rosbag_jobs_in_project(device_rosbag_job.name) -# -# def test_02_create_rosbag_jobs(self): -# self.logger.info('creating rosbag jobs on cloud and device') -# self.__class__.device_rosbag_job = self.create_rosbag_job('talker-device', is_device=True) -# self.__class__.cloud_rosbag_job = self.create_rosbag_job('talker-cloud') -# self.assert_rosbag_jobs_present(self.deployment.deploymentId, -# [self.device_rosbag_job.name, self.cloud_rosbag_job.name], -# [ROSBagJobStatus.RUNNING, ROSBagJobStatus.STARTING]) -# -# def test_03_stop_rosbag_jobs(self): -# self.wait_till_jobs_are_running(self.deployment.deploymentId, [self.cloud_rosbag_job.guid, -# self.device_rosbag_job.guid], -# sleep_interval_in_sec=5) -# self.logger.info('stopping the running rosbag jobs on cloud and device') -# self.config.client.stop_rosbag_jobs(self.deployment.deploymentId, guids=[ -# self.device_rosbag_job.guid, self.cloud_rosbag_job.guid]) -# self.assert_rosbag_jobs_present(self.deployment.deploymentId, -# [self.device_rosbag_job.name, self.cloud_rosbag_job.name], -# [ROSBagJobStatus.STOPPING, ROSBagJobStatus.STOPPED]) -# -# def test_04_rosbag_blobs(self): -# blobs = self.wait_till_blobs_are_uploaded(sleep_interval_in_sec=5) -# self.logger.info('validating the uploaded rosbag blobs for the stopped jobs') -# self.assert_rosbag_blobs_of_device(blobs) -# self.assert_rosbag_blobs(blobs) -# -# def test_05_auto_stop_rosbag_jobs_on_deprovision(self): -# jobs = self.config.client.list_rosbag_jobs(deployment_id=self.deployment.deploymentId, -# statuses=[ROSBagJobStatus.RUNNING]) -# job_ids = list(map(lambda job: job.guid, jobs)) -# self.assertEqual(2, len(jobs)) -# init_job_names = list(map(lambda job: job.name, jobs)) -# self.logger.info('deprovisioning deployment with running rosbag jobs') -# self.deployment.deprovision() -# self.assert_rosbag_jobs_present(self.deployment.deploymentId, init_job_names, -# [ROSBagJobStatus.STOPPING, ROSBagJobStatus.STOPPED]) -# self.wait_till_blobs_are_uploaded(job_ids=job_ids, sleep_interval_in_sec=5) -# -# def test_06_create_deployment_with_rosbag_jos_in_package_config(self): -# provision_config = self.package[1].get_provision_configuration() -# deployment = self.deploy_package(self.package[1], provision_config, -# ignored_device_configs=['network_interface']) -# deployment.poll_deployment_till_ready(retry_count=100, sleep_interval=5) -# self.assert_rosbag_jobs_present(deployment.deploymentId, [self.rosbag_job_name], -# [ROSBagJobStatus.STARTING, ROSBagJobStatus.RUNNING]) -# jobs = self.config.client.list_rosbag_jobs(deployment.deploymentId) -# job_ids = [job.guid for job in jobs] -# self.wait_till_jobs_are_running(deployment.deploymentId) -# self.config.client.stop_rosbag_jobs(deployment.deploymentId) -# self.wait_till_blobs_are_uploaded(job_ids=job_ids) -# deployment.deprovision() -# -# def test_07_rosbag_job_with_upload_type_continuous(self): -# job_name = 'continuous_upload_type' -# -# self.logger.info('creating device deployment with rosbag job with upload type as Continuous') -# provision_config = self.package[2].get_provision_configuration() -# ignored_device_configs = ['network_interface'] -# provision_config.add_device('talker-fast-device', self.device, ignore_device_config=ignored_device_configs) -# deployment = self.deploy_package(self.package[2], provision_config, -# ignored_device_configs=ignored_device_configs) -# deployment.poll_deployment_till_ready(retry_count=100, sleep_interval=5) -# self.__class__.deployment_with_fast_talker = self.config.client.get_deployment(deployment.deploymentId) -# -# self.assert_rosbag_jobs_present(self.deployment_with_fast_talker.deploymentId, [job_name], -# [ROSBagJobStatus.STARTING, ROSBagJobStatus.RUNNING]) -# self.assert_rosbag_jobs_in_project(job_name) -# self.__class__.continuous_upload_type_rosbag = self.get_job_by_job_name(deployment.deploymentId, job_name) -# uploaded_blobs = self.wait_till_blobs_are_uploaded(job_ids=[self.continuous_upload_type_rosbag.guid]) -# -# # to ensure first split is uploaded because it continuously -# # uploads -# first_bag_uploaded = False -# for blob in uploaded_blobs: -# if blob.filename.endswith('_0.bag'): -# first_bag_uploaded = True -# break -# -# self.assertTrue(first_bag_uploaded) -# -# self.config.client.stop_rosbag_jobs( -# deployment_id=deployment.deploymentId, -# guids=[self.continuous_upload_type_rosbag.guid] -# ) -# -# def test_08_rosbag_job_with_upload_type_on_demand(self): -# self.logger.info('creating rosbag job with upload type as OnDemand') -# -# job_name = 'on_demand_upload_type' -# component_instance_id = self.deployment_with_fast_talker.get_component_instance_id('talker-fast-device') -# -# job_req = ROSBagJob( -# name=job_name, -# deployment_id=self.deployment_with_fast_talker.deploymentId, -# component_instance_id=component_instance_id, -# rosbag_options=ROSBagOptions( -# all_topics=True, -# max_splits=10, -# max_split_size=10 -# ), -# upload_options=UploadOptions(upload_type=ROSBagUploadTypes.ON_DEMAND), -# ) -# -# rosbag_creation_time = int(time.time()) -# job = self.config.client.create_rosbag_job(job_req) -# -# start_recording_duration = 8 -# split_duration = 60 -# -# self.logger.info('sleeping for sometime for recording to continue') -# sleep(start_recording_duration + (split_duration * 2)) -# -# from_time = rosbag_creation_time + start_recording_duration + split_duration + 10 -# to_time = from_time + split_duration -# on_demand_opts = ROSBagOnDemandUploadOptions( -# time_range=ROSBagTimeRange( -# from_time=from_time, -# to_time=to_time -# ) -# ) -# -# job.patch(on_demand_options=on_demand_opts) -# -# uploaded_blobs = self.wait_till_blobs_are_uploaded(job_ids=[job.guid]) -# -# # to ensure first split is not uploaded because it is not -# # within the time range provided -# for blob in uploaded_blobs: -# self.assertFalse(blob.filename.endswith('_0.bag')) -# -# self.deployment_with_fast_talker.deprovision() -# -# def test_09_rosbag_job_throttling(self): -# """ -# Default publishing rate on channels -# /topic1: 15 -# /topic2: 30 -# /topic3: 5 -# /topic4: 20 -# """ -# difference_margin = 8 -# topic2_throttled_freq = 15 -# topic3_throttled_freq = 2 -# self.logger.info('deploying throttling package') -# device_rosbag_job = ROSBagJob('device-init-job', ROSBagOptions(all_topics=True), -# upload_options=UploadOptions(upload_type=ROSBagUploadTypes.ON_STOP)) -# provision_config = self.package[3].get_provision_configuration() -# ignored_device_configs = ['network_interface'] -# provision_config.add_device('throttling-component', self.device, ignore_device_config=ignored_device_configs) -# provision_config.add_rosbag_job('throttling-component', device_rosbag_job) -# deployment = self.deploy_package(self.package[3], provision_config, -# ignored_device_configs=ignored_device_configs) -# deployment.poll_deployment_till_ready(retry_count=100, sleep_interval=5) -# self.__class__.deployment_with_throttling = self.config.client.get_deployment(deployment.deploymentId) -# -# component_instance_id = self.deployment_with_throttling.get_component_instance_id('throttling-component') -# throttling_rosbag_job = ROSBagJob('rosbag-test-throttling', -# deployment_id=self.deployment_with_throttling.deploymentId, -# component_instance_id=component_instance_id, -# rosbag_options=ROSBagOptions(all_topics=True), -# upload_options=UploadOptions(upload_type=ROSBagUploadTypes.ON_STOP), -# override_options=OverrideOptions( -# topic_override_info=[ -# TopicOverrideInfo('/topic2', topic2_throttled_freq, False), -# TopicOverrideInfo('/topic3', topic3_throttled_freq, False), -# ], -# exclude_topics=['/topic4'] -# )) -# self.__class__.throttling_rosbag_job = self.config.client.create_rosbag_job(throttling_rosbag_job) -# self.assert_rosbag_jobs_present(self.deployment_with_throttling.deploymentId, -# [throttling_rosbag_job.name], -# [ROSBagJobStatus.STARTING, ROSBagJobStatus.RUNNING]) -# self.assert_rosbag_jobs_in_project(throttling_rosbag_job.name) -# self.wait_till_jobs_are_running(self.deployment_with_throttling.deploymentId, -# [self.throttling_rosbag_job.guid], sleep_interval_in_sec=5) -# self.logger.info('sleeping for 8 seconds') -# time.sleep(8) -# self.config.client.stop_rosbag_jobs(self.deployment_with_throttling.deploymentId, -# guids=[self.throttling_rosbag_job.guid]) -# self.assert_rosbag_jobs_present(self.deployment_with_throttling.deploymentId, -# [self.throttling_rosbag_job.name], -# [ROSBagJobStatus.STOPPING, ROSBagJobStatus.STOPPED]) -# uploaded_blobs = self.wait_till_blobs_are_uploaded(sleep_interval_in_sec=5, -# job_ids=[self.throttling_rosbag_job.guid]) -# # self.logger.info('validating the uploaded rosbag blobs for the stopped jobs') -# """ -# TODO: -# Observation: This following assertion succeeds on a newly onboarded device but fails on reusing the same device. -# Cause of failure: The bag files fetched on basis of device id > bag files created during this deployment. -# Inference: bag files are not getting deleted after each test. -# Hence, Commenting this assertion for now. -# """ -# # self.assert_rosbag_blobs_of_device(uploaded_blobs) -# self.assertEqual(len(uploaded_blobs), 1) -# uploaded_blob = uploaded_blobs[0] -# relevant_topics = ['/topic1', '/topic2', '/topic3', '/topic4'] -# record_duration = uploaded_blob.info.duration -# topics = list(filter(lambda topic: topic.name in relevant_topics, uploaded_blob.info.topics)) -# topic1_metadata = next(filter(lambda topic: topic.name == '/topic1', topics), None) -# topic2_metadata = next(filter(lambda topic: topic.name == '/topic2', topics), None) -# topic3_metadata = next(filter(lambda topic: topic.name == '/topic3', topics), None) -# -# # asserting that the message count numbers recorded on topic1 and topic2 are close -# expected_msg_count_t1_t2 = record_duration * topic2_throttled_freq -# self.assertGreater(topic1_metadata.message_count, expected_msg_count_t1_t2 - difference_margin) -# self.assertLess(topic1_metadata.message_count, expected_msg_count_t1_t2 + difference_margin) -# self.assertGreater(topic2_metadata.message_count, expected_msg_count_t1_t2 - difference_margin) -# self.assertLess(topic2_metadata.message_count, expected_msg_count_t1_t2 + difference_margin) -# self.logger.info("Expected msg count: %s, " -# "Actual msg count on '/topic1': %s, " -# "Actual msg count on '/topic2': %s, " -# "Allowed difference margin: %s", -# expected_msg_count_t1_t2, topic1_metadata.message_count, topic2_metadata.message_count, -# difference_margin) -# -# self.assertGreater(topic3_metadata.message_count, record_duration * topic3_throttled_freq - 5) -# self.assertLess(topic3_metadata.message_count, record_duration * topic3_throttled_freq + 5) -# self.deployment_with_throttling.deprovision() -# -# def test_10_rosbag_job_latching(self): -# self.logger.info('deploying latching package') -# device_rosbag_job = ROSBagJob('device-init-job', ROSBagOptions(all_topics=True), -# upload_options=UploadOptions(upload_type=ROSBagUploadTypes.ON_STOP)) -# provision_config = self.package[4].get_provision_configuration() -# ignored_device_configs = ['network_interface'] -# provision_config.add_device('latching-component', self.device, ignore_device_config=ignored_device_configs) -# provision_config.add_rosbag_job('latching-component', device_rosbag_job) -# deployment = self.deploy_package(self.package[4], provision_config, -# ignored_device_configs=ignored_device_configs) -# deployment.poll_deployment_till_ready(retry_count=100, sleep_interval=5) -# self.__class__.deployment_with_latching = self.config.client.get_deployment(deployment.deploymentId) -# -# component_instance_id = self.deployment_with_latching.get_component_instance_id('latching-component') -# latching_rosbag_job = ROSBagJob('rosbag-test-latching', -# deployment_id=self.deployment_with_latching.deploymentId, -# component_instance_id=component_instance_id, -# rosbag_options=ROSBagOptions(all_topics=True, max_splits=5, max_split_size=20), -# upload_options=UploadOptions(upload_type=ROSBagUploadTypes.ON_STOP), -# override_options=OverrideOptions( -# topic_override_info=[ -# TopicOverrideInfo('/map', latched=True), -# ], -# )) -# self.__class__.latching_rosbag_job = self.config.client.create_rosbag_job(latching_rosbag_job) -# self.assert_rosbag_jobs_present(self.deployment_with_latching.deploymentId, -# [latching_rosbag_job.name], -# [ROSBagJobStatus.STARTING, ROSBagJobStatus.RUNNING]) -# self.assert_rosbag_jobs_in_project(latching_rosbag_job.name) -# self.wait_till_jobs_are_running(self.deployment_with_latching.deploymentId, -# [self.latching_rosbag_job.guid], sleep_interval_in_sec=5) -# self.logger.info('sleeping for 60 seconds') -# time.sleep(60) -# self.config.client.stop_rosbag_jobs(self.deployment_with_latching.deploymentId, -# guids=[self.latching_rosbag_job.guid]) -# self.assert_rosbag_jobs_present(self.deployment_with_latching.deploymentId, -# [self.latching_rosbag_job.name], -# [ROSBagJobStatus.STOPPING, ROSBagJobStatus.STOPPED]) -# -# uploaded_blobs = self.wait_till_blobs_are_uploaded(sleep_interval_in_sec=5, -# job_ids=[self.latching_rosbag_job.guid]) -# # self.logger.info('validating the uploaded rosbag blobs for the stopped jobs') -# """ -# TODO: -# Observation: This following assertion succeeds on a newly onboarded device but fails on reusing the same device. -# Cause of failure: The bag files fetched on basis of device id > bag files created during this deployment. -# Inference: bag files are not getting deleted after each test. -# Hence, Commenting this assertion for now. -# """ -# # self.assert_rosbag_blobs_of_device(uploaded_blobs) -# """ -# TODO: -# There's an anomaly in the following assertion. -# len(uploaded_blobs) outputs 3 while debugging, while in the UI there were 6 splits. -# Hence, Commenting the assertion for now (even though it passes because 3>1) unless the reason behind this -# behaviour is understood. -# """ -# # self.assertGreater(len(uploaded_blobs), 1) -# -# topic_absent_in_split = False -# for blob in uploaded_blobs: -# topics = blob.info.topics -# x = next((topic for topic in topics if topic.name == '/map'), None) -# if x is None: -# topic_absent_in_split = True -# break -# -# self.assertFalse(topic_absent_in_split) -# self.deployment_with_latching.deprovision() -# -# def assert_rosbag_jobs_present(self, deployment_id, job_names, statuses=None): -# self.logger.info('checking jobs ') -# jobs_list = self.config.client.list_rosbag_jobs(deployment_id) -# jobs = [x for x in jobs_list if x.name in job_names] -# self.assertNotEqual(len(jobs), 0, 'no jobs were started') -# if statuses: -# for job in jobs: -# self.assertTrue(job.status in statuses) -# -# def assert_rosbag_jobs_in_project(self, job_name): -# self.logger.info('checking jobs in project ') -# jobs_list = self.config.client.list_rosbag_jobs_in_project([self.device.deviceId]) -# self.assertEqual((job_name in [job.name for job in jobs_list]), True) -# -# def assert_rosbag_blobs(self, blobs): -# for blob in blobs: -# self.config.client.download_rosbag_blob(blob.guid, filename=self.bag_filename) -# self.assert_bag_file_exists() -# self.config.client.delete_rosbag_blob(blob.guid) -# self.assert_rosbag_blob_deleted(blob.guid) -# -# def assert_rosbag_blobs_of_device(self, blobs): -# self.logger.info('checking if the blobs fetched based on device id are present in the uploaded blobs') -# blobs_based_on_device_id = self.config.client.list_rosbag_blobs(device_ids=[self.device.deviceId]) -# guids_based_on_device_id = [blob.guid for blob in blobs_based_on_device_id] -# all_guids = [blob.guid for blob in blobs] -# self.assertEqual(all(guid in all_guids for guid in guids_based_on_device_id), True) -# -# def assert_bag_file_exists(self): -# self.assertTrue(os.path.exists(self.bag_filename)) -# -# def assert_rosbag_blob_deleted(self, blob_guid): -# blobs = self.config.client.list_rosbag_blobs(guids=[blob_guid]) -# self.assertEqual(len(blobs), 0) -# -# def create_rosbag_job(self, component_name, is_device=False): -# self.logger.info('creating rosbag job for {} component'.format(component_name)) -# rosbag_job_name = generate_random_value() -# upload_options = None -# component_instance_id = self.deployment.get_component_instance_id(component_name) -# if is_device: -# upload_options = UploadOptions(upload_type=ROSBagUploadTypes.ON_STOP) -# rosbag_job = ROSBagJob(rosbag_job_name, ROSBagOptions(all_topics=True), -# deployment_id=self.deployment.deploymentId, -# component_instance_id=component_instance_id, -# upload_options=upload_options) -# return self.config.client.create_rosbag_job(rosbag_job) -# -# def wait_till_jobs_are_running(self, deployment_id, guids=None, retry_limit=50, sleep_interval_in_sec=1): -# self.logger.info('waiting for rosbag jobs to start running') -# retry_count = 0 -# while retry_count < retry_limit: -# jobs = self.config.client.list_rosbag_jobs(deployment_id, -# guids=guids) -# running_jobs = [job for job in jobs if job.status == ROSBagJobStatus.RUNNING] -# if len(jobs) == len(running_jobs): -# self.logger.info('rosbag jobs are running') -# return -# sleep(sleep_interval_in_sec) -# retry_count += 1 -# -# raise Exception('rosbag jobs are not running, waiting timed out') -# -# def wait_till_blobs_are_uploaded( -# self, -# job_ids=None, -# retry_limit=50, -# sleep_interval_in_sec=1, -# list_blobs_sleep_interval_in_sec=5 -# ): -# if not job_ids: -# job_ids = [self.cloud_rosbag_job.guid, self.device_rosbag_job.guid] -# self.logger.info('waiting for rosbag blobs to finish uploading') -# -# blobs = [] -# retry_count = 0 -# job_ids_copy = job_ids.copy() -# while retry_count < retry_limit: -# blobs = self.config.client.list_rosbag_blobs(job_ids=job_ids) -# if not blobs: -# sleep(list_blobs_sleep_interval_in_sec) -# continue -# -# for blob in blobs: -# if blob.job.guid in job_ids_copy: -# job_ids_copy.remove(blob.job.guid) -# -# if len(job_ids_copy) == 0: -# break -# -# if not job_ids_copy: -# break -# -# sleep(list_blobs_sleep_interval_in_sec) -# -# if job_ids_copy: -# raise Exception( -# 'not even a single rosbag blob has been uploaded for job ids {}, waiting timed out'.format(job_ids_copy) -# ) -# -# for blob in blobs: -# blob.poll_till_ready(retry_count=retry_limit, sleep_interval=sleep_interval_in_sec) -# -# self.logger.info('rosbag blobs are uploaded') -# -# return blobs -# -# def get_job_by_job_name(self, deployment_id, job_name): -# jobs = self.config.client.list_rosbag_jobs(deployment_id) -# for job in jobs: -# if job.name == job_name: -# return job -# -# return None diff --git a/sdk_test/package/routed_networks_tests.py b/sdk_test/package/routed_networks_tests.py deleted file mode 100644 index 8db1559f..00000000 --- a/sdk_test/package/routed_networks_tests.py +++ /dev/null @@ -1,158 +0,0 @@ -from __future__ import absolute_import -from rapyuta_io import DeploymentStatusConstants, DeviceArch -from rapyuta_io.clients.deployment import DeploymentPhaseConstants -from rapyuta_io.clients.package import Runtime, ROSDistro, RestartPolicy -from rapyuta_io.utils.utils import generate_random_value -from sdk_test.config import Configuration -from sdk_test.device.device_test import DeviceTest -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, add_package, delete_package, get_package -from rapyuta_io.clients.routed_network import Parameters -from rapyuta_io.clients.common_models import Limits - -class RoutedNetworkTest(PackageTest, DeviceTest): - - LISTENER_MANIFEST = 'listener.json' - LISTENER_PACKAGE = 'test-routed-network-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.LISTENER_MANIFEST, cls.LISTENER_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.LISTENER_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.name = 'net-' + generate_random_value() - self.routed_network = None - self.ros_distro = ROSDistro.MELODIC - self.shared = True - self.routed_network = None - self.docker_device = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose')[0] - self.supervisor_device = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Preinstalled')[0] - self.parameters = Parameters(Limits(cpu=0.5, memory=1024)) - - def create_routed_network(self, runtime, parameters): - if runtime == Runtime.CLOUD: - self.routed_network = self.config.client.create_cloud_routed_network( - self.name, self.ros_distro, self.shared) - else: - device = self.config.client.get_device(parameters['device_id']) - self.routed_network = self.config.client.create_device_routed_network( - self.name, self.ros_distro, self.shared, device, - parameters['NETWORK_INTERFACE'], parameters['restart_policy']) - self.routed_network.poll_routed_network_till_ready() - - def assert_deployment_status(self, routed_network): - self.logger.info('Checking network status') - status = routed_network.get_status() - self.assertEqual(status.status, DeploymentStatusConstants.RUNNING.value) - self.assertEqual(status.phase, DeploymentPhaseConstants.SUCCEEDED.value) - self.logger.info('network %s(%s) started successfully' % (routed_network.name, - routed_network.guid)) - - def assert_routed_network_present_in_list(self, guid): - all_routed_network = self.config.client.get_all_routed_networks() - routed_network = list(filter(lambda network: network.guid == guid, all_routed_network))[0] - self.assertEqual(routed_network.name, self.routed_network.name) - - def assert_routed_network_stopped(self, guid): - all_routed_network = self.config.client.get_all_routed_networks() - routed_network = list(filter(lambda network: network.guid == guid, all_routed_network))[0] - self.assertEqual(routed_network.internalDeploymentStatus.phase, - DeploymentPhaseConstants.DEPLOYMENT_STOPPED.value) - - def validate_refresh(self, guid): - partial_net = [n for n in self.config.client.get_all_routed_networks() if n.guid == guid][0] - self.assertTrue(partial_net.is_partial) - with self.assertRaises(AttributeError): - partial_net.internalDeploymentStatus.status - partial_net.refresh() - self.assertFalse(partial_net.is_partial) - self.assertTrue(partial_net.internalDeploymentStatus.status) - - def assert_routed_network_parameters(self, parameters, network): - self.assertEqual(parameters['device_id'], self.docker_device.deviceId) - self.assertEqual(parameters['NETWORK_INTERFACE'], network) - self.assertEqual(parameters['restart_policy'], RestartPolicy.OnFailure) - - def test_add_device_routed_network(self): - self.logger.info('Started creating device routed network') - self.create_routed_network(Runtime.DEVICE, {'device_id': self.docker_device.deviceId, - 'NETWORK_INTERFACE': 'lo', - 'restart_policy': RestartPolicy.OnFailure}) - - self.logger.info('getting device routed network') - self.routed_network = self.config.client.get_routed_network(self.routed_network.guid) - self.assertEqual(self.routed_network.runtime, 'device') - self.assertEqual(self.routed_network.guid, self.routed_network.guid) - self.assert_routed_network_parameters(self.routed_network.parameters, 'lo') - self.assert_deployment_status(self.routed_network) - self.assert_routed_network_present_in_list(self.routed_network.guid) - - self.logger.info('Started creating package listener component') - app_package = get_package(self.LISTENER_PACKAGE) - prov_config = app_package.get_provision_configuration() - ignore_device_config = ['network_interface'] - prov_config.add_device('default', self.supervisor_device, ignore_device_config=ignore_device_config) - prov_config.add_routed_network(self.routed_network, 'docker0') - guid = self.routed_network.guid - self.assertEqual(prov_config.context['routedNetworks'], [{"guid": guid, "bindParameters": - {"NETWORK_INTERFACE": 'docker0'}}]) - self.logger.info('creating deployment') - deployment = self.deploy_package(app_package, prov_config, device=self.supervisor_device, - ignored_device_configs=ignore_device_config) - deployment.poll_deployment_till_ready() - deployment.deprovision() - - self.logger.info('Delete routed network with guid : %s' % guid) - self.routed_network.delete() - self.assert_routed_network_stopped(guid) - - def test_create_cloud_routed_network(self): - self.logger.info('Started creating cloud routed network') - self.create_routed_network(Runtime.CLOUD, {}) - routed_network = self.config.client.get_routed_network(self.routed_network.guid) - self.assertEqual(self.routed_network.runtime, 'cloud') - self.assertEqual(routed_network.guid, self.routed_network.guid) - self.assertEqual(routed_network.parameters, self.parameters.to_dict()) - self.assert_deployment_status(routed_network) - self.assert_routed_network_present_in_list(routed_network.guid) - guid = self.routed_network.guid - self.logger.info('Delete routed network with guid : %s' % guid) - routed_network.delete() - self.assert_routed_network_stopped(guid) - - def test_create_cloud_routed_network_with_parameters(self): - self.logger.info('Started creating cloud routed network with parameters') - self.create_routed_network(Runtime.CLOUD, Parameters(Limits(cpu=0.5, memory=1024))) - routed_network = self.config.client.get_routed_network(self.routed_network.guid) - self.assertEqual(self.routed_network.runtime, 'cloud') - self.assertEqual(routed_network.guid, self.routed_network.guid) - self.assertEqual(routed_network.parameters, self.parameters.to_dict()) - self.assert_deployment_status(routed_network) - self.assert_routed_network_present_in_list(routed_network.guid) - guid = self.routed_network.guid - self.validate_refresh(guid) - self.logger.info('Delete routed network with guid : %s' % guid) - self.config.client.delete_routed_network(guid) - self.assert_routed_network_stopped(guid) - - def test_create_device_routed_network(self): - self.logger.info('Started creating device routed network') - self.create_routed_network(Runtime.DEVICE, {'device_id': self.docker_device.deviceId, - 'NETWORK_INTERFACE': 'docker0', - 'restart_policy': RestartPolicy.OnFailure}) - self.routed_network = self.config.client.get_routed_network(self.routed_network.guid) - self.assertEqual(self.routed_network.runtime, 'device') - self.assertEqual(self.routed_network.guid, self.routed_network.guid) - self.assert_routed_network_parameters(self.routed_network.parameters, 'docker0') - self.assert_deployment_status(self.routed_network) - self.assert_routed_network_present_in_list(self.routed_network.guid) - guid = self.routed_network.guid - self.logger.info('Delete routed network with guid : %s' % guid) - self.routed_network.delete() - self.assert_routed_network_stopped(guid) diff --git a/sdk_test/package/static_route_test.py b/sdk_test/package/static_route_test.py deleted file mode 100644 index c6d5b794..00000000 --- a/sdk_test/package/static_route_test.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import absolute_import -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, add_package, delete_package, get_package - - -class StaticRouteTest(PackageTest): - STATIC_ROUTE_1 = 'nginx' - STATIC_ROUTE_2 = 'nginx2' - ENDPOINT_1 = 'test' - ENDPOINT_2 = 'test2' - - NGINX_MULTI_COMPONENT_MANIFEST = 'nginx-multi-component.json' - NGINX_MULTI_COMPONENT_PACKAGE = 'test-static-route-nginx-multi-component-pkg' - - NGINX_SINGLE_COMPONENT_MANIFEST = 'nginx-single-component.json' - NGINX_SINGLE_COMPONENT_PACKAGE = 'test-static-route-nginx-single-component-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.NGINX_MULTI_COMPONENT_MANIFEST, - cls.NGINX_MULTI_COMPONENT_PACKAGE) - add_package(cls.NGINX_SINGLE_COMPONENT_MANIFEST, - cls.NGINX_SINGLE_COMPONENT_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.NGINX_MULTI_COMPONENT_PACKAGE) - delete_package(cls.NGINX_SINGLE_COMPONENT_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.volume_instance = None - self.deployment = None - self.static_routes = [] - self.nginx_multi_pkg = get_package(self.NGINX_MULTI_COMPONENT_PACKAGE) - self.nginx_single_pkg = get_package(self.NGINX_SINGLE_COMPONENT_PACKAGE) - - def tearDown(self): - self.deprovision_all_deployments([self.deployment]) - self._remove_static_routes() - - def _create_static_routes(self): - sr1 = self.config.client.create_static_route(self.STATIC_ROUTE_1) - sr2 = self.config.client.create_static_route(self.STATIC_ROUTE_2) - # Doing a get again to get the urlString field - self.static_routes.append(self.config.client.get_static_route(sr1.guid)) - self.static_routes.append(self.config.client.get_static_route(sr2.guid)) - - def _remove_static_routes(self): - self.logger.info("Removing all static routes") - for route in self.static_routes: - result = route.delete() - if not result: - self.logger.warn("Error while cleaning up static routes {}".format(route)) - - def test_deployment_with_multiple_static_routes(self): - self.logger.info("Started multi component static route deployment") - provision_configuration = self.nginx_multi_pkg.get_provision_configuration() - self._create_static_routes() - provision_configuration.add_static_route(self.STATIC_ROUTE_1, self.ENDPOINT_1, self.static_routes[0]) - provision_configuration.add_static_route(self.STATIC_ROUTE_2, self.ENDPOINT_2, self.static_routes[1]) - self.deployment = self.deploy_package(self.nginx_multi_pkg, provision_configuration) - self.deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.deployment) - self.assert_static_route('https://' + str(self.static_routes[0].urlString)) - self.assert_static_route('https://' + str(self.static_routes[1].urlString)) - - def test_deployment_with_single_static_routes(self): - self.logger.info("Started single component static route deployment") - provision_configuration = self.nginx_single_pkg.get_provision_configuration() - self._create_static_routes() - provision_configuration.add_static_route(self.STATIC_ROUTE_1, self.ENDPOINT_1, self.static_routes[0]) - self.deployment = self.deploy_package(self.nginx_single_pkg, provision_configuration) - self.deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.deployment) - self.assert_static_route('https://' + str(self.static_routes[0].urlString)) - - def test_get_static_route_by_name_404_case(self): - self._create_static_routes() - route = self.config.client.get_static_route_by_name('temp') - self.assertIsNone(route) - - # TODO(senapati): This test is commented because filter static route - # API is deprecated in v1 api server. - # def test_get_static_route_by_name_success(self): - # self._create_static_routes() - # route = self.config.client.get_static_route_by_name(self.STATIC_ROUTE_1) - # route_name = route['urlPrefix'].split('-')[0] - # self.assertEqual(route_name, self.STATIC_ROUTE_1) - - def test_delete_static_route_success(self): - route1 = self.config.client.create_static_route(self.STATIC_ROUTE_1) - route_guid = route1.guid - self.config.client.delete_static_route(route_guid) - route = self.config.client.get_static_route_by_name(self.STATIC_ROUTE_1) - self.assertIsNone(route) - - diff --git a/sdk_test/package/transformer_with_docker_device_test.py b/sdk_test/package/transformer_with_docker_device_test.py deleted file mode 100644 index 9f71ea4b..00000000 --- a/sdk_test/package/transformer_with_docker_device_test.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import absolute_import -from rapyuta_io import DeviceArch -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, get_package, delete_package, add_package - - -class TestTransformerWithDockerDevice(PackageTest): - - TALKER_DOCKER_MANIFEST = 'talker-docker.json' - LISTENER_DOCKER_MANIFEST = 'listener-docker.json' - CLOUD_TRANSFORM_MANIFEST = 'cloud-transform.json' - - TALKER_DOCKER_PACKAGE = 'test-transformer-talker-docker-pkg' - LISTENER_DOCKER_PACKAGE = 'test-transformer-listener-docker-pkg' - CLOUD_TRANSFORM_PACKAGE = 'test-transformer-cloud-transform-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.TALKER_DOCKER_MANIFEST, cls.TALKER_DOCKER_PACKAGE) - add_package(cls.LISTENER_DOCKER_MANIFEST, cls.LISTENER_DOCKER_PACKAGE) - add_package(cls.CLOUD_TRANSFORM_MANIFEST, cls.CLOUD_TRANSFORM_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.TALKER_DOCKER_PACKAGE) - delete_package(cls.LISTENER_DOCKER_PACKAGE) - delete_package(cls.CLOUD_TRANSFORM_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.device = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose')[0] - self.talker_deployment = None - self.cloud_transform_deployment = None - self.listener_deployment = None - self.routed_network = self.create_cloud_routed_network('transformer_with_docker_device') - - def tearDown(self): - self.deprovision_all_deployments([self.talker_deployment, self.cloud_transform_deployment, - self.listener_deployment]) - self.routed_network.delete() - - def deploy_talker_package(self, device): - self.logger.info('Deploying talker package') - package = get_package(self.TALKER_DOCKER_PACKAGE) - provision_config = package.get_provision_configuration() - ignored_device_configs = ['ros_workspace', 'ros_distro'] - provision_config.add_device("default", device, ignored_device_configs) - provision_config.add_routed_network(self.routed_network) - self.talker_deployment = self.deploy_package(package, provision_config, device, - ignored_device_configs) - - def deploy_cloud_transform_package(self): - package = get_package(self.CLOUD_TRANSFORM_PACKAGE) - provision_config = package.get_provision_configuration() - provision_config.add_dependent_deployment(self.talker_deployment) - provision_config.add_routed_network(self.routed_network) - self.logger.info('Deploying cloud transform package') - self.cloud_transform_deployment = self.deploy_package(package, provision_config) - self.assert_dependent_deployment(self.cloud_transform_deployment, [self.talker_deployment]) - - def deploy_listener_package(self, device): - package = get_package(self.LISTENER_DOCKER_PACKAGE) - provision_config = package.get_provision_configuration() - ignored_device_configs = ['ros_workspace', 'ros_distro'] - provision_config.add_device("default", device, ignored_device_configs) - provision_config.add_routed_networks([self.routed_network]) - provision_config.add_dependent_deployment(self.cloud_transform_deployment) - self.listener_deployment = self.deploy_package(package, provision_config, device, - ignored_device_configs) - self.assert_dependent_deployment(self.listener_deployment, [self.cloud_transform_deployment]) - - # TODO(senapati): This test is commented as its using build based package - # which is not supported anymore - # def test_deploy_transformer_with_docker_device(self): - # self.logger.info('Started transformer with docker device test case') - # self.deploy_talker_package(self.device) - # self.talker_deployment.poll_deployment_till_ready() - # self.assert_deployment_status(self.talker_deployment) - # self.deploy_cloud_transform_package() - # self.cloud_transform_deployment.poll_deployment_till_ready() - # self.assert_deployment_status(self.cloud_transform_deployment) - # self.deploy_listener_package(self.device) - # self.listener_deployment.poll_deployment_till_ready() - # self.assert_deployment_status(self.listener_deployment) diff --git a/sdk_test/package/volume_test.py b/sdk_test/package/volume_test.py deleted file mode 100644 index 5cf3b614..00000000 --- a/sdk_test/package/volume_test.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import absolute_import - -from rapyuta_io import DeviceArch -from rapyuta_io.clients.package import ExecutableMount, Runtime, RestartPolicy -from sdk_test.config import Configuration -from sdk_test.package.package_test import PackageTest -from sdk_test.util import get_logger, delete_package, add_package, get_package - -MOUNT_PATH = "/data" - - -class TestVolume(PackageTest): - - PV_READER_MANIFEST = 'pv-reader.json' - PV_READER_PACKAGE = 'test-volume-pv-reader-pkg' - DEVICE_VOLUME_MANIFEST = 'device-volume.json' - DEVICE_VOLUME_PACKAGE = 'test-device-volume-pkg' - - @classmethod - def setUpClass(cls): - add_package(cls.PV_READER_MANIFEST, cls.PV_READER_PACKAGE) - add_package(cls.DEVICE_VOLUME_MANIFEST, cls.DEVICE_VOLUME_PACKAGE) - - @classmethod - def tearDownClass(cls): - delete_package(cls.PV_READER_PACKAGE) - delete_package(cls.DEVICE_VOLUME_PACKAGE) - - def setUp(self): - self.config = Configuration() - self.logger = get_logger() - self.volume_instance = None - self.deployment = None - self.package = get_package(self.PV_READER_PACKAGE) - self.device_package = get_package(self.DEVICE_VOLUME_PACKAGE) - self.docker_device = self.config.get_devices(arch=DeviceArch.AMD64, runtime='Dockercompose')[0] - self.docker_device.refresh() - - def tearDown(self): - self.deprovision_all_deployments([self.deployment, self.volume_instance]) - - def test_01_persistent_volume_as_dependent_deployment(self): - self.logger.info("Started persistent volume as dependent deployment") - self.volume_instance = self.get_persistent_volume_instance(instance_name='volume_instance') - self.volume_instance.poll_deployment_till_ready() - provision_configuration = self.package.get_provision_configuration() - self.logger.info('Adding persistent volume as dependent deployment') - provision_configuration.add_dependent_deployment(self.volume_instance) - provision_configuration.mount_volume("default", volume=self.volume_instance, mount_path=MOUNT_PATH) - self.deployment = self.deploy_package(self.package, provision_configuration) - self.deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.deployment) - self.validate_refresh() - - def test_02_persistent_volume_as_dependent_deployment_with_executable_mounts(self): - self.logger.info("Started persistent volume as dependent deployment") - self.volume_instance = self.get_persistent_volume_instance(instance_name='test_executable_mounts') - self.volume_instance.poll_deployment_till_ready() - provision_configuration = self.package.get_provision_configuration() - self.logger.info('Adding persistent volume as dependent deployment') - provision_configuration.add_dependent_deployment(self.volume_instance) - executable_mounts = [ - ExecutableMount(exec_name='CompReaderExec', mount_path='/test_path', sub_path='test_subpath')] - provision_configuration.mount_volume("default", volume=self.volume_instance, mount_path=None, - executable_mounts=executable_mounts) - self.deployment = self.deploy_package(self.package, provision_configuration) - self.deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.deployment) - - def test_03_device_volume_with_executable_mounts(self): - self.logger.info("Started device volume") - self.volume_instance = None - provision_configuration = self.device_package.get_provision_configuration() - self.logger.info('Adding device to component') - provision_configuration.add_device('default', self.docker_device) - exec_mounts = [ExecutableMount('nginx', '/tmp/', '/home/rapyuta/')] - self.logger.info('Adding mount paths to device volume') - provision_configuration.mount_volume('default', device=self.docker_device, mount_path=None, executable_mounts=exec_mounts) - self.deployment = self.deploy_package(self.device_package, provision_configuration) - self.deployment.poll_deployment_till_ready() - self.assert_deployment_status(self.deployment) - - def validate_refresh(self): - partial_volume = [v for v in self.get_persistent_volume().get_all_volume_instances() - if v.deploymentId == self.volume_instance.deploymentId][0] - self.assertTrue(partial_volume.is_partial) - partial_volume.refresh() - self.assertFalse(partial_volume.is_partial) - self.assertTrue(partial_volume.parameters) diff --git a/sdk_test/run_rio_sdk_test.py b/sdk_test/run_rio_sdk_test.py index 6e1e25e1..9160a5e8 100644 --- a/sdk_test/run_rio_sdk_test.py +++ b/sdk_test/run_rio_sdk_test.py @@ -36,16 +36,12 @@ def get_concurrent_test_suites(self): def setUpSuite(self): self.logger.info('Creating project') self.config.create_project() - self.logger.info('Creating secrets') - self.config.create_secrets() self.onboard_devices() self.wait_for_devices() def tearDownSuite(self): self.remove_devices() self.config.set_devices(None) - self.logger.info('Deleting secrets') - self.config.delete_secrets() self.logger.info('Deleting project') self.config.delete_project() diff --git a/sdk_test/util.py b/sdk_test/util.py index bf9e5542..cd935528 100644 --- a/sdk_test/util.py +++ b/sdk_test/util.py @@ -1,17 +1,9 @@ from __future__ import absolute_import -import json import logging -import os from time import sleep -from rapyuta_io import Build, BuildOptions, CatkinOption, SimulationOptions, ROSDistro from rapyuta_io.clients.model import Command -from rapyuta_io.clients.native_network import NativeNetwork -from rapyuta_io.clients.package import Runtime -from rapyuta_io.utils.rest_client import HttpMethod -from rapyuta_io.utils.utils import get_api_response_data -from sdk_test.config import Configuration _JSON_PATH = '' _PACKAGE_MAP = dict() @@ -20,333 +12,6 @@ _NATIVE_NETWORK_MAP = dict() -def get_manifest_file(manifest_name, manifest_type): - """ - get_manifest_file generates the filepath relative to the current executable for the Manifest (JSON files). - - manifest_name: Name of the manifest file (Package/Build/Deployment) with the extension - manifest_type: Type of the manifest. Possible Values: Package, Build - """ - global _JSON_PATH - if _JSON_PATH == '': - dir_path = os.path.dirname(os.path.realpath(__file__)) - _JSON_PATH = os.path.join(dir_path, 'jsons') - - if manifest_type == 'Package': - path = os.path.join(_JSON_PATH, 'packages') - elif manifest_type == 'Build': - path = os.path.join(_JSON_PATH, 'builds') - elif manifest_type == 'Deployment': - path = os.path.join(_JSON_PATH, 'deployment') - else: - raise Exception('Invalid manifest type') - - return '{}/{}'.format(path, manifest_name) - - -def get_build(build_name): - """ - get_build is the utility function to fetch the Build using its *Manifest* name. The latest Build object is fetched - using the API. - - build_name: Name of the Build Manifest (JSON file) without the extension - """ - global _BUILD_MAP - config = Configuration() - build_id = _BUILD_MAP[build_name]['guid'] - return config.client.get_build(build_id) - - -def _get_build_from_manifest(manifest): - """ - _get_build_from_manifest translates the JSON manifest for Build into the Build Object that can be used to interact - with the API. - - It supports partial manifests, i.e., the value of Secret field only needs to have the type of the Secret instead of - the actual Secret GUID. It will populate the fields based on the actual Secret GUID automatically. For more - information check the `create_secrets` and `get_secret` method on the Configuration. - Possible types of Secret: git, docker. - - manifest: Parsed JSON payload in the form of Dictionary with all the required fields for Build. Check the Golang - Model for reference. - """ - config = Configuration() - secret_guid = '' - simulation_options = None - build_options = None - - if manifest.get('secret') is not None: - secret = config.get_secret(manifest.get('secret')) - secret_guid = secret.guid - - if manifest.get('buildOptions') is not None: - catkin_options = [] - for opt in manifest['buildOptions']['catkinOptions']: - catkin_options.append(CatkinOption( - rosPkgs=opt.get('rosPkgs'), - cmakeArgs=opt.get('cmakeArgs'), - makeArgs=opt.get('makeArgs'), - blacklist=opt.get('blacklist'), - catkinMakeArgs=opt.get('catkinMakeArgs') - )) - build_options = BuildOptions(catkin_options) - - if manifest.get('simulationOptions') is not None: - value = manifest['simulationOptions']['simulation'] - simulation_options = SimulationOptions(value) - - return Build( - branch=manifest.get('branch', ''), - buildName=manifest['buildName'], - strategyType=manifest['strategyType'], - repository=manifest['repository'], - architecture=manifest['architecture'], - rosDistro=manifest.get('rosDistro', ''), - isRos=manifest.get('isRos', False), - contextDir=manifest.get('contextDir', ''), - dockerFilePath=manifest.get('dockerFilePath', ''), - secret=secret_guid, - simulationOptions=simulation_options, - buildOptions=build_options, - ) - - -def add_build(manifest_name, build_name=None, wait=True, modify_func=None): - """ - add_build is a utility function that creates the Build from the JSON manifest. - - manifest_name: Name of the Build Manifest (JSON file) with the extension - [optional] build_name: Name of the build - [optional] wait: Flag to enable waiting for the Build to complete - Default: True - [optional] modify_func: This utility provides a hook function that gets called after loading and parsing the - Manifest. It can be used to perform arbitrary runtime manipulations on the Payload. Only use it for advanced use - cases that are not directly supported by the utility directly. - """ - global _BUILD_MAP - config = Configuration() - logger = get_logger() - path = get_manifest_file(manifest_name, 'Build') - with open(path, 'r') as f: - build_payload = json.load(f) - if build_name is not None: - build_payload['buildName'] = build_name - - if modify_func is not None: - modify_func(build_payload) - - logger.info('Creating the build: %s', build_name) - build = config.client.create_build(_get_build_from_manifest(build_payload)) - _BUILD_MAP[build_name] = build - if wait: - logger.debug('Waiting for the build %s to complete', build_name) - build.poll_build_till_ready(sleep_interval=10) - return build - - -def delete_build(build_name): - """ - delete_build is a utility function that deletes the Build using the Manifest Name. This function is idempotent and - it can be safely called multiple times for the same Build. - - build_name: Name of the Build Manifest (JSON file) without the extension - """ - global _BUILD_MAP - if build_name in _BUILD_MAP: - _BUILD_MAP[build_name].delete() - del _BUILD_MAP[build_name] - - -def get_package(package_name): - """ - get_package is the utility function to fetch the Package using its *Manifest* name. The latest Package object is - fetched using the API. - - package_name: Name of the Package Manifest (JSON file) without the extension - """ - global _PACKAGE_MAP - config = Configuration() - package_id = _PACKAGE_MAP[package_name]['packageId'] - return config.client.get_package(package_id) - - -def add_package(manifest_name, package_name=None, wait=True, build_map=None, modify_func=None): - """ - add_package is a utility function to create new packages using there *Manifest* name. It loads the Manifest file and - creates the Package based on it. It supports partials, i.e., Package templates without Build information can also be - used. You can use the build_map option to provide Builds information at runtime. - - manifest_name: Name of the Package Manifest (JSON file) with the extension - [optional] package_name: Name of the Package - [optional] wait: Flag to enable waiting for all the Builds to complete for the Package. It is compatible with older - and newer package versions. - Default: True - [optional] build_map: Build Map can be used to inject Builds information (like BuildGUID) at runtime. It accepts a - dictionary that maps Components to Executable-BuildGUID Map. - Example: { - "comp1": { - "exec1": "build-guid" - } - } - [optional] modify_func: This utility provides a hook function that gets called after loading and parsing the - Manifest. It can be used to perform arbitrary runtime manipulations on the Manifest. Only use it for advanced use - cases that are not directly supported by the utility directly. - """ - global _PACKAGE_MAP - config = Configuration() - logger = get_logger() - - def wait_for_package(pkg_id): - logger.debug('Waiting for the Package (and its Builds) to finish...') - max_poll_count = 60 - poll_count = 0 - while poll_count < max_poll_count: - ready = True - url = '{}/serviceclass/status'.format(config.catalog_server) - params = {'package_uid': pkg_id, 'builds': 'true'} - response = config.client._catalog_client._execute(url, HttpMethod.GET, query_params=params) - new_package = get_api_response_data(response, parse_full=True) - package_info = new_package['packageInfo'] - if package_info['status'] != 'Complete': - ready = False - if 'buildInfo' in new_package: - for build in new_package['buildInfo']: - if build['status'] != 'Complete': - ready = False - if ready: - break - sleep(20) - poll_count += 1 - - path = get_manifest_file(manifest_name, 'Package') - with open(path, 'r') as f: - package_payload = json.load(f) - if package_name is not None: - package_payload['name'] = package_name - - if build_map is not None: - _apply_build_on_package(package_payload, build_map) - - if modify_func is not None: - modify_func(package_payload) - - logger.info('Creating the package: %s', package_name) - package = config.client.create_package(manifest=package_payload, retry_limit=2) - _PACKAGE_MAP[package_name] = package - if wait: - wait_for_package(package['packageId']) - return package - - -def _apply_build_on_package(manifest, build_map): - """ - _apply_build_on_package implements the logic of injecting the Build Map into the Package Manifest. - - manifest: Parsed JSON payload in the form of Dictionary of the Package. - build_map: Dictionary with the Build Information. Check docstring of `add_package` for more information. - """ - global _BUILD_MAP - if build_map is None: - return - - for component in manifest['plans'][0]['components']: - if component['name'] in build_map: - component_name = component['name'] - for executable in component['executables']: - exec_name = executable['name'] - if exec_name in build_map[component_name]: - build_name = build_map[component_name][exec_name][0] - build_manifest = build_map[component_name][exec_name][1] - if _BUILD_MAP.get(build_name) is None: - add_build(build_manifest, build_name) - build = get_build(build_name) - executable['buildGUID'] = build['guid'] - - -def delete_package(package_name, delete_builds=True): - """ - delete_package is a utility function that deletes the Packages using there *Manifest* name. It is idempotent and can - be safely called multiple times for the same package. - - package_name: Name of the Package Manifest (JSON file) without the extension - [optional] delete_builds: Flag to enable/disable the cleanup of Builds associated with the Package. - Default: True - """ - global _PACKAGE_MAP - config = Configuration() - logger = get_logger() - - if package_name not in _PACKAGE_MAP: - return - - package_data = _PACKAGE_MAP[package_name] - - url = '{}/serviceclass/status'.format(config.catalog_server) - params = {'package_uid': package_data['packageId'], 'builds': 'true'} - response = config.client._catalog_client._execute(url, HttpMethod.GET, query_params=params) - package = get_api_response_data(response, parse_full=True) - builds = [] - if 'buildInfo' in package: - for build in package['buildInfo']: - builds.append(build['guid']) - - url = '{}/{}?package_uid={}'.format(config.catalog_server, '/serviceclass/delete', package_data['packageId']) - logger.info('Deleting the package: %s', package_name) - config.client._catalog_client._execute(url, HttpMethod.DELETE, 2) - - if delete_builds: - for build in builds: - config.client.delete_build(build) - - -def get_routed_network(network_name): - """ - get_routed_network is the utility function to fetch the Routed Network by its name. The latest - RoutedNetwork object is fetched using the API. - - network_name: Name of the Routed Network - """ - global _ROUTED_NETWORK_MAP - config = Configuration() - network_id = _ROUTED_NETWORK_MAP[network_name]['guid'] - return config.client.get_routed_network(network_id) - - -def add_cloud_routed_network(network_name, ros_distro=ROSDistro.KINETIC, wait=True): - """ - add_cloud_routed_network is a utility function that provisions a Cloud Routed Network. - - network_name: Name of the Routed Network - [optional] ros_distro: ROS Distribution for the Routed Network - Default: Kinetic - [optional] wait: Flag to enable waiting for the Routed Network to succeed - Default: True - """ - global _ROUTED_NETWORK_MAP - config = Configuration() - logger = get_logger() - logger.info('Provisioning the cloud routed network: %s', network_name) - routed_network = config.client.create_cloud_routed_network(network_name, ros_distro, True) - _ROUTED_NETWORK_MAP[network_name] = routed_network - if wait: - logger.debug('Waiting for the routed network %s to succeed', network_name) - routed_network.poll_routed_network_till_ready(sleep_interval=10) - return routed_network - - -def delete_routed_network(network_name): - """ - delete_routed_network is a utility function that deletes the Routed Network by its name. This - function is idempotent and it can be safely called multiple times for the same Network. - - network_name: Name of the Routed Network - """ - global _ROUTED_NETWORK_MAP - if network_name in _ROUTED_NETWORK_MAP: - _ROUTED_NETWORK_MAP[network_name].delete() - del _ROUTED_NETWORK_MAP[network_name] - - def get_logger(): formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') @@ -369,54 +34,3 @@ def start_roscore(device, bg=True): def stop_roscore(device, bg=True): command = Command(cmd='pkill roscore', shell='/bin/bash', bg=bg) device.execute_command(command, retry_limit=10) - - -def add_cloud_native_network(network_name, ros_distro=ROSDistro.KINETIC, wait=True): - """ - add_cloud_native_network is a utility function that provisions a Cloud Native Network. - - network_name: Name of the Native Network - [optional] ros_distro: ROS Distribution for the Native Network - Default: Kinetic - [optional] wait: Flag to enable waiting for the Native Network to succeed - Default: True - """ - global _NATIVE_NETWORK_MAP - config = Configuration() - logger = get_logger() - logger.info('Provisioning the cloud native network: %s', network_name) - native_network_payload = NativeNetwork(network_name, Runtime.CLOUD, ros_distro) - native_network = config.client.create_native_network(native_network_payload) - _NATIVE_NETWORK_MAP[network_name] = native_network - if wait: - logger.debug('Waiting for the native network %s to succeed', network_name) - native_network.poll_native_network_till_ready(sleep_interval=10) - return native_network - - -def delete_native_network(network_name): - """ - delete_native_network is a utility function that deletes the Native Network by its name. This - function is idempotent and it can be safely called multiple times for the same Network. - - network_name: Name of the Native Network - """ - global _NATIVE_NETWORK_MAP - if network_name in _NATIVE_NETWORK_MAP: - config = Configuration() - network_id = _NATIVE_NETWORK_MAP[network_name].guid - config.client.delete_native_network(network_id) - del _NATIVE_NETWORK_MAP[network_name] - - -def get_native_network(network_name): - """ - get_routed_network is the utility function to fetch the Native Network by its name. The latest - NativeNetwork object is fetched using the API. - - network_name: Name of the Native Network - """ - global _NATIVE_NETWORK_MAP - config = Configuration() - network_id = _NATIVE_NETWORK_MAP[network_name].guid - return config.client.get_native_network(network_id) diff --git a/sdk_test/v2_client.py b/sdk_test/v2_client.py new file mode 100644 index 00000000..b754b1f7 --- /dev/null +++ b/sdk_test/v2_client.py @@ -0,0 +1,38 @@ +from rapyuta_io.utils import prepend_bearer_to_auth_token, RestClient +from rapyuta_io.utils.rest_client import HttpMethod +from rapyuta_io.utils.utils import get_api_response_data + + +class V2Client: + """ + V2 Client used for sdk integration tests only. Its added here to avoid importing v2 client in cli. + CLI have its own v2 client, it should not use sdk v2 client. + + :ivar auth_token: Auth Token + :vartype guid: str + :ivar v2_api_host: V2 Host URL + :vartype name: str + """ + + def __init__(self, auth_token, v2_api_host): + self.v2_api_host = v2_api_host + self._auth_token = prepend_bearer_to_auth_token(auth_token) + self._project = None + + def set_project(self, project): + self._project = project + + def create_project(self, request): + url = self.v2_api_host + '/v2/projects/' + organization_guid = request['metadata']['organizationGUID'] + headers = dict(Authorization=self._auth_token, organizationguid=organization_guid) + response = RestClient(url).method(HttpMethod.POST).headers(headers).execute(request) + data = get_api_response_data(response, parse_full=True) + return data.get('metadata')['guid'] + + def delete_project(self, guid): + url = self.v2_api_host + '/v2/projects/' + guid + '/' + headers = dict(Authorization=self._auth_token) + response = RestClient(url).method(HttpMethod.DELETE).headers(headers).execute() + data = get_api_response_data(response, parse_full=True) + return data diff --git a/setup.py b/setup.py index 89d423f2..efc9617d 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,16 @@ from setuptools import setup, find_packages -import rapyuta_io +import re +version = re.search( + '^__version__\s*=\s*"(.*)"', open("rapyuta_io/__init__.py").read(), re.M +).group(1) with open("README.md", encoding="utf-8") as f: long_desc = f.read() setup( name="rapyuta_io", - version=rapyuta_io.__version__, + version=version, description="Rapyuta.io Python SDK", long_description=long_desc, long_description_content_type="text/markdown", @@ -24,7 +27,7 @@ ], install_requires=[ "requests>=2.20.0", - "six>=1.13.0", + "six>=1.16.0", "urllib3>=1.23", "python-dateutil>=2.8.2", "pytz", diff --git a/tests/build_test.py b/tests/build_test.py deleted file mode 100644 index 15737a03..00000000 --- a/tests/build_test.py +++ /dev/null @@ -1,867 +0,0 @@ -from __future__ import absolute_import -import requests -import unittest -import json -from mock import patch, Mock, MagicMock, call -from rapyuta_io.clients.build import Build, BuildStatus, SimulationOptions, BuildOptions, CatkinOption, GithubWebhook -from rapyuta_io.clients.buildoperation import BuildOperation, BuildOperationInfo -from tests.utils.client import get_client, remove_auth_token, headers -from rapyuta_io.utils.error import InvalidParameterException, ResourceNotFoundError -from tests.utils.build_responses import BUILD_CREATE_SUCCESS, BUILD_GET_SUCCESS, BUILD_GET_SUCCESS_WITH_BUILD_REQUESTS,\ - BUILD_LIST_SUCCESS, BUILD_NOT_FOUND, TRIGGER_BUILD_RESPONSE, \ - ROLLBACK_BUILD_RESPONSE, BUILD_GUID_NOT_FOUND, BUILD_IN_PROGRESS_ERROR -from requests import Response -from six.moves import map -from rapyuta_io.utils.partials import PartialMixin - - -class BuildTest(unittest.TestCase): - - def test_create_build_invalid_build_type(self): - expected_err_msg = 'build must be non-empty and of type rapyuta_io.clients.build.Build' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build('invalid-build-type') - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_strategy_type(self): - expected_err_msg = 'StrategyType must be one of rapyuta_io.clients.package.StrategyType' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'invalid', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'melodic')) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_ros_distro(self): - expected_err_msg = 'rosDistro must be one of rapyuta_io.clients.package.ROSDistro' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'invalid', True)) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_architecture(self): - expected_err_msg = 'Architecture must be one of rapyuta_io.clients.device_manager.DeviceArch' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'invalid', 'melodic')) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_build_name_absent(self): - expected_err_msg = 'buildName must be a non-empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'melodic')) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_repository_absent(self): - expected_err_msg = 'repository must be a valid non-empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', None, 'amd64', 'melodic')) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_simulation_option(self): - expected_err_msg = 'simulation must be a boolean' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', None, 'amd64', 'melodic', simulationOptions=SimulationOptions(1))) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_catkin_options(self): - expected_err_msg = 'catkinOptions must be an instance of list' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', None, 'amd64', rosDistro='melodic', buildOptions=BuildOptions(catkinOptions={}))) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_only_docker_push_secret(self): - expected_err_msg = 'both dockerPushRepository and dockerPushSecret must be present' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/example', 'amd64', - isRos=False, dockerPushSecret='secret-guid')) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_create_build_invalid_only_docker_push_repository(self): - expected_err_msg = 'both dockerPushRepository and dockerPushSecret must be present' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/example', 'amd64', - isRos=False, dockerPushRepository='docker.io/example/example')) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_create_build_invalid_trigger_name(self): - expected_err_msg = 'triggerName must be a non-empty string' - client = get_client() - invalid_trigger_name = 1 - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/example', 'amd64', - isRos=False, dockerPushSecret='secret-guid', triggerName=invalid_trigger_name, - dockerPushRepository='docker.io/example/example')) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_tag_name(self): - expected_err_msg = 'tagName must be a non-empty string' - client = get_client() - invalid_tag_name = 1 - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/example', 'amd64', - isRos=False, dockerPushSecret='secret-guid', tagName=invalid_tag_name, - dockerPushRepository='docker.io/example/example')) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_invalid_only_tag_name_with_no_docker_push_secret(self): - expected_err_msg = 'cannot use tagName without dockerPushSecret' - client = get_client() - test_tag_name = 'test_tag_name' - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/example', 'amd64', - isRos=False, tagName=test_tag_name)) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_create_build_operation_info_with_invalid_trigger_name(self): - expected_err_msg = 'triggerName must be a non-empty string' - inavlid_trigger_name = 1 - with self.assertRaises(InvalidParameterException) as e: - BuildOperationInfo('build-guid', triggerName=inavlid_trigger_name, tagName='tag-name') - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_operation_info_with_invalid_tag_name(self): - expected_err_msg = 'tagName must be a non-empty string' - inavlid_tag_name = 1 - with self.assertRaises(InvalidParameterException) as e: - BuildOperationInfo('build-guid', triggerName='trigger_name', tagName=inavlid_tag_name) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_build_operation_info_with_invalid_webhook(self): - expected_err_msg = 'buildWebhooks must be a list of rapyuta_io.clients.build.GithubWebhook' - inavlid_webhook = 1 - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_build(Build('test_build', 'Source', 'https://github.com/example', 'amd64', - isRos=False, dockerPushSecret='secret-guid', - dockerPushRepository='docker.io/example/example', - buildWebhooks = inavlid_webhook)) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_create_build_noetic_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Source", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "architecture": "amd64", - "rosDistro": "noetic", - "isRos": True - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - mock_create_build = Mock() - mock_create_build.text = BUILD_CREATE_SUCCESS - mock_create_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_build] - client = get_client() - build = client.create_build( - Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'noetic', True), False) - mock_request.assert_called_once_with(headers=headers, - json=expected_payload, - method='POST', - url=expected_url, - params=None) - self.assertEqual(build.get('guid'), 'build-guid') - self.assertEqual(build.get('buildName'), 'test_build') - self.assertTrue(build.is_partial) - - @patch('requests.request') - def test_create_build_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Source", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "architecture": "amd64", - "rosDistro": "melodic", - "isRos": True - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - mock_create_build = Mock() - mock_create_build.text = BUILD_CREATE_SUCCESS - mock_create_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_build] - client = get_client() - build = client.create_build( - Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'melodic', True), False) - mock_request.assert_called_once_with(headers=headers, - json=expected_payload, - method='POST', - url=expected_url, - params=None) - self.assertEqual(build.get('guid'), 'build-guid') - self.assertEqual(build.get('buildName'), 'test_build') - self.assertTrue(build.is_partial) - - @patch('requests.request') - def test_create_build_with_push_pull_secrets_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Docker", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "architecture": "amd64", - "isRos": False, - "dockerPullSecrets": ["secret-guid"], - "dockerPushSecret": "secret-guid", - "dockerPushRepository": "docker.io/example/example" - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - mock_create_build = Mock() - mock_create_build.text = BUILD_CREATE_SUCCESS - mock_create_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_build] - client = get_client() - build = client.create_build( - Build('test_build', 'Docker', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', isRos=False, dockerPushSecret='secret-guid', - dockerPushRepository='docker.io/example/example', dockerPullSecret='secret-guid'), False) - mock_request.assert_called_once_with(headers=headers, - json=expected_payload, - method='POST', - url=expected_url, - params=None) - self.assertEqual(build.get('guid'), 'build-guid') - self.assertEqual(build.get('buildName'), 'test_build') - - @patch('requests.request') - def test_create_build_with_trigger_name_tag_name_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Docker", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "architecture": "amd64", - "isRos": False, - "dockerPullSecrets": ["secret-guid"], - "dockerPushSecret": "secret-guid", - "dockerPushRepository": "docker.io/example/example", - "triggerName": "test-trigger-name", - "tagName": "test-tag-name" - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - mock_create_build = Mock() - mock_create_build.text = BUILD_CREATE_SUCCESS - mock_create_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_build] - client = get_client() - build = client.create_build( - Build('test_build', 'Docker', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', isRos=False, dockerPushSecret='secret-guid', - dockerPushRepository='docker.io/example/example', dockerPullSecret='secret-guid', - triggerName='test-trigger-name', tagName='test-tag-name'), False) - mock_request.assert_called_once_with(headers=headers, - json=expected_payload, - method='POST', - url=expected_url, - params=None) - self.assertEqual(build.get('guid'), 'build-guid') - self.assertEqual(build.get('buildName'), 'test_build') - - @patch('requests.request') - def test_create_build_with_refresh_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Source", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "architecture": "amd64", - "rosDistro": "melodic", - "isRos": True - } - get_build_response = MagicMock(spec=Response) - get_build_response.text = BUILD_GET_SUCCESS - get_build_response.status_code = requests.codes.OK - mock_create_build = Mock() - mock_create_build.text = BUILD_CREATE_SUCCESS - mock_create_build.status_code = requests.codes.OK - mock_request.side_effect = [get_build_response, mock_create_build] - client = get_client() - build = client.create_build( - Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'melodic', True)) - self.assertEqual(mock_request.call_count, 2) - self.assertEqual(build.get('guid'), 'build-guid') - self.assertEqual(build.get('buildName'), 'test_build') - self.assertFalse(build.is_partial) - - @patch('requests.request') - def test_create_build_with_webhook_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Source", - "repository": "https://github.com/rapyuta-robotics/io_tutorials.git", - "architecture": "amd64", - "rosDistro": "melodic", - "isRos": True, - "buildWebhooks": [ - { - "webhookType": "githubWorkflow", - "accessToken": "fake_access_token", - "workflowName": "fake.yaml" - } - ] - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - expected_get_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/build-guid' - mock_create_build = Mock() - mock_create_build.text = BUILD_CREATE_SUCCESS - mock_create_build.status_code = requests.codes.OK - mock_get_build = Mock() - mock_get_build.text = BUILD_GET_SUCCESS_WITH_BUILD_REQUESTS - mock_get_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_build, mock_get_build] - webhooks = [GithubWebhook(workflowName='fake.yaml', accessToken='fake_access_token')] - client = get_client() - created_build = client.create_build( - Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', - 'amd64', 'melodic', True, buildWebhooks=webhooks), False) - build = client.get_build('build-guid', include_build_requests=True) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_get_url, method='GET', params={'include_build_requests': True}), - ]) - self.assertEqual(created_build.get('guid'), 'build-guid') - self.assertEqual(created_build.get('buildName'), 'test_build') - self.assertEqual(build.buildRequests[0]['buildWebhooks'][0]['webhookType'], 'githubWorkflow') - self.assertEqual(build.buildRequests[0]['buildWebhooks'][0]['accessToken'], 'fake_access_token') - self.assertEqual(build.buildRequests[0]['buildWebhooks'][0]['workflowName'], 'fake.yaml') - self.assertEqual(build.buildRequests[0]['buildWebhooks'][0]['repositoryUrl'], 'https://github.com/rapyuta-robotics/io_tutorials.git') - - @patch('requests.request') - def test_get_build_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/{}'.format('build-guid') - mock_get_build = Mock() - mock_get_build.text = BUILD_GET_SUCCESS - mock_get_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_build] - client = get_client() - build = client.get_build('build-guid') - remove_auth_token(build) - delattr(build, 'dockerPullSecret') - expected_response = json.loads(BUILD_GET_SUCCESS) - expected_response[PartialMixin.PARTIAL_ATTR] = False - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='GET', - params=None) - self.assertEqual(build.get('guid'), 'build-guid') - self.assertEqual(build.get('buildName'), 'test_build') - self.assertEqual(build.to_dict(), expected_response) - self.assertFalse(build.is_partial) - - @patch('requests.request') - def test_get_build_with_guid_not_found(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/{}'.format('build-guid') - expected_err_msg = "build guid not found" - mock_get_build = Mock() - mock_get_build.text = BUILD_NOT_FOUND - mock_get_build.status_code = requests.codes.NOT_FOUND - mock_request.side_effect = [mock_get_build] - client = get_client() - with self.assertRaises(ResourceNotFoundError) as e: - client.get_build('build-guid') - self.assertEqual(str(e.exception), expected_err_msg) - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params=None) - - @patch('requests.request') - def test_get_build_with_query_params(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/{}'.format('build-guid') - expected_query_params = {'include_build_requests': True} - mock_get_build = Mock() - mock_get_build.text = BUILD_GET_SUCCESS_WITH_BUILD_REQUESTS - mock_get_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_build] - client = get_client() - build = client.get_build('build-guid', include_build_requests=True) - remove_auth_token(build) - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params=expected_query_params) - self.assertFalse(build.is_partial) - - @patch('requests.request') - def test_list_build_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - - mock_list_build = Mock() - mock_list_build.text = BUILD_LIST_SUCCESS - mock_list_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_build] - client = get_client() - builds = client.list_builds() - list(map(remove_auth_token, builds)) - list(map(lambda build: delattr(build, 'dockerPullSecret'), builds)) - expected_response = json.loads(BUILD_LIST_SUCCESS) - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params=None) - self.assertListEqual(builds, expected_response) - for build in builds: - self.assertTrue(build.is_partial) - - @patch('requests.request') - def test_build_refresh_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - - mock_list_build = Mock() - mock_list_build.text = BUILD_LIST_SUCCESS - mock_list_build.status_code = requests.codes.OK - mock_get_build = Mock() - mock_get_build.text = BUILD_GET_SUCCESS - mock_get_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_build, mock_get_build] - - client = get_client() - builds = client.list_builds() - build = builds[0] - self.assertTrue(build.is_partial) - builds[0].refresh() - - self.assertFalse(build.is_partial) - remove_auth_token(build) - expected_response = json.loads(BUILD_GET_SUCCESS) - expected_response[PartialMixin.PARTIAL_ATTR] = False - expected_response['dockerPullSecret'] = '' - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params=None), - call(headers=headers, json=None, url=expected_url+'/build-guid', method='GET', params={}), - ]) - self.assertEqual(build, expected_response) - - @patch('requests.request') - def test_list_builds_with_query_params_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build' - expected_query_params = {'status': ['Complete', 'BuildInProgress']} - mock_list_build = Mock() - mock_list_build.text = BUILD_LIST_SUCCESS - mock_list_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_build] - statuses = [BuildStatus.COMPLETE, BuildStatus.BUILD_IN_PROGRESS] - client = get_client() - builds = client.list_builds(statuses=statuses) - list(map(remove_auth_token, builds)) - list(map(lambda build: delattr(build, 'dockerPullSecret'), builds)) - expected_response = json.loads(BUILD_LIST_SUCCESS) - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params=expected_query_params) - self.assertListEqual(builds, expected_response) - for build in builds: - self.assertTrue(build.is_partial) - - def test_list_builds_with_invalid_statuses_type(self): - expected_err_msg = 'statuses must be an instance of list' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.list_builds(statuses={}) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_list_builds_with_invalid_status_value(self): - expected_err_msg = 'status must be of rapyuta_io.clients.build.BuildStatus' - client = get_client() - statuses = ['invalid-status'] - with self.assertRaises(InvalidParameterException) as e: - client.list_builds(statuses=statuses) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Source", - "architecture": "amd64", - "isRos": True, - "rosDistro": "melodic", - "repository": "https://github.com/rapyuta-robotics", - "contextDir": "contextDir", - "branch": "master", - "buildOptions": { - "catkinOptions": [ - { - "rosPkgs": "listener", - "cmakeArgs": None, - "makeArgs": None, - "blacklist": None, - "catkinMakeArgs": None - } - ] - }, - "secret": "test-secret" - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/{}'.format('build-guid') - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - setattr(build, '_host', 'https://gacatalog.apps.okd4v2.prod.rapyuta.io') - setattr(build, 'guid', 'build-guid') - setattr(build, '_auth_token', 'Bearer test_auth_token') - setattr(build, '_project', 'test_project') - mock_update_build = Mock() - mock_update_build.text = 'null' - mock_update_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_update_build] - build.buildInfo.repository = 'https://github.com/rapyuta-robotics' - build.buildInfo.branch = 'master' - build.buildInfo.contextDir = 'contextDir' - build.buildInfo.buildOptions = BuildOptions(catkinOptions=[CatkinOption(rosPkgs='listener')]) - build.secret = 'test-secret' - build.save() - mock_request.assert_called_with(headers=headers, json=expected_payload, - url=expected_url, method='PUT', params={}) - - @patch('requests.request') - def test_update_build_with_webhook_success(self, mock_request): - expected_payload = { - "buildName": "test_build", - "strategyType": "Source", - "architecture": "amd64", - "isRos": True, - "rosDistro": "melodic", - "repository": "https://github.com/rapyuta-robotics", - "contextDir": "contextDir", - "branch": "master", - "buildOptions": { - "catkinOptions": [ - { - "rosPkgs": "listener", - "cmakeArgs": None, - "makeArgs": None, - "blacklist": None, - "catkinMakeArgs": None - } - ] - }, - "secret": "test-secret", - "buildWebhooks": [ - { - "webhookType": "githubWorkflow", - "accessToken": "fake_access_token", - "workflowName": "fake.yaml" - } - ] - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/{}'.format('build-guid') - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - setattr(build, '_host', 'https://gacatalog.apps.okd4v2.prod.rapyuta.io') - setattr(build, 'guid', 'build-guid') - setattr(build, '_auth_token', 'Bearer test_auth_token') - setattr(build, '_project', 'test_project') - mock_update_build = Mock() - mock_update_build.text = 'null' - mock_update_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_update_build] - build.buildInfo.repository = 'https://github.com/rapyuta-robotics' - build.buildInfo.branch = 'master' - build.buildInfo.contextDir = 'contextDir' - build.buildInfo.buildOptions = BuildOptions(catkinOptions=[CatkinOption(rosPkgs='listener')]) - build.secret = 'test-secret' - build.buildWebhooks = [GithubWebhook(workflowName='fake.yaml', accessToken='fake_access_token')] - build.save() - mock_request.assert_called_with(headers=headers, json=expected_payload, - url=expected_url, method='PUT', params={}) - - @patch('requests.request') - def test_update_build_invalid_secret(self, mock_request): - expected_err_msg = 'secret must be a string' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.secret = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_docker_pull_secret(self, mock_request): - expected_err_msg = 'dockerPullSecret must be a string' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.dockerPullSecret = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_docker_push_repository(self, mock_request): - expected_err_msg = 'dockerPushRepository must be a string' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.dockerPushRepository = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_repository(self, mock_request): - expected_err_msg = 'repository must be a valid non-empty string' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.buildInfo.repository = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_branch(self, mock_request): - expected_err_msg = 'branch must be a valid non-empty string' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.buildInfo.branch = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_docker_file_path_usage_for_source_strategyType(self, mock_request): - expected_err_msg = 'cannot use dockerFilePath for source strategyType' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.buildInfo.dockerFilePath = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_context_directory(self, mock_request): - expected_err_msg = 'contextDir must be a string' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.buildInfo.contextDir = 1 - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_webhook(self, mock_request): - expected_err_msg = 'buildWebhooks must be a list of rapyuta_io.clients.build.GithubWebhook' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.buildWebhooks = '' - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_update_build_invalid_webhook_workflow(self, mock_request): - expected_err_msg = 'workflowName must be present and should be of string type' - build = Build('test_build', 'Source', 'https://github.com/rapyuta-robotics/io_tutorials.git', 'amd64', - 'melodic', True) - with self.assertRaises(InvalidParameterException) as e: - build.buildWebhooks = [GithubWebhook(workflowName='', accessToken='fake_access_token')] - build.save() - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_delete_build_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/{}'.format('build-guid') - get_build_response = MagicMock(spec=Response) - get_build_response.text = BUILD_GET_SUCCESS - get_build_response.status_code = requests.codes.OK - mock_delete_build = Mock() - mock_delete_build.text = None - mock_delete_build.status_code = requests.codes.OK - mock_request.side_effect = [get_build_response, mock_delete_build] - client = get_client() - client.delete_build('build-guid') - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='DELETE', params=None) - - @patch('requests.request') - def test_delete_build_build_not_found(self, mock_request): - expected_err_msg = 'build guid not found' - get_build_response = MagicMock(spec=Response) - get_build_response.text = BUILD_GET_SUCCESS - get_build_response.status_code = requests.codes.OK - mock_delete_build = Mock() - mock_delete_build.text = BUILD_NOT_FOUND - mock_delete_build.status_code = requests.codes.NOT_FOUND - mock_request.side_effect = [get_build_response, mock_delete_build] - client = get_client() - build = client.get_build('build-guid') - with self.assertRaises(ResourceNotFoundError) as e: - build.delete() - self.assertEqual(str(e.exception), expected_err_msg) - - def test_trigger_build_invalid_build_operation_info_type(self): - expected_err_msg = 'buildOperationInfo must be an instance of list' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.trigger_build(BuildOperation(buildOperationInfo={})) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_trigger_build_invalid_build_guid(self): - expected_err_msg = 'buildGuid must be a non-empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - build_op_info = [BuildOperationInfo("")] - req = BuildOperation(build_op_info) - client.trigger_build(BuildOperation(req)) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_trigger_build_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/trigger' - get_build_response = json.loads(BUILD_GET_SUCCESS) - mock_trigger_build = Mock() - mock_trigger_build.text = TRIGGER_BUILD_RESPONSE - mock_trigger_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_trigger_build] - trigger_request = BuildOperation([BuildOperationInfo('build-guid')]) - client = get_client() - trigger_response = client.trigger_build(trigger_request) - mock_request.assert_called_once_with(headers=headers, json=trigger_request, - url=expected_url, method='PUT', params=None) - self.assertEqual(get_build_response['buildGeneration'] + 1, - trigger_response['buildOperationResponse'][0]['buildGenerationNumber']) - - @patch('requests.request') - def test_trigger_build_success_with_trigger_name(self, mock_request): - expected_payload = { - "buildOperationInfo": [ - { - "buildGUID": "build-guid", - "triggerName": "trigger-name" - } - ] - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/trigger' - get_build_response = json.loads(BUILD_GET_SUCCESS) - mock_trigger_build = Mock() - mock_trigger_build.text = TRIGGER_BUILD_RESPONSE - mock_trigger_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_trigger_build] - trigger_request = BuildOperation([BuildOperationInfo('build-guid', triggerName='trigger-name')]) - client = get_client() - trigger_response = client.trigger_build(trigger_request) - mock_request.assert_called_once_with(headers=headers, json=expected_payload, - url=expected_url, method='PUT', params=None) - self.assertEqual(get_build_response['buildGeneration'] + 1, - trigger_response['buildOperationResponse'][0]['buildGenerationNumber']) - - @patch('requests.request') - def test_trigger_build_success_with_tag_name(self, mock_request): - expected_payload = { - "buildOperationInfo": [ - { - "buildGUID": "build-guid", - "tagName": "tag-name" - } - ] - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/trigger' - get_build_response = json.loads(BUILD_GET_SUCCESS) - mock_trigger_build = Mock() - mock_trigger_build.text = TRIGGER_BUILD_RESPONSE - mock_trigger_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_trigger_build] - trigger_request = BuildOperation([BuildOperationInfo('build-guid', tagName='tag-name')]) - client = get_client() - trigger_response = client.trigger_build(trigger_request) - mock_request.assert_called_once_with(headers=headers, json=expected_payload, - url=expected_url, method='PUT', params=None) - self.assertEqual(get_build_response['buildGeneration'] + 1, - trigger_response['buildOperationResponse'][0]['buildGenerationNumber']) - - @patch('requests.request') - def test_trigger_build_build_in_progress_error(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/trigger' - expected_err_msg = "build is in BuildInProgress state" - mock_trigger_build = Mock() - mock_trigger_build.text = BUILD_IN_PROGRESS_ERROR - mock_trigger_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_trigger_build] - trigger_request = BuildOperation([BuildOperationInfo('build-guid')]) - client = get_client() - trigger_response = client.trigger_build(trigger_request) - mock_request.assert_called_once_with(headers=headers, json=trigger_request, - url=expected_url, method='PUT', params=None) - self.assertEqual(trigger_response['buildOperationResponse'][0]['error'], expected_err_msg) - - @patch('requests.request') - def test_trigger_build_guid_not_found(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/trigger' - expected_err_msg = "build guid not found" - mock_trigger_build = Mock() - mock_trigger_build.text = BUILD_GUID_NOT_FOUND - mock_trigger_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_trigger_build] - trigger_request = BuildOperation([BuildOperationInfo('build-guid-1')]) - client = get_client() - trigger_response = client.trigger_build(trigger_request) - mock_request.assert_called_once_with(headers=headers, json=trigger_request, - url=expected_url, method='PUT', params=None) - self.assertEqual(trigger_response['buildOperationResponse'][0]['error'], expected_err_msg) - - @patch('requests.request') - def test_rollback_build_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/rollback' - get_build_response = json.loads(BUILD_GET_SUCCESS) - mock_rollback_build = Mock() - mock_rollback_build.text = ROLLBACK_BUILD_RESPONSE - mock_rollback_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_rollback_build] - rollback_request = BuildOperation([BuildOperationInfo('build-guid', 1)]) - client = get_client() - rollback_response = client.rollback_build(rollback_request) - mock_request.assert_called_once_with(headers=headers, json=rollback_request, - url=expected_url, method='PUT', params=None) - self.assertEqual(get_build_response['buildGeneration'], - rollback_response['buildOperationResponse'][0]['buildGenerationNumber']) - - @patch('requests.request') - def test_rollback_build_build_in_progress_error(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/rollback' - expected_err_msg = "build is in BuildInProgress state" - mock_rollback_build = Mock() - mock_rollback_build.text = BUILD_IN_PROGRESS_ERROR - mock_rollback_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_rollback_build] - rollback_request = BuildOperation([BuildOperationInfo('build-guid', 1)]) - client = get_client() - rollback_response = client.rollback_build(rollback_request) - mock_request.assert_called_once_with(headers=headers, json=rollback_request, - url=expected_url, method='PUT', params=None) - self.assertEqual(rollback_response['buildOperationResponse'][0]['error'], expected_err_msg) - - @patch('requests.request') - def test_rollback_build_guid_not_found(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/build/operation/rollback' - expected_err_msg = "build guid not found" - mock_rollback_build = Mock() - mock_rollback_build.text = BUILD_GUID_NOT_FOUND - mock_rollback_build.status_code = requests.codes.OK - mock_request.side_effect = [mock_rollback_build] - rollback_request = BuildOperation([BuildOperationInfo('build-guid-1', 1)]) - client = get_client() - rollback_response = client.rollback_build(rollback_request) - mock_request.assert_called_once_with(headers=headers, json=rollback_request, - url=expected_url, method='PUT', params=None) - self.assertEqual(rollback_response['buildOperationResponse'][0]['error'], expected_err_msg) - - @patch('requests.request') - def test_rollback_build_invalid_build_gen_number(self, mock_request): - expected_err_msg = 'build generation number must be an integer and greater than 0' - get_build_response = MagicMock(spec=Response) - get_build_response.text = BUILD_GET_SUCCESS - get_build_response.status_code = requests.codes.OK - mock_request.side_effect = [get_build_response] - build = get_client().get_build('build-guid') - with self.assertRaises(InvalidParameterException) as e: - build.rollback(-1) - self.assertEqual(str(e.exception), expected_err_msg) diff --git a/tests/client_get_package_test.py b/tests/client_get_package_test.py deleted file mode 100644 index a99c819f..00000000 --- a/tests/client_get_package_test.py +++ /dev/null @@ -1,221 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import -import requests -import unittest - -from mock import patch, Mock -from requests import Response - -from rapyuta_io.clients.package import Package -from rapyuta_io.clients.persistent_volumes import PersistentVolumes -from rapyuta_io.clients.plan import Plan -from rapyuta_io.utils.error import APIError, PackageNotFound, PlanNotFound, InternalServerError,\ - InvalidParameterException -from tests.utils.client import get_client -from tests.utils.package_responses import PACKAGE_OK_VALIDATE, PACKAGES_LIST, PERSISTENT_VOLUME_INFO - - -class ClientPackageTests(unittest.TestCase): - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_package_ok(self, rest_mock, response): - response.text = PACKAGE_OK_VALIDATE - response.status_code = requests.codes.OK - rest_mock.return_value = response - client = get_client() - pkg = client.get_package('test_package_id') - rest_mock.assert_called_once() - self.assertIsInstance(pkg, Package) - self.assertEqual(pkg.packageId, 'test_package_id') - self.assertEqual(pkg.packageName, 'test-1.0') - self.assertEqual(pkg._auth_token, 'Bearer test_auth_token') - self.assertFalse(pkg.is_partial) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_package_no_plan(self, rest_mock, response): - response.text = PACKAGE_OK_VALIDATE - response.status_code = requests.codes.OK - rest_mock.return_value = response - client = get_client() - pkg = client.get_package('my_package') - with self.assertRaises(PlanNotFound): - pkg.get_plan_by_id('test_plan_id') - rest_mock.assert_called_once() - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_package_no_pkg_info(self, rest_mock, response): - response.text = '{}' - response.status_code = requests.codes.OK - rest_mock.return_value = response - client = get_client() - with self.assertRaises(APIError): - client.get_package('test_package_id') - rest_mock.assert_called_once() - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_package_no_service_id(self, rest_mock, response): - response.text = '{}' - response.status_code = requests.codes.NOT_FOUND - rest_mock.return_value = response - client = get_client() - with self.assertRaises(PackageNotFound): - client.get_package('test_package_id') - rest_mock.assert_called_once() - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_package_api_error(self, rest_mock, response): - response.text = '{}' - response.status_code = requests.codes.INTERNAL_SERVER_ERROR - rest_mock.return_value = response - client = get_client() - with self.assertRaises(InternalServerError): - client.get_package('test_package_id') - rest_mock.assert_called_once() - - def test_get_all_packages_failure_invalid_filter_name(self): - invalid_filter_name = 25 - expected_err_msg = 'name must be a string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.get_all_packages(name=invalid_filter_name) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_get_all_packages_failure_invalid_filter_version(self): - invalid_filter_version = 1.0 - expected_err_msg = 'version must be a string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.get_all_packages(version=invalid_filter_version) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_all_packages_success_no_filter_parameters(self, rest_mock, response): - response.text = PACKAGES_LIST - response.status_code = requests.codes.OK - rest_mock.return_value = response - rest_mock.side_effect = [response] - client = get_client() - pkg_list = client.get_all_packages() - rest_mock.assert_called_once() - self.assertEqual(len(pkg_list), 2) - for pkg in pkg_list: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assertEqual(pkg_list[0].packageName, 'test_package') - self.assertEqual(pkg_list[0].packageId, 'package_id') - - @patch('requests.request') - def test_get_all_packages_success_filter_name_only(self, rest_mock): - filter_name = 'test' - all_packages = Mock() - all_packages.text = PACKAGES_LIST - all_packages.status_code = requests.codes.OK - rest_mock.side_effect = [all_packages] - client = get_client() - pkgs = client.get_all_packages(name=filter_name) - self.assertEqual(rest_mock.call_count, 1) - self.assertEqual(len(pkgs), 1) - for pkg in pkgs: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assertIn(filter_name, pkgs[0].packageName) - - @patch('requests.request') - def test_get_all_packages_success_filter_version_only(self, rest_mock): - filter_version = 'v1.0' - all_packages = Mock() - all_packages.text = PACKAGES_LIST - all_packages.status_code = requests.codes.OK - rest_mock.side_effect = [all_packages] - client = get_client() - pkgs = client.get_all_packages(version=filter_version) - self.assertEqual(rest_mock.call_count, 1) - self.assertEqual(len(pkgs), 2) - for pkg in pkgs: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assertEqual(filter_version, pkgs[0].packageVersion) - - @patch('requests.request') - def test_get_all_packages_success_filter_name_filter_version(self, rest_mock): - filter_version = 'v1.0' - filter_name = 'test' - all_packages = Mock() - all_packages.text = PACKAGES_LIST - all_packages.status_code = requests.codes.OK - rest_mock.side_effect = [all_packages] - client = get_client() - pkgs = client.get_all_packages(name=filter_name, version=filter_version) - self.assertEqual(rest_mock.call_count, 1) - self.assertEqual(len(pkgs), 1) - for pkg in pkgs: - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertTrue(pkg.is_partial) - self.assertIn(filter_name, pkgs[0].packageName) - self.assertEqual(filter_version, pkgs[0].packageVersion) - - @patch('requests.request') - def test_get_all_packages_success_no_data_filter_name_filter_version(self, rest_mock): - filter_version = 'v1.1' - filter_name = 'test' - all_packages = Mock() - all_packages.text = PACKAGES_LIST - all_packages.status_code = requests.codes.OK - rest_mock.side_effect = [all_packages] - client = get_client() - pkgs = client.get_all_packages(name=filter_name, version=filter_version) - self.assertEqual(rest_mock.call_count, 1) - self.assertEqual(len(pkgs), 0) - - @patch('requests.request') - def test_package_refresh_ok(self, rest_mock): - package_list = Mock() - package_list.text = PACKAGES_LIST - package_list.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE - get_package.status_code = requests.codes.OK - rest_mock.side_effect = [package_list, get_package] - client = get_client() - pkg_list = client.get_all_packages() - pkg = pkg_list[0] - self.assertTrue(pkg.is_partial) - pkg.refresh() - self.assertFalse(pkg.is_partial) - self.assertIsInstance(pkg, Package, 'pkg should be instance of Package class') - self.assertEqual(pkg_list[0].packageName, 'test-1.0') - self.assertEqual(pkg_list[0].packageId, 'pkg-xlhpyvigqoorhnomeryjfjqx') - self.assertEqual(rest_mock.call_count, 2) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_all_packages_api_error(self, rest_mock, response): - response.text = {} - response.status_code = requests.codes.INTERNAL_SERVER_ERROR - rest_mock.return_value = response - client = get_client() - with self.assertRaises(InternalServerError): - client.get_all_packages() - rest_mock.assert_called_once() - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_persistent_volume_ok(self, rest_mock, response): - response.text = PERSISTENT_VOLUME_INFO - response.status_code = requests.codes.OK - rest_mock.return_value = response - client = get_client() - persistent_volume = client.get_persistent_volume() - self.assertIsInstance(persistent_volume, PersistentVolumes, - 'object should be instance of PersitentVolumes class') - self.assertEqual(persistent_volume.packageId, 'io-public-persistent-volume') - self.assertEqual(persistent_volume.packageName, 'Rapyuta IO Persistent Volume') - for plan in persistent_volume.plans: - self.assertIsInstance(plan, Plan, 'Object should be instance of class Plan') - rest_mock.assert_called_once() diff --git a/tests/deployment_test.py b/tests/deployment_test.py deleted file mode 100644 index 8d2f9f61..00000000 --- a/tests/deployment_test.py +++ /dev/null @@ -1,232 +0,0 @@ -from __future__ import absolute_import -import requests -import unittest -import json - -from mock import Mock, patch, MagicMock, call -from requests import Response - -from rapyuta_io import DeploymentPhaseConstants -from rapyuta_io.utils import InternalServerError, RetriesExhausted, InvalidParameterException -from rapyuta_io.utils.rest_client import HttpMethod -from sdk_test.util import get_manifest_file -from tests.utils.client import get_client, headers -from tests.utils.package_responses import DEPLOYMENT_INFO, \ - DEPLOYMENT_STATUS_RUNNING, DEPLOYMENT_BINDING_OK, DEPLOYMENT_LIST, DEPLOYMENT_STATUS_PENDING, \ - DEPLOYMENT_STATUS_RUNNING_PHASE_PROVISIONING, UPDATE_DEPLOYMENT - - -class DeploymentTest(unittest.TestCase): - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_package_bindable(self, rest_mock, deployment_info_rseponse): - deployment_info_rseponse.text = DEPLOYMENT_INFO - deployment_info_rseponse.status_code = requests.codes.OK - binding_response = deployment_info_rseponse() - binding_response.text = DEPLOYMENT_BINDING_OK - binding_response.status_code = requests.codes.OK - rest_mock.side_effect = [deployment_info_rseponse, binding_response] - client = get_client() - deployment = client.get_deployment('deploment_id') - binding_result = deployment.get_service_binding('binding_id') - self.assertIsNotNone(binding_result, 'binding response should not be empty') - self.assertIn('credentials', binding_result, 'Credentials should not be empty') - self.assertEqual(rest_mock.call_count, 2) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_get_deployment_status_ok(self, rest_mock, deployment_info_response): - deployment_info_response.text = DEPLOYMENT_INFO - deployment_info_response.status_code = requests.codes.OK - deployment_status_response = deployment_info_response() - deployment_status_response.text = DEPLOYMENT_STATUS_RUNNING - deployment_status_response.status_code = requests.codes.OK - rest_mock.side_effect = [deployment_info_response, deployment_status_response] - client = get_client() - deployment = client.get_deployment('deployment_id') - self.assertFalse(deployment.is_partial) - deployment_status = deployment.get_status() - self.assertEqual(deployment_status.packageId, 'package_id') - self.assertEqual(deployment_status.planId, 'test-plan') - self.assertEqual(deployment_status.status, 'Running') - self.assertEqual(deployment_status.phase, 'Succeeded') - self.assertEqual(rest_mock.call_count, 2) - - @patch('requests.request') - def test_poll_deployment_till_ready_ok(self, mock_request): - deployment_info_response = MagicMock(spec=Response) - deployment_info_response.text = DEPLOYMENT_INFO - deployment_info_response.status_code = requests.codes.OK - first_deployment_status_response = MagicMock(spec=Response) - first_deployment_status_response.text = DEPLOYMENT_STATUS_PENDING - first_deployment_status_response.status_code = requests.codes.OK - second_deployment_status_response = MagicMock(spec=Response) - second_deployment_status_response.text = DEPLOYMENT_STATUS_RUNNING - second_deployment_status_response.status_code = requests.codes.OK - mock_request.side_effect = [deployment_info_response, first_deployment_status_response, - second_deployment_status_response] - deployment = get_client().get_deployment('deployment_id') - deployment_status = deployment.poll_deployment_till_ready(retry_count=2, sleep_interval=0) - - self.assertEqual(deployment_status.packageId, 'package_id') - self.assertEqual(deployment_status.planId, 'test-plan') - self.assertEqual(deployment_status.status, 'Running') - self.assertEqual(deployment_status.phase, 'Succeeded') - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_poll_deployment_till_ready_retries_exhausted(self, mock_request): - deployment_info_response = MagicMock(spec=Response) - deployment_info_response.text = DEPLOYMENT_INFO - deployment_info_response.status_code = requests.codes.OK - first_deployment_status_response = MagicMock(spec=Response) - first_deployment_status_response.text = DEPLOYMENT_STATUS_PENDING - first_deployment_status_response.status_code = requests.codes.OK - second_deployment_status_response = MagicMock(spec=Response) - second_deployment_status_response.text = DEPLOYMENT_STATUS_RUNNING_PHASE_PROVISIONING - second_deployment_status_response.status_code = requests.codes.OK - mock_request.side_effect = [deployment_info_response, first_deployment_status_response, - second_deployment_status_response] - - deployment = get_client().get_deployment('deployment_id') - regexp = 'Retries exhausted: Tried 2 times with 0s interval. Deployment: phase=Provisioning status=Running' + \ - ' errors=None' - with self.assertRaisesRegex(RetriesExhausted, regexp): - deployment.poll_deployment_till_ready(retry_count=2, sleep_interval=0) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_package_deprovision_api_error(self, rest_mock, deployment_info_response): - deployment_info_response.text = DEPLOYMENT_INFO - deployment_info_response.status_code = requests.codes.OK - deprovision_response = deployment_info_response() - deprovision_response.status_code = requests.codes.INTERNAL_SERVER_ERROR - rest_mock.side_effect = [deployment_info_response, deprovision_response] - client = get_client() - deployment = client.get_deployment('deployment_id') - with self.assertRaises(InternalServerError): - deployment.deprovision() - self.assertEqual(rest_mock.call_count, 2) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_package_bindable_api_error(self, rest_mock, deployment_info_response): - deployment_info_response.text = DEPLOYMENT_INFO - deployment_info_response.status_code = requests.codes.OK - binding_response = deployment_info_response() - binding_response.status_code = requests.codes.INTERNAL_SERVER_ERROR - rest_mock.side_effect = [deployment_info_response, binding_response] - client = get_client() - deployment = client.get_deployment('deployment_id') - with self.assertRaises(InternalServerError): - deployment.get_service_binding() - self.assertEqual(rest_mock.call_count, 2) - - @patch('requests.Response', spec=Response) - @patch('rapyuta_io.utils.rest_client.RestClient.execute') - def test_deployment_status_api_error(self, rest_mock, deployment_info_response): - deployment_info_response.text = DEPLOYMENT_INFO - deployment_info_response.status_code = requests.codes.OK - deployment_status_response = deployment_info_response() - deployment_status_response.status_code = requests.codes.INTERNAL_SERVER_ERROR - rest_mock.side_effect = [deployment_info_response, deployment_status_response] - client = get_client() - deployment = client.get_deployment('deployment_id') - with self.assertRaises(InternalServerError): - deployment.get_status() - self.assertEqual(rest_mock.call_count, 2) - - @patch('rapyuta_io.clients.catalog_client.CatalogClient._execute') - def test_get_deployment_list_ok(self, catalog_mock_execute): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - expected_query_param = {'phase': ['Succeeded', 'Provisioning']} - catalog_mock_execute.return_value = MagicMock() - catalog_mock_execute.return_value.status_code = 200 - catalog_mock_execute.return_value.text = DEPLOYMENT_LIST - phases = [DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING] - deployments = get_client().get_all_deployments(phases=phases) - - catalog_mock_execute.assert_called_once_with(expected_url, HttpMethod.GET, 0, - query_params=expected_query_param) - for dep in deployments: - self.assertTrue(dep.is_partial) - - @patch('rapyuta_io.clients.catalog_client.CatalogClient._execute') - def test_get_deployment_list_without_query_param(self, catalog_mock_execute): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - catalog_mock_execute.return_value = MagicMock() - catalog_mock_execute.return_value.status_code = 200 - catalog_mock_execute.return_value.text = DEPLOYMENT_LIST - get_client().get_all_deployments() - catalog_mock_execute.assert_called_once_with(expected_url, HttpMethod.GET, 0, query_params={}) - - @patch('rapyuta_io.clients.catalog_client.CatalogClient._execute') - def test_get_deployment_list_with_phase_query_param(self, catalog_mock_execute): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - expected_query_param = {'phase': ['Succeeded', 'Provisioning']} - catalog_mock_execute.return_value = MagicMock() - catalog_mock_execute.return_value.status_code = 200 - catalog_mock_execute.return_value.text = DEPLOYMENT_LIST - get_client().get_all_deployments(phases=[DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING]) - catalog_mock_execute.assert_called_once_with(expected_url, HttpMethod.GET, 0, query_params=expected_query_param) - - @patch('requests.request') - def test_get_bad_request_error_with_invalid_device_id(self, mock_request): - expected_err_msg = 'invalid deviceID' - with self.assertRaises(InvalidParameterException) as e: - get_client().get_all_deployments(device_id=1234) - - self.assertEqual(str(e.exception), expected_err_msg) - self.assertEqual(mock_request.call_count, 0) - - @patch('requests.request') - def test_get_filtered_deployment_list_with_valid_device_id(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - expected_query_param = {'device_uid': 'test-device-id'} - client = get_client() - mock_get_filtered_deployment = Mock() - mock_get_filtered_deployment.status_code = 200 - mock_get_filtered_deployment.text = DEPLOYMENT_LIST - mock_request.side_effect = [mock_get_filtered_deployment] - client.get_all_deployments(device_id='test-device-id') - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='GET', - params=expected_query_param) - self.assertEqual(mock_get_filtered_deployment.status_code, 200) - - @patch('requests.request') - def test_deployment_refresh_ok(self, mock_request): - deployment_list = Mock() - deployment_list.text = DEPLOYMENT_LIST - deployment_list.status_code = requests.codes.OK - get_deployment = Mock() - get_deployment.text = DEPLOYMENT_STATUS_RUNNING - get_deployment.status_code = requests.codes.OK - mock_request.side_effect = [deployment_list, get_deployment] - - client = get_client() - deployments = client.get_all_deployments() - self.assertTrue(deployments[0].is_partial) - deployments[0].refresh() - self.assertFalse(deployments[0].is_partial) - self.assertEqual(deployments[0].packageId, 'package_id') - self.assertEqual(deployments[0].planId, 'test-plan') - self.assertEqual(deployments[0].status, 'Running') - self.assertEqual(deployments[0].phase, 'Succeeded') - self.assertEqual(mock_request.call_count, 2) - - @patch('rapyuta_io.clients.catalog_client.CatalogClient._execute') - def test_update_deployment_success(self, catalog_mock_execute): - catalog_mock_execute.return_value = MagicMock() - catalog_mock_execute.return_value.status_code = 200 - catalog_mock_execute.return_value.text = UPDATE_DEPLOYMENT - payload = json.loads(UPDATE_DEPLOYMENT) - get_client().update_deployment(payload, 0) - catalog_mock_execute.assert_has_calls([call( - 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/v2/service_instances/dep-xyiwwwfongcfhpkhqlohtbee', - HttpMethod.PATCH, 0, - payload=payload)]) diff --git a/tests/native_network_test.py b/tests/native_network_test.py deleted file mode 100644 index 85141a5f..00000000 --- a/tests/native_network_test.py +++ /dev/null @@ -1,536 +0,0 @@ -from __future__ import absolute_import -import unittest -import requests -from mock import patch, Mock, call -from rapyuta_io.utils import ResourceNotFoundError - -from rapyuta_io.utils.error import InvalidParameterException -from tests.utils.client import get_client, headers, add_auth_token -from rapyuta_io.clients.package import Runtime, ROSDistro, Device -from rapyuta_io.clients.native_network import NativeNetwork, Parameters -from tests.utils.native_network_responses import NATIVE_NETWORK_CREATE_SUCCESS, NATIVE_NETWORK_LIST_SUCCESS, \ - NATIVE_NETWORK_GET_SUCCESS, NATIVE_NETWORK_FAILURE, NATIVE_NETWORK_NOT_FOUND -from rapyuta_io.clients.common_models import Limits - -class NativeNetworkTests(unittest.TestCase): - def setUp(self): - native_network = { - "ID": 1, - "name": "native_network_name", - "guid": "net-guid", - "ownerProject": "project-id", - "creator": "creator-id", - "runtime": "cloud", - "rosDistro": "kinetic", - "internalDeploymentGUID": "dep-id", - "internalDeploymentStatus": { - "phase": "Succeeded", - "status": "Running" - }, - "parameters": { - "limits": { - "cpu": 1, - "memory": 4096 - } - } - } - self.native_network = NativeNetwork.deserialize(native_network) - add_auth_token(self.native_network) - - def test_create_native_network_name_empty(self): - expected_err_msg = 'name must be a non-empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network(NativeNetwork('', Runtime.CLOUD, ROSDistro.KINETIC)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_invalid_name(self): - expected_err_msg = 'name must be a non-empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network(NativeNetwork(1, Runtime.CLOUD, ROSDistro.KINETIC)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_noetic_device_err(self): - with self.assertRaises(InvalidParameterException) as e: - NativeNetwork('native_network_name', Runtime.DEVICE, ROSDistro.NOETIC) - self.assertEqual(str(e.exception), 'device runtime does not support noetic ros_distro yet') - - def test_create_native_network_invalid_runtime(self): - expected_err_msg = 'runtime must be one of rapyuta_io.clients.package.Runtime' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network(NativeNetwork('native_network_name', 'invalid_runtime', ROSDistro.KINETIC)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_invalid_rosdistro(self): - expected_err_msg = 'ros_distro must be one of rapyuta_io.clients.package.ROSDistro' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network(NativeNetwork('native_network_name', Runtime.CLOUD, 'invalid rosdistro')) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_invalid_parameters(self): - expected_err_msg = 'parameters must be of type rapyuta_io.clients.native_network.Parameters' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network(NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.MELODIC, - 'invalid_parameters')) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_parameters_invalid_device(self): - expected_err_msg = 'device must be of type rapyuta_io.clients.device.Device' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - parameters = Parameters(device='device', network_interface='lo') - client.create_native_network( - NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_parameters_empty_device_uuid(self): - expected_err_msg = 'uuid field must be present in rapyuta_io.clients.device.Device object' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - device = Device('dev-name') - device.uuid = '' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='lo') - client.create_native_network( - NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_parameters_empty_ip_interfaces(self): - expected_err_msg = 'ip_interfaces field must be present in rapyuta_io.clients.device.Device object' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - device = Device('dev-name') - device.uuid = 'random' - parameters = Parameters(device=device, network_interface='lo') - client.create_native_network( - NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_parameters_invalid_network_interface(self): - expected_err_msg = 'NETWORK_INTERFACE should be in [\'lo\']' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - device = Device('dev-name') - device.uuid = 'random' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='docker0') - client.create_native_network( - NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_parameters_invalid_restart_policy(self): - expected_err_msg = 'RestartPolicy must be one of rapyuta_io.clients.package.RestartPolicy' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - device = Device('dev-name') - device.uuid = 'random' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='lo', restart_policy='') - client.create_native_network( - NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_no_parameters(self): - expected_err_msg = 'parameters must be present for device runtime' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network( - NativeNetwork('native_network_name', Runtime.DEVICE, ROSDistro.MELODIC, None)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_no_device_id(self): - expected_err_msg = 'device_id field must be present in rapyuta_io.clients.' \ - 'native_network.Parameters object for device runtime' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - device = Device('dev-name') - device.uuid = 'random' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='lo') - parameters.device_id = '' - client.create_native_network( - NativeNetwork('native_network_name', Runtime.DEVICE, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_device_runtime_no_network_interface(self): - expected_err_msg = 'network_interface must be present in rapyuta_io.clients.' \ - 'native_network.Parameters object for device runtime' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - device = Device('dev-name') - device.uuid = 'random' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='lo') - parameters.network_interface = '' - client.create_native_network( - NativeNetwork('native_network_name', Runtime.DEVICE, ROSDistro.MELODIC, parameters)) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_native_network_object_invalid_native_network_payload_object(self): - expected_err_msg = 'native_network must be non-empty and of type ' \ - 'rapyuta_io.clients.native_network.NativeNetwork' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_native_network(1) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_create_noetic_native_network_success(self, mock_request): - expected_payload = { - "name": "native_network_name", - "runtime": 'cloud', - "rosDistro": 'noetic' - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_create_native_network = Mock() - mock_create_native_network.text = NATIVE_NETWORK_CREATE_SUCCESS - mock_create_native_network.status_code = requests.codes.OK - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_native_network, mock_get_native_network] - - client = get_client() - native_network_parameters = None - native_network_payload = NativeNetwork("native_network_name", Runtime.CLOUD, ROSDistro.NOETIC, - native_network_parameters) - native_network_response = client.create_native_network(native_network_payload) - print(headers, expected_payload, expected_url, 'POST', None) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-guid', method='GET', params=None) - ]) - self.assertEqual(native_network_response.guid, 'net-guid') - self.assertEqual(native_network_response.name, 'native_network_name') - self.assertFalse(native_network_response.is_partial) - - @patch('requests.request') - def test_create_native_network_success(self, mock_request): - expected_payload = { - "name": "native_network_name", - "runtime": 'cloud', - "rosDistro": 'kinetic', - "parameters": {"limits": {"cpu": 1, "memory": 1024}} - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_create_native_network = Mock() - mock_create_native_network.text = NATIVE_NETWORK_CREATE_SUCCESS - mock_create_native_network.status_code = requests.codes.OK - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_native_network, mock_get_native_network] - - client = get_client() - native_network_parameters = Parameters(limits=Limits(1,1024)) - native_network_payload = NativeNetwork("native_network_name", Runtime.CLOUD, ROSDistro.KINETIC, - native_network_parameters) - native_network_response = client.create_native_network(native_network_payload) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-guid', method='GET', params=None) - ]) - self.assertEqual(native_network_response.guid, 'net-guid') - self.assertEqual(native_network_response.name, 'native_network_name') - self.assertFalse(native_network_response.is_partial) - - @patch('requests.request') - def test_create_native_network_success_without_parameters(self, mock_request): - expected_payload = { - "name": "native_network_name", - "runtime": 'cloud', - "rosDistro": 'kinetic' - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_create_native_network = Mock() - mock_create_native_network.text = NATIVE_NETWORK_CREATE_SUCCESS - mock_create_native_network.status_code = requests.codes.OK - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_native_network, mock_get_native_network] - - client = get_client() - native_network_payload = NativeNetwork("native_network_name", Runtime.CLOUD, ROSDistro.KINETIC) - native_network_response = client.create_native_network(native_network_payload) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-guid', method='GET', params=None) - ]) - self.assertEqual(native_network_response.guid, 'net-guid') - self.assertEqual(native_network_response.name, 'native_network_name') - - @patch('requests.request') - def test_create_device_native_network_success(self, mock_request): - expected_payload = { - "name": "native_network_name", - "runtime": 'device', - "rosDistro": 'melodic', - "parameters": {"device_id": "random", "NETWORK_INTERFACE": "lo"} - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_create_native_network = Mock() - mock_create_native_network.text = NATIVE_NETWORK_CREATE_SUCCESS - mock_create_native_network.status_code = requests.codes.OK - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_native_network, mock_get_native_network] - - client = get_client() - device = Device('dev-name') - device.uuid = 'random' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='lo') - native_network_payload = NativeNetwork("native_network_name", Runtime.DEVICE, ROSDistro.MELODIC, - parameters) - native_network_response = client.create_native_network(native_network_payload) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-guid', method='GET', params=None) - ]) - self.assertEqual(native_network_response.guid, 'net-guid') - self.assertEqual(native_network_response.name, 'native_network_name') - self.assertFalse(native_network_response.is_partial) - - def test_get_native_network_invalid_guid(self): - expected_err_msg = 'guid needs to be a non empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.get_native_network(None) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_get_native_network_guid_empty(self): - expected_err_msg = 'guid needs to be a non empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.get_native_network('') - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_get_native_network_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork/{}'.format('net-guid') - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_native_network] - - client = get_client() - native_network = client.get_native_network('net-guid') - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='GET', - params=None) - self.assertEqual(native_network.name, 'native_network_name') - self.assertEqual(native_network.runtime, 'cloud') - self.assertEqual(native_network.ros_distro, 'kinetic') - self.assertEqual(native_network.parameters.limits.cpu, 1) - self.assertEqual(native_network.parameters.limits.memory, 4096) - self.assertEqual(native_network.updated_at, '2021-02-05T13:16:08.736362Z') - self.assertEqual(native_network.guid, 'net-guid') - self.assertEqual(native_network.owner_project, 'project-id') - self.assertEqual(native_network.creator, 'creator-id') - self.assertEqual(native_network.internal_deployment_guid, 'dep-id') - self.assertEqual(native_network.internal_deployment_status.phase, 'Succeeded') - self.assertFalse(native_network.is_partial) - - @patch('requests.request') - def test_list_native_network_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_list_native_network = Mock() - mock_list_native_network.text = NATIVE_NETWORK_LIST_SUCCESS - mock_list_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_native_network] - - client = get_client() - native_network_list = client.list_native_networks() - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='GET', - params=None) - self.assertEqual(native_network_list[0].name, 'native_network_name') - self.assertEqual(native_network_list[0].runtime, 'cloud') - self.assertEqual(native_network_list[0].ros_distro, 'kinetic') - self.assertEqual(native_network_list[0].parameters.limits.cpu, 1) - self.assertEqual(native_network_list[0].parameters.limits.memory, 4096) - self.assertEqual(native_network_list[0].updated_at, '2021-02-05T13:16:08.736362Z') - self.assertEqual(native_network_list[0].guid, 'net-guid') - self.assertEqual(native_network_list[0].owner_project, 'project-id') - self.assertEqual(native_network_list[0].creator, 'creator-id') - self.assertEqual(native_network_list[0].internal_deployment_guid, 'dep-id') - self.assertEqual(native_network_list[0].internal_deployment_status.phase, 'Succeeded') - for net in native_network_list: - self.assertTrue(net.is_partial) - - @patch('requests.request') - def test_native_network_refresh_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_list_native_network = Mock() - mock_list_native_network.text = NATIVE_NETWORK_LIST_SUCCESS - mock_list_native_network.status_code = requests.codes.OK - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_native_network, mock_get_native_network] - - client = get_client() - native_network_list = client.list_native_networks() - self.assertTrue(native_network_list[0].is_partial) - native_network_list[0].refresh() - self.assertFalse(native_network_list[0].is_partial) - - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params=None), - call(headers=headers, json=None, url=expected_url + '/net-guid', method='GET', params={}), - ]) - self.assertEqual(native_network_list[0].name, 'native_network_name') - self.assertEqual(native_network_list[0].runtime, 'cloud') - self.assertEqual(native_network_list[0].ros_distro, 'kinetic') - self.assertEqual(native_network_list[0].parameters.limits.cpu, 1) - self.assertEqual(native_network_list[0].parameters.limits.memory, 4096) - self.assertEqual(native_network_list[0].updated_at, '2021-02-05T13:16:08.736362Z') - self.assertEqual(native_network_list[0].guid, 'net-guid') - self.assertEqual(native_network_list[0].owner_project, 'project-id') - self.assertEqual(native_network_list[0].creator, 'creator-id') - self.assertEqual(native_network_list[0].internal_deployment_guid, 'dep-id') - self.assertEqual(native_network_list[0].internal_deployment_status.phase, 'Succeeded') - self.assertEqual(native_network_list[0].internal_deployment_status.status, 'Running') - - def test_delete_native_network_invalid_guid(self): - expected_err_msg = 'guid needs to be a non empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.delete_native_network(None) - self.assertEqual(str(e.exception), expected_err_msg) - - def test_delete_native_network_guid_empty(self): - expected_err_msg = 'guid needs to be a non empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.delete_native_network('') - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_delete_native_network_not_found(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork/{}'.format('net-guid') - mock_delete_native_network = Mock() - mock_delete_native_network.text = NATIVE_NETWORK_NOT_FOUND - mock_delete_native_network.status_code = requests.codes.NOT_FOUND - mock_request.side_effect = [mock_delete_native_network] - client = get_client() - with self.assertRaises(ResourceNotFoundError) as e: - client.delete_native_network('net-guid') - mock_request.assert_called_once_with(headers=headers, url=expected_url, json=None, - method='DELETE', params=None) - self.assertEqual(str(e.exception), 'native network not found in db') - - @patch('requests.request') - def test_delete_native_network_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork/{}'.format('net-guid') - mock_delete_native_network = Mock() - mock_delete_native_network.text = 'null' - mock_delete_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_delete_native_network] - - client = get_client() - client.delete_native_network('net-guid') - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='DELETE', - params=None) - - @patch('requests.request') - def test_delete_native_network_success_with_native_network_object(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork/{}'.format('net-guid') - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_native_network.status_code = requests.codes.OK - mock_delete_native_network = Mock() - mock_delete_native_network.text = 'null' - mock_delete_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_native_network, mock_delete_native_network] - - client = get_client() - native_network = client.get_native_network('net-guid') - native_network.delete() - mock_request.assert_called_with(headers=headers, - json=None, - url=expected_url, - method='DELETE', - params={}) - self.assertEqual(mock_request.call_count, 2) - - @patch('requests.request') - def test_get_status_native_network_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork/{}'.format('net-guid') - internal_deployment_status_expected = self.native_network.internal_deployment_status - - mock_get_status_native_network = Mock() - mock_get_status_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_get_status_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_status_native_network] - - internal_deployment_status_actual = self.native_network.get_status() - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='GET', - params={}) - self.assertEqual(internal_deployment_status_expected.phase, internal_deployment_status_actual.phase) - self.assertEqual(internal_deployment_status_expected.status, internal_deployment_status_actual.status) - - @patch('requests.request') - def test_create_native_network_failure(self, mock_request): - expected_payload = { - "name": "native_network_name", - "runtime": 'cloud', - "rosDistro": 'kinetic' - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork' - mock_create_native_network = Mock() - mock_create_native_network.text = NATIVE_NETWORK_FAILURE - mock_create_native_network.status_code = requests.codes.OK - mock_get_native_network = Mock() - mock_get_native_network.text = NATIVE_NETWORK_FAILURE - mock_get_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_native_network, mock_get_native_network] - - mock_request.side_effect = [mock_create_native_network, mock_get_native_network] - - client = get_client() - native_network_payload = NativeNetwork("native_network_name", Runtime.CLOUD, ROSDistro.KINETIC) - native_network_response = client.create_native_network(native_network_payload) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-guid', method='GET', params=None) - ]) - self.assertEqual(native_network_response.internal_deployment_status.error_code[0], "DEP_E209") - self.assertEqual(native_network_response.internal_deployment_status.phase, "Failed to start") - - @patch('requests.request') - def test_poll_native_network_till_ready(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/nativenetwork/{}'.format('net-guid') - internal_deployment_status_expected = self.native_network.internal_deployment_status - - mock_poll_native_network = Mock() - mock_poll_native_network.text = NATIVE_NETWORK_GET_SUCCESS - mock_poll_native_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_poll_native_network] - - internal_deployment_status_actual = self.native_network.poll_native_network_till_ready() - mock_request.assert_called_once_with(headers=headers, - json=None, - url=expected_url, - method='GET', - params={}) - self.assertEqual(internal_deployment_status_expected.phase, - internal_deployment_status_actual.internal_deployment_status.phase) - self.assertEqual(internal_deployment_status_expected.status, - internal_deployment_status_actual.internal_deployment_status.status) diff --git a/tests/package_test.py b/tests/package_test.py deleted file mode 100644 index a20819af..00000000 --- a/tests/package_test.py +++ /dev/null @@ -1,1741 +0,0 @@ -# encoding: utf-8 -from __future__ import absolute_import - -import unittest -from copy import deepcopy - -import requests -from mock import patch, MagicMock, Mock -from requests import Response - -from rapyuta_io import DeviceStatus, DeploymentPhaseConstants -from rapyuta_io.clients.device import Device -from rapyuta_io.clients.native_network import NativeNetwork, Parameters -from rapyuta_io.clients.package import RestartPolicy, Runtime, ROSDistro, ExecutableMount -from rapyuta_io.clients.rosbag import ROSBagJob, ROSBagOptions, UploadOptions -from rapyuta_io.clients.routed_network import RoutedNetwork -from rapyuta_io.utils import InternalServerError -from rapyuta_io.utils.error import OperationNotAllowedError, PlanNotFound, \ - InvalidParameterException, ParameterMissingException, DuplicateAliasException, \ - ConflictError, BadRequestError, ResourceNotFoundError -from rapyuta_io.utils.rest_client import HttpMethod -from tests.utils.client import get_client, headers -from tests.utils.device_respones import DEVICE_INFO, CONFIG_VARIABLES, DOCKER_CONFIG_VARIABLES, \ - DOCKER_DEVICE, DOCKER_EMPTY_ROSBAG_CONFIG_VARIABLES, GET_DOCKERCOMPOSE_DEVICE_SUCCESS, \ - DOCKER_CONFIG_VARIABLE_WITH_ROSBAG_VARIABLES, PREINSTALLED_DEVICE_WITH_NEW_RUNTIME -from .utils.package_responses import PACKAGE_OK_VALIDATE, PACKAGE_OK_VALIDATE_DEVICE, \ - DEPLOYMENT_INFO, PACKAGE_OK_NO_VALIDATE, DEPLOYMENT_STATUS_RUNNING, DEPLOYMENT_STATUS_STOPPED, \ - PROVISION_OK, DEPLOYMENT_LIST, CREATE_PACKAGE, MANIFEST, PACKAGE_OK_VALIDATE_DEVICE_DOCKER, \ - PACKAGE_NOT_FOUND, PACKAGE_OK_NON_ROS_VALIDATE, PACKAGES_LIST, PACKAGE_OK_VALIDATE_ROSBAG_JOB, \ - DEPLOYMENT_INFO_ROSBAG_JOB, PACKAGE_OK_VALIDATE_DEVICE_ROSBAG_JOB, DEPLOYMENT_INFO_DEVICE_ROSBAG_JOB,\ - GET_VOLUME_INSTANCE_OK - -from .utils.scoped_targeted_responses import SCOPED_TARGETED_PACKAGE, CAN_BE_TARGETED, \ - SCOPED_TARGETABLE_DEPEPNDANT_DEPLOY, SCOPED_CLOUD_PACKAGE -from .utils.static_route_responses import STATIC_ROUTE_RESPONSE - - -class PackageTests(unittest.TestCase): - - @patch('requests.request') - def test_package_provision_on_device_ok(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - device.status = DeviceStatus.OFFLINE - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - ignore_device_config = ['ros_workspace'] - provision_config.add_device('ros-talker', device, ignore_device_config) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - deployment = pkg.provision('test_deployment_name', provision_config) - # this is not scoped or targeted - self.assertNotIn("bridge_params", - provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 5) - - @patch('requests.request') - def test_package_provision_on_device_with_set_comonent_alias_false_ok(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - ignore_device_config = ['ros_workspace'] - provision_config.add_device('ros-talker', device, ignore_device_config, set_component_alias=False) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'ros-talker') - deployment = pkg.provision('test_deployment_name', provision_config) - # this is not scoped or targeted - self.assertNotIn("bridge_params", - provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 5) - - @patch('requests.request') - def test_package_provision_with_rosbag_preinstalled_device(self, mock_request): - expected_err = 'ROSBag on Device does not support Preinstalled devices' - get_device = Mock() - get_device.status_code = requests.codes.OK - get_device.text = DEVICE_INFO - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - ignore_device_config = ['ros_workspace'] - provision_config.add_device('ros-talker', device, ignore_device_config) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - provision_config.add_rosbag_job('ros-talker', ROSBagJob('test-job', ROSBagOptions(all_topics=True))) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - with self.assertRaises(InvalidParameterException) as e: - pkg.provision('test_deployment_name', provision_config) - - self.assertEqual(str(e.exception), expected_err) - - @patch('requests.request') - def test_package_provision_with_rosbag_device_without_rosbag_mount_path(self, mock_request): - expected_err = 'This device does not have ROSBag components installed. Please re-onboard the device to use ROSBag features' - get_device = Mock() - get_device.text = DOCKER_DEVICE - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = DOCKER_CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - provision_config.add_rosbag_job('ros-talker', ROSBagJob('test-job', ROSBagOptions(all_topics=True))) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - with self.assertRaises(InvalidParameterException) as e: - pkg.provision('test_deployment_name', provision_config) - - self.assertEqual(str(e.exception), expected_err) - - @patch('requests.request') - def test_package_provision_with_rosbag_device_with_empty_rosbag_mount_path(self, mock_request): - expected_err = 'This device does not have ROSBag components installed. Please re-onboard the device to use ROSBag features' - get_device = Mock() - get_device.text = DOCKER_DEVICE - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = DOCKER_EMPTY_ROSBAG_CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - provision_config.add_rosbag_job('ros-talker', ROSBagJob('test-job', ROSBagOptions(all_topics=True))) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - with self.assertRaises(InvalidParameterException) as e: - pkg.provision('test_deployment_name', provision_config) - - self.assertEqual(str(e.exception), expected_err) - - @patch('requests.request') - def test_package_provision_on_cloud_ok(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE - get_package.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_package, provision, deployment_info] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_provision_on_cloud_with_rosbag_job_ok(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_ROSBAG_JOB - get_package.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO_ROSBAG_JOB - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_package, provision, deployment_info] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 3) - self.assertEqual( - deployment.provisionContext.component_context['gpzxcgjynhulepjjcyglgepl'].ros_bag_job_defs[0].name, 'rbag') - self.assertEqual( - deployment.provisionContext.component_context['gpzxcgjynhulepjjcyglgepl'].ros_bag_job_defs[ - 0].recordOptions.topics, ['/telemetry']) - - @patch('requests.request') - def test_package_provision_on_device_with_rosbag_job_ok(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_ROSBAG_JOB - get_package.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - get_device = Mock() - get_device.text = GET_DOCKERCOMPOSE_DEVICE_SUCCESS - get_device.status_code = requests.codes.OK - get_device_configs = Mock() - get_device_configs.text = DOCKER_CONFIG_VARIABLE_WITH_ROSBAG_VARIABLES - get_device_configs.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO_DEVICE_ROSBAG_JOB - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_device, get_device_configs, provision, deployment_info] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - device = client.get_device('test_device_id') - provision_config.add_device('ros-talker', device=device, ignore_device_config=['ros_workspace'], - set_component_alias=False) - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 5) - self.assertEqual( - deployment.provisionContext.component_context['gpzxcgjynhulepjjcyglgepl'].ros_bag_job_defs[0].name, 'rbag') - self.assertEqual( - deployment.provisionContext.component_context['gpzxcgjynhulepjjcyglgepl'].ros_bag_job_defs[ - 0].recordOptions.topics, ['/telemetry']) - self.assertEqual( - deployment.provisionContext.component_context['gpzxcgjynhulepjjcyglgepl'].ros_bag_job_defs[ - 0].uploadOptions.maxUploadRate, 1048576) - - @patch('requests.request') - def test_package_provision_plan_not_found_failure(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - with self.assertRaises(PlanNotFound): - pkg.get_provision_configuration('test-plan1') - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_provision_empty_ros_workspace_failure(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException): - provision_config.add_device('ros-talker', device) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_add_device_remove_ros_workspace_docker_device(self, rest_mock): - component_id = "jakmybngjupwdjjdqztmcrjq" - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package_response.status_code = requests.codes.OK - get_device_response = MagicMock() - get_device_response.text = DOCKER_DEVICE - get_device_response.status_code = requests.codes.OK - config_variable_response = MagicMock() - config_variable_response.text = DOCKER_CONFIG_VARIABLES - config_variable_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_device_response, get_package_response, config_variable_response] - client = get_client() - device = client.get_device('test_device_id') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device) - self.assertTrue('ros_workspace' not in provision_config.parameters.get(component_id), - 'ros_workspace should be present for docker runtime') - - @patch('requests.request') - def test_package_provision_empty_add_label_failure(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(ParameterMissingException): - provision_config.add_label('', '') - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_provision_empty_deployment_name(self, rest_mock): - mock_response = MagicMock() - mock_response.text = PACKAGE_OK_VALIDATE - mock_response.status_code = 200 - rest_mock.return_value = mock_response - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - pkg.provision('', provision_config) - self.assertEqual("deployment_name must be a non-empty string", str(e.exception)) - self.assertEqual(rest_mock.call_count, 1) - - @patch('requests.request') - def test_package_provision_incorrect_provision_configuration_type(self, rest_mock): - mock_response = MagicMock() - mock_response.text = PACKAGE_OK_VALIDATE - mock_response.status_code = 200 - rest_mock.return_value = mock_response - client = get_client() - pkg = client.get_package('my_package') - with self.assertRaises(InvalidParameterException) as e: - pkg.provision('dep1', '') - self.assertEqual("provision_configuration must be of type ProvisionConfiguration", str(e.exception)) - self.assertEqual(rest_mock.call_count, 1) - - @patch('requests.request') - def test_package_provision_component_not_mapped_with_device_failure(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = 200 - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(OperationNotAllowedError): - pkg.provision('test_deployment_name', provision_config) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_provision_api_error(self, mock_request): - response = Mock() - response.text = PACKAGE_OK_VALIDATE - response.status_code = 200 - response2 = response() - response2.status_code = 500 - mock_request.side_effect = [response, response2] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InternalServerError): - pkg.provision('test_package_name', provision_config) - self.assertEqual(mock_request.call_count, 2) - - @patch('requests.request') - def test_package_provision_component_parameter_empty_component_name_failure(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_NO_VALIDATE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException): - provision_config.add_parameter('', 'invalid-value', 123) - - @patch('requests.request') - def test_package_provision_component_parameter_empty_failure(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_NO_VALIDATE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - ignore_device_config = ['ros_workspace'] - provision_config.add_device('ros-talker', device, ignore_device_config) - with self.assertRaises(InvalidParameterException): - pkg.provision('test_deployment_name', provision_config) - self.assertEqual(mock_request.call_count, 3) - - def test_create_executable_mount_invalid_executable_name(self): - with self.assertRaises(InvalidParameterException) as e: - mount = ExecutableMount(1, '/mountPath', '/subPath') - self.assertEqual("exec_name must be a non-empty string", str(e.exception)) - - def test_create_executable_mount_invalid_mount_path(self): - with self.assertRaises(InvalidParameterException) as e: - mount = ExecutableMount('exec-name', 1, '/subPath') - self.assertEqual("mount_path must be a non-empty string", str(e.exception)) - - def test_create_executable_mount_invalid_sub_path(self): - with self.assertRaises(InvalidParameterException) as e: - mount = ExecutableMount('exec-name', '/mountPath', 1) - self.assertEqual("sub_path must be a non-empty string", str(e.exception)) - - @patch('requests.request') - def test_provision_config_add_mount_volume_both_mount_path_and_executable_mounts_none_error(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - get_volume_instance = Mock() - get_volume_instance.text = GET_VOLUME_INSTANCE_OK - get_volume_instance.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_volume_instance, get_volume_instance] - client = get_client() - pkg = client.get_package('my_package') - volume_instance = client.get_volume_instance('test-id') - prov_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', volume=volume_instance, mount_path=None, executable_mounts=None) - self.assertEqual("One of mount_path or executable_mounts should be present", str(e.exception)) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_provision_config_add_mount_volume_invalid_executable_mounts(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - get_volume_instance = Mock() - get_volume_instance.text = GET_VOLUME_INSTANCE_OK - get_volume_instance.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_volume_instance, get_volume_instance] - client = get_client() - pkg = client.get_package('my_package') - volume_instance = client.get_volume_instance('test-id') - prov_config = pkg.get_provision_configuration('test-plan') - executable_mounts = ["invalid mount"] - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', volume=volume_instance, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual("executable_mounts must be a list of rapyuta_io.clients.package.ExecutableMount", str(e.exception)) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_add_mount_volume_to_provision_config_ok(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - get_volume_instance = Mock() - get_volume_instance.text = GET_VOLUME_INSTANCE_OK - get_volume_instance.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_volume_instance, get_volume_instance] - client = get_client() - pkg = client.get_package('my_package') - volume_instance = client.get_volume_instance('test-id') - prov_config = pkg.get_provision_configuration('test-plan') - executable_mounts = [ExecutableMount(exec_name='ros-talker', mount_path='/test_path', sub_path='data')] - prov_config.mount_volume('ros-talker', volume=volume_instance, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual( - prov_config.context['dependentDeployments'][0]['config']['mountPaths']['ros-talker']['mountPath'], - '/test_path') - self.assertEqual( - prov_config.context['dependentDeployments'][0]['config']['mountPaths']['ros-talker']['subPath'], 'data') - - @patch('requests.request') - def test_package_add_volume_to_provision_config_both_present(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - prov_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', volume='random', device='random', mount_path=None, executable_mounts=None) - self.assertEqual("both volume and device parameter cannot be present", str(e.exception)) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_add_volume_to_provision_config_both_absent(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - prov_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', mount_path=None, executable_mounts=None) - self.assertEqual("either a volume or device parameter must be present", str(e.exception)) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_invalid_volume(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - prov_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', volume='invalid', mount_path=None, executable_mounts=None) - self.assertEqual("volume must be of type rapyuta_io.clients.persistent_volumes.VolumeInstance", str(e.exception)) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_invalid_device(self, mock_request): - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('my_package') - prov_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', device='invalid', mount_path=None, executable_mounts=None) - self.assertEqual("device must be of type Device", str(e.exception)) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_invalid_exec_mount(self, mock_request): - get_device = Mock() - get_device.text = DOCKER_DEVICE - get_device.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = DOCKER_CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_device, config_variable] - client = get_client() - pkg = client.get_package('my_package') - device = client.get_device('test-dev') - prov_config = pkg.get_provision_configuration('test-plan') - prov_config.add_device('ros-talker', device, ['ros_workspace']) - executable_mounts = ["invalid"] - with self.assertRaises(InvalidParameterException) as e: - prov_config.mount_volume('ros-talker', device=device, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual("executable_mounts must be a list of rapyuta_io.clients.package.ExecutableMount", str(e.exception)) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_preinstalled_runtime(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_device, config_variable] - client = get_client() - pkg = client.get_package('my_package') - device = client.get_device('test-dev') - prov_config = pkg.get_provision_configuration('test-plan') - prov_config.add_device('ros-talker', device, ['ros_workspace']) - executable_mounts = [ExecutableMount(exec_name='ros-talker', mount_path='/test_path', sub_path='/data')] - with self.assertRaises(OperationNotAllowedError) as e: - prov_config.mount_volume('ros-talker', device=device, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual("Device must be a dockercompose device", str(e.exception)) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_with_new_preinstalled_runtime(self, mock_request): - get_device = Mock() - get_device.text = PREINSTALLED_DEVICE_WITH_NEW_RUNTIME - get_device.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_device, config_variable] - client = get_client() - pkg = client.get_package('my_package') - device = client.get_device('test-dev') - prov_config = pkg.get_provision_configuration('test-plan') - prov_config.add_device('ros-talker', device, ['ros_workspace']) - executable_mounts = [ExecutableMount(exec_name='ros-talker', mount_path='/test_path', sub_path='/data')] - with self.assertRaises(OperationNotAllowedError) as e: - prov_config.mount_volume('ros-talker', device=device, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual("Device must be a dockercompose device", str(e.exception)) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_not_added_device(self, mock_request): - get_device = Mock() - get_device.text = DOCKER_DEVICE - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_device] - client = get_client() - pkg = client.get_package('my_package') - device = client.get_device('test-dev') - prov_config = pkg.get_provision_configuration('test-plan') - executable_mounts = [ExecutableMount(exec_name='ros-talker', mount_path='/test_path', sub_path='/data')] - with self.assertRaises(OperationNotAllowedError) as e: - prov_config.mount_volume('ros-talker', device=device, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual("Device must be added to the component", str(e.exception)) - self.assertEqual(mock_request.call_count, 2) - - @patch('requests.request') - def test_package_add_device_volume_to_provision_config_ok(self, mock_request): - get_device = Mock() - get_device.text = DOCKER_DEVICE - get_device.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = DOCKER_CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE_DOCKER - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package, get_device, config_variable] - client = get_client() - pkg = client.get_package('my_package') - device = client.get_device('test-dev') - device.status = DeviceStatus.OFFLINE.value - prov_config = pkg.get_provision_configuration('test-plan') - prov_config.add_device('ros-talker', device, ['ros_workspace']) - executable_mounts = [ExecutableMount(exec_name='ros-talker', mount_path='/test_path', sub_path='/data')] - prov_config.mount_volume('ros-talker', device=device, mount_path=None, executable_mounts=executable_mounts) - self.assertEqual( - prov_config.context['diskMountInfo'][0]['diskResourceId'], - device.deviceId) - self.assertEqual( - prov_config.context['diskMountInfo'][0]['config']['mountPaths']['ros-talker']['mountPath'], - '/test_path') - self.assertEqual( - prov_config.context['diskMountInfo'][0]['config']['mountPaths']['ros-talker']['subPath'], '/data') - - @patch('requests.request') - def test_package_provision_dependent_deployment_ok(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - deployment_status = Mock() - deployment_status.text = DEPLOYMENT_STATUS_RUNNING - deployment_status.status_code = requests.codes.OK - provision = Mock() - provision.text = '''{"operation": "deployment_id"}''' - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = 200 - mock_request.side_effect = [get_device, get_package, config_variable, deployment_status, - deployment_status, provision, deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device, ['ros_workspace']) - dep_deployment = client.get_deployment('deployment_id') - provision_config.add_dependent_deployment(dep_deployment) - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 7) - self.assertFalse(deployment.is_partial) - - @patch('requests.request') - def test_package_provision_dependent_deployment_with_partial_package_and_deployment_ok(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - all_packages = Mock() - all_packages.text = PACKAGES_LIST - all_packages.status_code = requests.codes.OK - get_package1 = Mock() - get_package1.text = PACKAGE_OK_VALIDATE_DEVICE - get_package1.status_code = requests.codes.OK - get_package2 = Mock() - get_package2.text = PACKAGE_OK_VALIDATE_DEVICE - get_package2.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - all_deployments = Mock() - all_deployments.text = DEPLOYMENT_LIST - all_deployments.status_code = requests.codes.OK - deployment_status = Mock() - deployment_status.text = DEPLOYMENT_STATUS_RUNNING - deployment_status.status_code = requests.codes.OK - provision = Mock() - provision.text = '''{"operation": "deployment_id"}''' - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = 200 - mock_request.side_effect = [get_device, all_packages, get_package1, config_variable, all_deployments, - deployment_status, get_package2, provision, deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkgs = client.get_all_packages() - partial_pkg = deepcopy(pkgs[0]) - self.assertTrue(pkgs[0].is_partial) - provision_config = pkgs[0].get_provision_configuration('test-plan') - self.assertFalse(pkgs[0].is_partial) - provision_config.add_device('ros-talker', device, ['ros_workspace']) - deployments = client.get_all_deployments() - self.assertTrue(deployments[0].is_partial) - provision_config.add_dependent_deployment(deployments[0]) - self.assertTrue(partial_pkg.is_partial) - deployment = partial_pkg.provision('test_deployment_name', provision_config) - self.assertFalse(partial_pkg.is_partial) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 9) - self.assertFalse(deployments[0].is_partial) - - @patch('requests.request') - def test_provision_dependent_deployment_failure(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - deployment_status = Mock() - deployment_status.text = DEPLOYMENT_STATUS_STOPPED - deployment_status.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, - deployment_status, deployment_status] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device, ['ros_workspace']) - dep_deployment = client.get_deployment('deployment_id') - with self.assertRaises(OperationNotAllowedError): - provision_config.add_dependent_deployment(dep_deployment) - self.assertEqual(mock_request.call_count, 5) - - @patch('requests.request') - def test_package_provision_with_same_provision_configuration_failed(self, rest_mock): - get_device_response = MagicMock() - get_device_response.text = DEVICE_INFO - get_device_response.status_code = requests.codes.OK - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE_DEVICE - get_package_response.status_code = requests.codes.OK - config_variable_response = MagicMock() - config_variable_response.text = CONFIG_VARIABLES - config_variable_response.status_code = requests.codes.OK - provision_response = MagicMock() - provision_response.text = '''{"operation": "deployment_id"}''' - provision_response.status_code = requests.codes.OK - deployment_info = MagicMock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = 200 - rest_mock.side_effect = [get_device_response, get_package_response, - config_variable_response, provision_response, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device, ['ros_workspace']) - deployment = pkg.provision('test_deployment_name-1', provision_config) - self.assertTrue(deployment.deploymentId) - with self.assertRaises(InvalidParameterException) as e: - pkg.provision('test_deployment_name-2', provision_config) - self.assertEqual("cannot reuse this ProvisionConfiguration for provisioning", str(e.exception)) - self.assertEqual(rest_mock.call_count, 5) - - @patch('requests.request') - def test_scoped_package_provision_defaulting(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = SCOPED_TARGETED_PACKAGE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - ignore_device_config = ['ros_workspace'] - provision_config.add_device('devicepong', device, ignore_device_config) - self.assertEqual(provision_config.parameters["dev-comp"]["bridge_params"]["alias"], 'D239-Device') - self.assertEqual(provision_config.parameters["cloud-comp"]["bridge_params"]["alias"], 'cloudping') - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 5) - - @patch('requests.request') - def test_scoped_package_provision_clash(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = SCOPED_TARGETED_PACKAGE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, provision, - deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - ignore_device_config = ['ros_workspace'] - provision_config.add_device('devicepong', device, ignore_device_config) - provision_config.validate() - provision_config.set_component_alias("cloudping", "samename") - provision_config.set_component_alias("devicepong", "samename") - with self.assertRaises(DuplicateAliasException): - pkg.provision('test_deployment_name', provision_config) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_scoped_package_provision_empty_component_name(self, mock_request): - expected_err = "component_name must be a non-empty string" - get_package = MagicMock() - get_package.text = SCOPED_CLOUD_PACKAGE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - provision_config.validate() - with self.assertRaises(InvalidParameterException) as e: - provision_config.set_component_alias("", "samename") - self.assertEqual(str(e.exception), expected_err) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_scoped_package_provision_invalid_component_name(self, mock_request): - expected_err = "component_name must be a non-empty string" - get_package = MagicMock() - get_package.text = SCOPED_CLOUD_PACKAGE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - provision_config.validate() - with self.assertRaises(InvalidParameterException) as e: - provision_config.set_component_alias(True, "samename") - self.assertEqual(str(e.exception), expected_err) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_scoped_package_provision_invalid_alias(self, mock_request): - expected_err = "alias must be a string" - get_package = MagicMock() - get_package.text = SCOPED_CLOUD_PACKAGE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - provision_config.validate() - with self.assertRaises(InvalidParameterException) as e: - provision_config.set_component_alias("cloudping", True) - self.assertEqual(str(e.exception), expected_err) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_scoped_package_provision_invalid_ros_namespace_flag(self, mock_request): - expected_err = "set_ros_namespace must be a boolean" - get_package = MagicMock() - get_package.text = SCOPED_CLOUD_PACKAGE - get_package.status_code = requests.codes.OK - mock_request.side_effect = [get_package] - client = get_client() - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - provision_config.validate() - with self.assertRaises(InvalidParameterException) as e: - provision_config.set_component_alias("cloudping", "alias", "true") - self.assertEqual(str(e.exception), expected_err) - self.assertEqual(mock_request.call_count, 1) - - @patch('requests.request') - def test_scoped_package_provision_on_cloud_ok(self, mock_request): - get_package = Mock() - get_package.text = SCOPED_CLOUD_PACKAGE - get_package.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = SCOPED_TARGETABLE_DEPEPNDANT_DEPLOY - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_package, provision, deployment_info] - client = get_client() - pkg = client.get_package('C2DPingMaxST2') - provision_config = pkg.get_provision_configuration('basicplan') - provision_config.validate() - provision_config.set_component_alias("cloudping", "parent", True) - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(deployment.parameters.sszyzwycqdgsnmgezoyaqydy.bridge_params.alias, "parent") - self.assertEqual(deployment.parameters.sszyzwycqdgsnmgezoyaqydy.bridge_params.setROSNamespace, True) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_canbetargeted_ok(self, mock_request): - get_package = Mock() - get_package.text = CAN_BE_TARGETED - get_package.status_code = requests.codes.OK - provision = Mock() - provision.text = PROVISION_OK - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = DEPLOYMENT_INFO - deployment_info.status_code = requests.codes.OK - mock_request.side_effect = [get_package, provision, deployment_info] - client = get_client() - pkg = client.get_package('canbetargeted') - provision_config = pkg.get_provision_configuration('basicplan') - self.assertEqual(provision_config.parameters["compid"]["bridge_params"]["alias"], 'comp') - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 3) - - @patch('requests.request') - def test_package_provision_normal_package_dependent_deployment_with_scoped_and_targeted(self, mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - deployment_status = Mock() - deployment_status.text = SCOPED_TARGETABLE_DEPEPNDANT_DEPLOY - deployment_status.status_code = requests.codes.OK - provision = Mock() - provision.text = '''{"operation": "deployment_id"}''' - provision.status_code = requests.codes.OK - deployment_info = Mock() - deployment_info.text = SCOPED_TARGETABLE_DEPEPNDANT_DEPLOY - deployment_info.status_code = 200 - mock_request.side_effect = [get_device, get_package, config_variable, - deployment_status, deployment_status, provision, deployment_info] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device, ['ros_workspace']) - dep_deployment = client.get_deployment('deployment_id') - provision_config.add_dependent_deployment(dep_deployment) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - deployment = pkg.provision('test_deployment_name', provision_config) - self.assertTrue(deployment.deploymentId) - self.assertEqual(mock_request.call_count, 7) - - @patch('requests.request') - def test_package_provision_normal_package_dependent_deployment_with_scoped_and_targeted_alias_clash(self, - mock_request): - get_device = Mock() - get_device.text = DEVICE_INFO - get_device.status_code = requests.codes.OK - get_package = Mock() - get_package.text = PACKAGE_OK_VALIDATE_DEVICE - get_package.status_code = requests.codes.OK - config_variable = Mock() - config_variable.text = CONFIG_VARIABLES - config_variable.status_code = requests.codes.OK - dep_deployment_info = Mock() - dep_deployment_info.text = SCOPED_TARGETABLE_DEPEPNDANT_DEPLOY - dep_deployment_info.status_code = requests.codes.OK - deployment_status = Mock() - deployment_status.text = SCOPED_TARGETABLE_DEPEPNDANT_DEPLOY - deployment_status.status_code = requests.codes.OK - provision = Mock() - provision.text = '''{"operation": "deployment_id"}''' - provision.status_code = requests.codes.OK - mock_request.side_effect = [get_device, get_package, config_variable, dep_deployment_info, - deployment_status, provision] - client = get_client() - device = client.get_device('test_device_id') - self.assertIsInstance(device, Device, 'Object should be an instance of class Device') - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_device('ros-talker', device, ['ros_workspace']) - dep_deployment = client.get_deployment('deployment_id') - provision_config.add_dependent_deployment(dep_deployment) - self.assertEqual(provision_config.parameters["jakmybngjupwdjjdqztmcrjq"]["bridge_params"]["alias"], - 'D239-Device') - provision_config.set_component_alias("ros-talker", "parent") - with self.assertRaises(DuplicateAliasException): - pkg.provision('test_deployment_name', provision_config) - self.assertEqual(mock_request.call_count, 5) - - @patch('rapyuta_io.clients.provision_client.ProvisionClient._execute_api') - @patch('requests.request') - def test_package_deployments_without_phase(self, mock_rest_client, mock_execute): - mock_get_package = Mock() - mock_get_package.text = SCOPED_TARGETED_PACKAGE - mock_get_package.status_code = requests.codes.OK - mock_rest_client.side_effect = [mock_get_package] - mock_execute.return_value = MagicMock() - mock_execute.return_value.status_code = 200 - mock_execute.return_value.text = DEPLOYMENT_LIST - client = get_client() - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - package = client.get_package('pkg-abcdefg') - deployments = package.deployments() - mock_execute.assert_called_once_with(expected_url, HttpMethod.GET, - query_params={'package_uid': 'pkg-abcdefg'}, retry_limit=0) - for deployment in deployments: - self.assertTrue(deployment.is_partial) - - @patch('rapyuta_io.clients.provision_client.ProvisionClient._execute_api') - @patch('requests.request') - def test_package_deployments_with_phase(self, mock_rest_client, mock_execute): - get_package = Mock() - get_package.text = SCOPED_TARGETED_PACKAGE - get_package.status_code = requests.codes.OK - mock_rest_client.side_effect = [get_package] - mock_execute.return_value = MagicMock() - mock_execute.return_value.status_code = 200 - mock_execute.return_value.text = DEPLOYMENT_LIST - client = get_client() - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - package = client.get_package('pkg-abcdefg') - deployments = package.deployments(phases=[DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING]) - mock_execute.assert_called_once_with(expected_url, HttpMethod.GET, - query_params={'package_uid': 'pkg-abcdefg', 'phase': - [DeploymentPhaseConstants.SUCCEEDED.value, - DeploymentPhaseConstants.PROVISIONING.value]}, - retry_limit=0) - for deployment in deployments: - self.assertTrue(deployment.is_partial) - - @patch('rapyuta_io.clients.provision_client.ProvisionClient._execute_api') - @patch('requests.request') - def test_package_deployments_with_phase_and_retry(self, mock_rest_client, mock_execute): - get_package = Mock() - get_package.text = SCOPED_TARGETED_PACKAGE - get_package.status_code = requests.codes.OK - mock_rest_client.side_effect = [get_package] - mock_execute.return_value = MagicMock() - mock_execute.return_value.status_code = 200 - mock_execute.return_value.text = DEPLOYMENT_LIST - client = get_client() - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/deployment/list' - package = client.get_package('pkg-abcdefg') - package.deployments(phases=[DeploymentPhaseConstants.SUCCEEDED, DeploymentPhaseConstants.PROVISIONING], - retry_limit=2) - mock_execute.assert_called_once_with(expected_url, HttpMethod.GET, - query_params={'package_uid': 'pkg-abcdefg', 'phase': - [DeploymentPhaseConstants.SUCCEEDED.value, - DeploymentPhaseConstants.PROVISIONING.value]}, - retry_limit=2) - - @patch('requests.request') - def test_create_package(self, mock_request): - mock_response = MagicMock(spec=Response) - mock_response.text = CREATE_PACKAGE - mock_response.status_code = requests.codes.OK - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/add' - mock_request.return_value = mock_response - get_client().create_package(MANIFEST) - mock_request.assert_called_once_with(headers=headers, - json=MANIFEST, - method='POST', - url=expected_url, - params=None) - - @patch('requests.request') - def test_create_package_package_with_same_exists(self, mock_request): - mock_response = MagicMock(spec=Response) - mock_response.text = CREATE_PACKAGE - mock_response.status_code = requests.codes.CONFLICT - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/add' - mock_request.return_value = mock_response - with self.assertRaises(ConflictError): - get_client().create_package(MANIFEST) - mock_request.assert_called_once_with(headers=headers, - json=MANIFEST, - method='POST', - url=expected_url, - params=None) - - @patch('requests.request') - def test_create_package_package_with_bad_request(self, mock_request): - mock_response = MagicMock(spec=Response) - mock_response.text = CREATE_PACKAGE - mock_response.status_code = requests.codes.BAD_REQUEST - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/add' - mock_request.return_value = mock_response - with self.assertRaises(BadRequestError): - get_client().create_package(MANIFEST) - mock_request.assert_called_once_with(headers=headers, - json=MANIFEST, - method='POST', - url=expected_url, - params=None) - - @patch('rapyuta_io.rio_client.Client._get_manifest_from_file') - @patch('requests.request') - def test_create_package_from_manifest(self, mock_request, mock_get_manifest_path): - mock_response = MagicMock() - mock_response.text = CREATE_PACKAGE - mock_response.status_code = requests.codes.OK - mock_request.return_value = mock_response - mock_get_manifest_path.return_value = MANIFEST - manifest_filepath = '/path/to/listener.json' - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/add' - get_client().create_package_from_manifest(manifest_filepath) - mock_request.assert_called_once_with(headers=headers, - json=MANIFEST, - method='POST', - url=expected_url, - params=None) - mock_get_manifest_path.assert_called_once_with(manifest_filepath) - - @patch('rapyuta_io.rio_client.Client._get_manifest_from_file') - @patch('requests.request') - def test_create_package_from_manifest_package_with_same_exists(self, mock_request, - mock_get_manifest_path): - mock_response = MagicMock() - mock_response.text = CREATE_PACKAGE - mock_response.status_code = requests.codes.CONFLICT - mock_request.return_value = mock_response - mock_get_manifest_path.return_value = MANIFEST - manifest_filepath = '/path/to/listener.json' - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/add' - with self.assertRaises(ConflictError): - get_client().create_package_from_manifest(manifest_filepath) - mock_request.assert_called_once_with(headers=headers, - json=MANIFEST, - method='POST', - url=expected_url, - params=None) - mock_get_manifest_path.assert_called_once_with(manifest_filepath) - - @patch('rapyuta_io.rio_client.Client._get_manifest_from_file') - @patch('requests.request') - def test_create_package_from_manifest_package_with_bad_request(self, mock_request, - mock_get_manifest_path): - mock_response = MagicMock() - mock_response.text = CREATE_PACKAGE - mock_response.status_code = requests.codes.BAD_REQUEST - mock_request.return_value = mock_response - mock_get_manifest_path.return_value = MANIFEST - manifest_filepath = '/path/to/listener.json' - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/add' - with self.assertRaises(BadRequestError): - get_client().create_package_from_manifest(manifest_filepath) - mock_request.assert_called_once_with(headers=headers, - json=MANIFEST, - method='POST', - url=expected_url, - params=None) - mock_get_manifest_path.assert_called_once_with(manifest_filepath) - - @patch('requests.request') - def test_add_rosbag_job_success(self, rest_mock): - component_id = 'jakmybngjupwdjjdqztmcrjq' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - rosbag_options = ROSBagOptions(all_topics=True) - job = ROSBagJob(name='job', rosbag_options=rosbag_options) - provision_config.add_rosbag_job('ros-talker', job) - self.assertEqual(provision_config['context']['component_context'][component_id] - ['ros_bag_job_defs'][0]['name'], job.name) - self.assertEqual(provision_config['context']['component_context'][component_id] - ['ros_bag_job_defs'][0]['recordOptions'], rosbag_options.serialize()) - - @patch('requests.request') - def test_add_rosbag_job_invalid_rosbag_job(self, rest_mock): - expected_err = 'rosbag_job needs to a ROSBagJob object' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - provision_config.add_rosbag_job('ros-talker', 'invalid') - - self.assertEqual(str(e.exception), expected_err) - - @patch('requests.request') - def test_add_rosbag_job_to_device_component(self, rest_mock): - component_id = 'jakmybngjupwdjjdqztmcrjq' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE_DEVICE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - job = ROSBagJob(name='job', rosbag_options=ROSBagOptions(all_topics=True), - upload_options=UploadOptions()) - provision_config.add_rosbag_job('ros-talker', job) - self.assertEqual(provision_config['context']['component_context'][component_id] - ['ros_bag_job_defs'][0]['name'], job.name) - self.assertEqual(provision_config['context']['component_context'][component_id] - ['ros_bag_job_defs'][0]['recordOptions'], job.rosbag_options.serialize()) - self.assertEqual(provision_config['context']['component_context'][component_id] - ['ros_bag_job_defs'][0]['uploadOptions'], job.upload_options.serialize()) - - @patch('requests.request') - def test_add_rosbag_jobs_with_same_name(self, rest_mock): - expected_err = 'rosbag job with same name already exists' - component_id = 'jakmybngjupwdjjdqztmcrjq' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE_DEVICE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - job1 = ROSBagJob(name='job', rosbag_options=ROSBagOptions(all_topics=True), - upload_options=UploadOptions()) - provision_config.add_rosbag_job('ros-talker', job1) - job2 = ROSBagJob(name='job', rosbag_options=ROSBagOptions(all_topics=True), - upload_options=UploadOptions()) - with self.assertRaises(OperationNotAllowedError) as e: - provision_config.add_rosbag_job('ros-talker', job2) - self.assertEqual(str(e.exception), expected_err) - - @patch('requests.request') - def test_add_rosbag_job_to_non_ros_component(self, rest_mock): - expected_err = 'rosbag job is only supported for ros components' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_NON_ROS_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - job = ROSBagJob(name='job', rosbag_options=ROSBagOptions(all_topics=True)) - with self.assertRaises(OperationNotAllowedError) as e: - provision_config.add_rosbag_job('ros-talker', job) - - self.assertEqual(str(e.exception), expected_err) - - @patch('requests.request') - def test_remove_rosbag_job_from_device_component(self, rest_mock): - component_id = 'jakmybngjupwdjjdqztmcrjq' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE_ROSBAG_JOB - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - self.assertEqual(provision_config.plan.components.components[0].rosBagJobDefs[0].name, 'rbag') - provision_config.remove_rosbag_job('ros-talker', 'rbag') - self.assertEqual(len(provision_config['context']['component_context'][component_id]['rosBagJobDefs']), 0) - - @patch('requests.request') - def test_add_restart_policy_success(self, rest_mock): - component_id = 'jakmybngjupwdjjdqztmcrjq' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_NO_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_restart_policy('ros-talker', RestartPolicy.Always) - self.assertEqual(provision_config['context']['component_context'][component_id] - ['component_override']['restart_policy'], - RestartPolicy.Always.value) - - @patch('requests.request') - def test_add_routed_networks_to_provision_config_invalid_routed_network(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = str('invalid object') - - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - provision_config.add_routed_networks([routed_network]) - self.assertEqual(str(e.exception), 'routed networks must be of type RoutedNetwork') - - @patch('requests.request') - def test_add_routed_network_invalid_routed_network(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - provision_config.add_routed_network('invalid', 'lo') - self.assertEqual(str(e.exception), - 'routed networks must be of type RoutedNetwork') - - @patch('requests.request') - def test_add_routed_network_cloud_runtime_with_network_interface(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'cloud'}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(OperationNotAllowedError) as e: - provision_config.add_routed_network(routed_network, 'docker0') - self.assertEqual(str(e.exception), - 'cloud routed network does not bind to network interface') - - @patch('requests.request') - def test_add_routed_network_to_provision_config_device_runtime(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'device', - 'parameters': {'NETWORK_INTERFACE': 'lo'}}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_routed_network(routed_network, 'docker0') - self.assertEqual(provision_config['context']['routedNetworks'], - [{'bindParameters': {'NETWORK_INTERFACE': 'docker0'}, 'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_routed_network_to_provision_config_device(self, rest_mock): - ''' - adding network interface to device routed network which is already present - ''' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'device', - 'parameters': {'NETWORK_INTERFACE': 'lo'}}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config['context']['routedNetworks'] = [{'guid': 'test-network-id'}] - self.assertEqual(provision_config['context']['routedNetworks'], - [{'guid': 'test-network-id'}]) - provision_config.add_routed_network(routed_network, 'docker0') - self.assertEqual(provision_config['context']['routedNetworks'], - [{'bindParameters': {'NETWORK_INTERFACE': 'docker0'}, 'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_routed_network_to_provision_config_cloud(self, rest_mock): - ''' - adding network interface to cloud routed network which is already added - ''' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'device'}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config['context']['routedNetworks'] = [{'guid': 'test-network-id'}] - self.assertEqual(provision_config['context']['routedNetworks'], - [{'guid': 'test-network-id'}]) - provision_config.add_routed_network(routed_network) - self.assertEqual(provision_config['context']['routedNetworks'], - [{'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_routed_network_to_provision_config_device_runtime_with_diff_interface(self, rest_mock): - ''' - The network interface value gets updated each time you call the function - ''' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'device', - 'parameters': {'NETWORK_INTERFACE': 'lo'}}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_routed_network(routed_network, 'docker0') - self.assertEqual(provision_config['context']['routedNetworks'], - [{'bindParameters': {'NETWORK_INTERFACE': 'docker0'}, 'guid': 'test-network-id'}]) - provision_config.add_routed_network(routed_network, 'lo') - self.assertEqual(provision_config['context']['routedNetworks'], - [{'bindParameters': {'NETWORK_INTERFACE': 'lo'}, 'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_routed_network_to_provision_config_cloud_runtime(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'cloud'}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_routed_network(routed_network) - self.assertEqual(provision_config['context']['routedNetworks'], - [{'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_routed_networks_to_provision_config(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - routed_network = RoutedNetwork({'guid': 'test-network-id', 'runtime': 'cloud'}) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_routed_networks([routed_network]) - self.assertEqual(provision_config['context']['routedNetworks'], [{'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_native_networks_invalid_native_network(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - native_network = str('invalid object') - - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - provision_config.add_native_networks([native_network]) - self.assertEqual(str(e.exception), 'native network must be of type NativeNetwork') - - @patch('requests.request') - def test_add_native_network_invalid_native_network(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException) as e: - provision_config.add_native_network('invalid native_network','lo') - self.assertEqual(str(e.exception), - 'native network must be of type NativeNetwork') - - @patch('requests.request') - def test_add_native_network_cloud_runtime_with_network_interface(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - native_network = NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.KINETIC) - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(OperationNotAllowedError) as e: - provision_config.add_native_network(native_network, 'docker0') - self.assertEqual(str(e.exception), - 'cloud native network does not bind to network interface') - - @patch('requests.request') - def test_add_native_networks_to_provision_config_cloud_success(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - native_network = NativeNetwork('native_network_name', Runtime.CLOUD, ROSDistro.KINETIC) - native_network.guid = 'test-network-id' - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_native_networks([native_network]) - self.assertEqual(provision_config['context']['nativeNetworks'], [{'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_native_networks_to_provision_config_device_success(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - device = Device('dev-name') - device.uuid = 'random' - device.ip_interfaces = {'lo': '0.0.0.0'} - parameters = Parameters(device=device, network_interface='lo') - native_network = NativeNetwork('native_network_name', Runtime.DEVICE, ROSDistro.KINETIC, parameters) - native_network.guid = 'test-network-id' - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_native_network(native_network, 'lo') - self.assertEqual(provision_config['context']['nativeNetworks'], - [{'bindParameters': {'NETWORK_INTERFACE': 'lo'}, 'guid': 'test-network-id'}]) - provision_config.add_native_network(native_network, 'docker0') - self.assertEqual(provision_config['context']['nativeNetworks'], - [{'bindParameters': {'NETWORK_INTERFACE': 'docker0'}, 'guid': 'test-network-id'}]) - - @patch('requests.request') - def test_add_static_route(self, rest_mock): - endpoint_name = 'test' - static_route_name = 'test_route' - component_id = 'jakmybngjupwdjjdqztmcrjq' - component_name = 'ros-talker' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_NO_VALIDATE - get_package_response.status_code = requests.codes.OK - static_route_response = MagicMock() - static_route_response.text = STATIC_ROUTE_RESPONSE - static_route_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response, static_route_response] - client = get_client() - pkg = client.get_package('my_package') - static_route = client.create_static_route(static_route_name) - provision_config = pkg.get_provision_configuration('test-plan') - provision_config.add_static_route(component_name, endpoint_name, static_route) - self.assertEqual( - provision_config['context']['component_context'][component_id]['static_route_config'][endpoint_name], - static_route.guid) - - @patch('requests.request') - def test_add_static_route_with_type_error(self, rest_mock): - endpoint_name = 'test' - component_name = 'ros-talker' - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_NO_VALIDATE - get_package_response.status_code = requests.codes.OK - static_route_response = MagicMock() - static_route_response.text = STATIC_ROUTE_RESPONSE - static_route_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response, static_route_response] - client = get_client() - pkg = client.get_package('my_package') - static_route = {'key': 'not a static route'} - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(TypeError): - provision_config.add_static_route(component_name, endpoint_name, static_route) - - @patch('requests.request') - def test_get_static_route_by_name_name_not_found(self, rest_mock): - static_route_response = MagicMock() - static_route_response.text = None - static_route_response.status_code = requests.codes.NOT_FOUND - rest_mock.side_effect = [static_route_response] - client = get_client() - sr = client.get_static_route_by_name('test') - self.assertIsNone(sr) - - @patch('requests.request') - def test_delete_static_route_failure_invalid_route_guid(self, rest_mock): - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/staticroute/delete' - expected_payload = {'guid': 'invalid-route-guid'} - static_route_response = MagicMock() - static_route_response.text = '{"success":false,"error":"unable to find the resource from db: record not found"}' - static_route_response.status_code = requests.codes.NOT_FOUND - rest_mock.side_effect = [static_route_response] - client = get_client() - expected_err_msg = 'unable to find the resource from db: record not found' - with self.assertRaises(ResourceNotFoundError) as e: - client.delete_static_route('invalid-route-guid') - rest_mock.assert_called_once_with(headers=headers, json=expected_payload, url=expected_url, method='DELETE', - params={}) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_delete_static_route_success_valid_route_guid(self, rest_mock): - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/staticroute/delete' - expected_payload = {'guid': 'valid-route-guid'} - static_route_response = MagicMock() - static_route_response.text = '{"success":true,"error":""}' - static_route_response.status_code = requests.codes.OK - rest_mock.side_effect = [static_route_response] - client = get_client() - client.delete_static_route('valid-route-guid') - rest_mock.assert_called_once_with(headers=headers, json=expected_payload, url=expected_url, method='DELETE', - params={}) - - @patch('requests.request') - def test_add_restart_policy_invalid_policy(self, rest_mock): - get_package_response = MagicMock() - get_package_response.text = PACKAGE_OK_NO_VALIDATE - get_package_response.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response] - client = get_client() - pkg = client.get_package('my_package') - provision_config = pkg.get_provision_configuration('test-plan') - with self.assertRaises(InvalidParameterException): - provision_config.add_restart_policy('ros-talker', 'forever') - - @patch('requests.request') - def test_delete_package_success(self, rest_mock): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/delete?package_uid={}'.format('pkg-guid') - mock_delete_package = Mock() - mock_delete_package.text = 'null' - mock_delete_package.status_code = requests.codes.OK - rest_mock.side_effect = [mock_delete_package] - client = get_client() - client.delete_package('pkg-guid') - rest_mock.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='DELETE', - params=None) - - @patch('requests.request') - def test_delete_package_with_package_object_success(self, rest_mock): - get_package_response = MagicMock(spec=Response) - get_package_response.text = PACKAGE_OK_VALIDATE - get_package_response.status_code = requests.codes.OK - mock_delete_package = Mock() - mock_delete_package.text = 'null' - mock_delete_package.status_code = requests.codes.OK - rest_mock.side_effect = [get_package_response, mock_delete_package] - client = get_client() - pkg = client.get_package('pkg-guid') - pkg.delete() - - @patch('requests.request') - def test_delete_package_invalid_package_id(self, rest_mock): - mock_delete_package = Mock() - mock_delete_package.status_code = requests.codes.UNPROCESSABLE_ENTITY - rest_mock.side_effect = [mock_delete_package] - client = get_client() - expected_err_msg = 'package_id must be a non-empty string' - with self.assertRaises(InvalidParameterException) as e: - client.delete_package(123) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_delete_package_package_not_found(self, rest_mock): - mock_delete_package = Mock() - mock_delete_package.text = PACKAGE_NOT_FOUND - mock_delete_package.status_code = requests.codes.NOT_FOUND - rest_mock.side_effect = [mock_delete_package] - client = get_client() - with self.assertRaises(ResourceNotFoundError) as e: - client.delete_package('pkg-guid') - expected_err_msg = 'Package not found' - self.assertEqual(str(e.exception), expected_err_msg) diff --git a/tests/persistent_volume_test.py b/tests/persistent_volume_test.py deleted file mode 100644 index 37dccbb1..00000000 --- a/tests/persistent_volume_test.py +++ /dev/null @@ -1,147 +0,0 @@ -from __future__ import absolute_import -import requests -import unittest - -from mock import patch, MagicMock, Mock, call, ANY -from requests import Response - -from rapyuta_io import DeploymentPhaseConstants, DiskType -from rapyuta_io.clients.persistent_volumes import DiskCapacity -from rapyuta_io.utils import InvalidParameterException -from rapyuta_io.utils.rest_client import HttpMethod -from tests.utils.client import get_client, headers -from tests.utils.package_responses import DEPLOYMENT_LIST -from tests.utils.persistent_volume_responses import GET_PACKAGE_SUCCESS, PROVISION_CLIENT_SUCCESS, \ - GET_VOLUME_INSTANCE_SUCCESS, GET_DISK_SUCCESS, GET_DISK_LIST_SUCCESS -from tests.utils.scoped_targeted_responses import SCOPED_TARGETED_PACKAGE - -PERSISTENT_VOLUME = 'io-public-persistent-volume' - - -class PersistentVolumeTest(unittest.TestCase): - - @patch('requests.request') - def test_get_volume_deployments_without_phase(self, mock_request): - expected_url_get_package = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/status?' \ - 'package_uid=io-public-persistent-volume' - expected_url_list_disk = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/disk' - expected_get_volume_instance = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceinstance/dep-lblpkngqrhdrefhvnvulaioq' - mock_get_package = Mock() - mock_get_package.text = GET_PACKAGE_SUCCESS - mock_get_package.status_code = requests.codes.OK - - mock_list_disk = Mock() - mock_list_disk.status_code = 200 - mock_list_disk.text = GET_DISK_LIST_SUCCESS - mock_get_volume_instance = Mock() - mock_get_volume_instance.status_code = 200 - mock_get_volume_instance.text = GET_VOLUME_INSTANCE_SUCCESS - mock_request.side_effect = [mock_get_package, mock_list_disk, mock_get_volume_instance] - client = get_client() - - volume = client.get_persistent_volume() - volume_instances = volume.get_all_volume_instances() - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url_get_package, method='GET', params=None), - call(headers=headers, json=None, url=expected_url_list_disk, method='GET', params={}), - call(headers=headers, json=None, url=expected_get_volume_instance, method='GET', params=None), - ]) - for vol in volume_instances: - self.assertTrue(vol.is_partial) - self.assertEqual(vol['name'], 'test-volume') - self.assertEqual(vol['parameters']['io-pv']['capacity'], 32) - self.assertEqual(vol['parameters']['io-pv']['diskType'], DiskType.SSD.value) - - @patch('requests.request') - def test_get_volume_deployments_with_phase(self, mock_request): - expected_url_get_package = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/status?' \ - 'package_uid=io-public-persistent-volume' - expected_url_list_disk = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/disk' - expected_get_volume_instance = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceinstance/dep-lblpkngqrhdrefhvnvulaioq' - mock_get_package = Mock() - mock_get_package.text = GET_PACKAGE_SUCCESS - mock_get_package.status_code = requests.codes.OK - - mock_list_disk = Mock() - mock_list_disk.status_code = 200 - mock_list_disk.text = GET_DISK_LIST_SUCCESS - mock_get_volume_instance = Mock() - mock_get_volume_instance.status_code = 200 - mock_get_volume_instance.text = GET_VOLUME_INSTANCE_SUCCESS - mock_request.side_effect = [mock_get_package, mock_list_disk, mock_get_volume_instance] - client = get_client() - - volume = client.get_persistent_volume() - volume_instances = volume.get_all_volume_instances(phases=['In Progress']) - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url_get_package, method='GET', params=None), - call(headers=headers, json=None, url=expected_url_list_disk, method='GET', params={}), - call(headers=headers, json=None, url=expected_get_volume_instance, method='GET', params=None), - ]) - for vol in volume_instances: - self.assertTrue(vol.is_partial) - self.assertEqual(vol['name'], 'test-volume') - self.assertEqual(vol['parameters']['io-pv']['capacity'], 32) - self.assertEqual(vol['parameters']['io-pv']['diskType'], DiskType.SSD.value) - - @patch('requests.request') - def test_create_volume_instance_invalid_disk_type(self, mock_request): - expected_err_msg = 'disk_type must be of rapyuta_io.clients.persistent_volumes.DiskType' - mock_get_package = Mock() - mock_get_package.text = GET_PACKAGE_SUCCESS - mock_get_package.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_package] - client = get_client() - persistent_volume = client.get_persistent_volume() - with self.assertRaises(InvalidParameterException) as e: - persistent_volume.create_volume_instance(name='test-volume', capacity=DiskCapacity.GiB_32, - disk_type='invalid_disk_type') - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_create_volume_instance_invalid_disk_capacity(self, mock_request): - expected_err_msg = 'capacity must be one of rapyuta_io.clients.persistent_volumes.DiskCapacity' - mock_get_package = Mock() - mock_get_package.text = GET_PACKAGE_SUCCESS - mock_get_package.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_package] - client = get_client() - persistent_volume = client.get_persistent_volume() - with self.assertRaises(InvalidParameterException) as e: - persistent_volume.create_volume_instance(name='test-volume', capacity='invalid_capacity', - disk_type=DiskType.SSD) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_create_volume_instance_with_integer_capacity_success(self, mock_request): - expected_url_create_disk = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/disk' - expected_url_get_disk = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/disk/disk-guid' - expected_url_provision_client = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceinstance/dep-guid' - expected_url_get_package = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/serviceclass/status?' \ - 'package_uid=io-public-persistent-volume' - mock_get_package = Mock() - mock_get_package.text = GET_PACKAGE_SUCCESS - mock_get_package.status_code = requests.codes.OK - mock_client_provision = Mock() - mock_client_provision.text = PROVISION_CLIENT_SUCCESS - mock_client_provision.status_code = requests.codes.OK - mock_client_get_disk = Mock() - mock_client_get_disk.text = GET_DISK_SUCCESS - mock_client_get_disk.status_code = requests.codes.OK - mock_get_volume_instance = Mock() - mock_get_volume_instance.text = GET_VOLUME_INSTANCE_SUCCESS - mock_get_volume_instance.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_package, mock_client_provision, mock_client_get_disk, mock_get_volume_instance] - client = get_client() - persistent_volume = client.get_persistent_volume() - volume_instance = persistent_volume.create_volume_instance(name='test-volume', capacity=32, - disk_type=DiskType.SSD) - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url_get_package, method='GET', params=None), - call(headers=headers, json=ANY, url=expected_url_create_disk, method='POST', params=None), - call(headers=headers, json=None, url=expected_url_get_disk, method='GET', params=None), - call(headers=headers, json=None, url=expected_url_provision_client, method='GET', params=None) - ]) - self.assertEqual(volume_instance['name'], 'test-volume') - self.assertEqual(volume_instance['parameters']['io-pv']['capacity'], 32) - self.assertEqual(volume_instance['parameters']['io-pv']['diskType'], DiskType.SSD.value) diff --git a/tests/project_test.py b/tests/project_test.py deleted file mode 100644 index 0ccc8a29..00000000 --- a/tests/project_test.py +++ /dev/null @@ -1,345 +0,0 @@ -from __future__ import absolute_import -import requests -import unittest - -from mock import patch, call, Mock - -from rapyuta_io.clients.project import Project -from rapyuta_io.utils import InvalidParameterException, InternalServerError -from tests.utils.client import get_client, headers, AUTH_TOKEN -from tests.utils.projects_responses import PROJECT_CREATE_SUCCESS, PROJECT_GET_SUCCESS, PROJECT_LIST_SUCCESS, \ - PROJECT_CREATE_INVITED_ORG_SUCCESS - - -class ProjectTests(unittest.TestCase): - def test_bad_names_length(self): - expected_err_msg = 'length of name must be between 3 and 15 characters' - with self.assertRaises(InvalidParameterException) as e: - Project('a') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_names_type(self): - expected_err_msg = 'name must be a string' - with self.assertRaises(InvalidParameterException) as e: - Project(name=123) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_names_invalid_characters(self): - expected_err_msg = 'name can have alphabets, numbers or - only' - with self.assertRaises(InvalidParameterException) as e: - Project('4@#1$') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_create_project_invalid_project_type(self): - expected_err_msg = 'project must be non-empty and of type rapyuta_io.clients.project.Project' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_project('invalid-project-type') - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_project_invalid_organization_id(self): - expected_err_msg = 'organization_guid needs to a non empty string' - with self.assertRaises(InvalidParameterException) as e: - Project('test-project', 1) - self.assertEqual(expected_err_msg, str(e.exception)) - - @patch('requests.request') - def test_create_project_success(self, mock_request): - project = Project('test-project') - client = get_client() - expected_payload = {'name': 'test-project'} - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/create' - mock_project = Mock() - mock_project.text = PROJECT_CREATE_SUCCESS - mock_project.status_code = requests.codes.OK - mock_request.side_effect = [mock_project] - result = client.create_project(project) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params={}), - ]) - self.assertIsInstance(result, Project) - self.assertTrue(hasattr(result, 'guid')) - - @patch('requests.request') - def test_create_project_in_invited_organization_success(self, mock_request): - project = Project('test-project', 'invited-org-guid') - client = get_client() - expected_payload = {'name': 'test-project', 'organization': {'guid': 'invited-org-guid'}} - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/create' - mock_project = Mock() - mock_project.text = PROJECT_CREATE_INVITED_ORG_SUCCESS - mock_project.status_code = requests.codes.OK - mock_request.side_effect = [mock_project] - result = client.create_project(project) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params={}), - ]) - self.assertIsInstance(result, Project) - self.assertTrue(hasattr(result, 'guid')) - self.assertEqual(result.name, 'test-project') - self.assertEqual(len(result.users), 2) - self.assertEqual(result.organization.guid, 'invited-org-guid') - - @patch('requests.request') - def test_create_project_server_error(self, mock_request): - project = Project('test-project') - client = get_client() - expected_payload = {'name': 'test-project'} - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/create' - mock_project = Mock() - mock_project.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_project] - with self.assertRaises(InternalServerError) as e: - client.create_project(project) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params={}), - ]) - - @patch('requests.request') - def test_get_project_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/project-guid/get' - mock_project = Mock() - mock_project.status_code = requests.codes.OK - mock_project.text = PROJECT_GET_SUCCESS - mock_request.side_effect = [mock_project] - result = client.get_project('project-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}) - ]) - self.assertIsInstance(result, Project) - self.assertTrue(hasattr(result, 'guid')) - - @patch('rapyuta_io.utils.rest_client.DEFAULT_RETRY_COUNT', 0) - @patch('requests.request') - def test_get_project_server_error(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/project-guid/get' - mock_project = Mock() - mock_project.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_project] - with self.assertRaises(InternalServerError): - client.get_project('project-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}) - ]) - - @patch('requests.request') - def test_list_projects_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/list' - mock_projects = Mock() - mock_projects.status_code = requests.codes.OK - mock_projects.text = PROJECT_LIST_SUCCESS - mock_request.side_effect = [mock_projects] - proj_list = client.list_projects() - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}) - ]) - self.assertIsInstance(proj_list, list) - for proj in proj_list: - self.assertIsInstance(proj, Project) - self.assertTrue(hasattr(proj, 'guid')) - self.assertTrue(hasattr(proj, 'organization')) - self.assertEqual(proj.organization.name, 'temp-org') - - @patch('rapyuta_io.utils.rest_client.DEFAULT_RETRY_COUNT', 0) - @patch('requests.request') - def test_list_projects_server_error(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/list' - mock_projects = Mock() - mock_projects.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_projects] - with self.assertRaises(InternalServerError): - client.list_projects() - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}) - ]) - - @patch('requests.request') - def test_delete_project_from_client_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/delete' - expected_payload = {'guid': 'project-guid'} - mock_project = Mock() - mock_project.status_code = requests.codes.OK - mock_project.text = '{"success": true, "error": ""}' - mock_request.side_effect = [mock_project] - client.delete_project('project-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_delete_project_from_client_no_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/delete' - expected_payload = {'guid': 'project-guid'} - mock_project = Mock() - mock_project.status_code = requests.codes.OK - mock_project.text = '{"success": false, "error": ""}' - mock_request.side_effect = [mock_project] - result = client.delete_project('project-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - self.assertFalse(result) - - @patch('requests.request') - def test_delete_project_from_client_sever_error(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/delete' - expected_payload = {'guid': 'project-guid'} - mock_project = Mock() - mock_project.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_project] - with self.assertRaises(InternalServerError): - client.delete_project('project-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_delete_method_success(self, mock_request): - proj = Project('test-project') - setattr(proj, '_core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') - setattr(proj, '_auth_token', 'Bearer ' + AUTH_TOKEN) - setattr(proj, 'guid', 'test_project') - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/delete' - expected_payload = {'guid': 'test_project'} - mock_project = Mock() - mock_project.status_code = requests.codes.OK - mock_project.text = '{"success": true, "error": ""}' - mock_request.side_effect = [mock_project] - proj.delete() - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_delete_method_server_error(self, mock_request): - proj = Project('test-project') - setattr(proj, '_core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') - setattr(proj, '_auth_token', 'Bearer ' + AUTH_TOKEN) - setattr(proj, 'guid', 'test_project') - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/delete' - expected_payload = {'guid': 'test_project'} - mock_project = Mock() - mock_project.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_project] - with self.assertRaises(InternalServerError): - proj.delete() - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - def test_delete_local_project_error(self): - proj = Project('test-project') - expected_err_msg = 'Project must be created first' - with self.assertRaises(InvalidParameterException) as e: - proj.delete() - self.assertEqual(expected_err_msg, str(e.exception)) - - @patch('requests.request') - def test_add_user_to_project_no_success(self, mock_request): - client = get_client() - dummy_project_guid = 'dummy-project-guid' - user_guid = 'user-guid' - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/{}/adduser'.format( - dummy_project_guid) - expected_payload = {'userGUID': user_guid} - mock_add_user_to_project = Mock() - mock_add_user_to_project.status_code = requests.codes.OK - mock_add_user_to_project.text = '{"success": false, "error": "project not found"}' - mock_request.side_effect = [mock_add_user_to_project] - client.add_user_to_project(project_guid=dummy_project_guid, user_guid=user_guid) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='PUT', params={}) - ]) - - @patch('requests.request') - def test_add_user_to_project_server_error(self, mock_request): - client = get_client() - project_guid = 'project-guid' - user_guid = 'user-guid' - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/{}/adduser'.format( - project_guid) - expected_payload = {'userGUID': user_guid} - mock_add_user_to_project = Mock() - mock_add_user_to_project.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_add_user_to_project] - with self.assertRaises(InternalServerError): - client.add_user_to_project(project_guid=project_guid, user_guid=user_guid) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='PUT', params={}) - ]) - - @patch('requests.request') - def test_add_user_to_project_success(self, mock_request): - client = get_client() - project_guid = 'project-guid' - user_guid = 'user-guid' - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/{}/adduser'.format( - project_guid) - expected_payload = {'userGUID': user_guid} - mock_add_user_to_project = Mock() - mock_add_user_to_project.status_code = requests.codes.OK - mock_add_user_to_project.text = '{"success": true, "error": ""}' - mock_request.side_effect = [mock_add_user_to_project] - client.add_user_to_project(project_guid=project_guid, user_guid=user_guid) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='PUT', params={}) - ]) - - @patch('requests.request') - def test_remove_user_from_project_no_success(self, mock_request): - client = get_client() - dummy_project_guid = 'dummy-project-guid' - user_guid = 'user-guid' - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/{}/removeuser'.format( - dummy_project_guid) - expected_payload = {'userGUID': user_guid} - mock_remove_user_from_project = Mock() - mock_remove_user_from_project.status_code = requests.codes.OK - mock_remove_user_from_project.text = '{"success": false, "error": "project not found"}' - mock_request.side_effect = [mock_remove_user_from_project] - client.remove_user_from_project(project_guid=dummy_project_guid, user_guid=user_guid) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_remove_user_from_project_server_error(self, mock_request): - client = get_client() - project_guid = 'project-guid' - user_guid = 'user-guid' - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/{}/removeuser'.format( - project_guid) - expected_payload = {'userGUID': user_guid} - mock_remove_user_from_project = Mock() - mock_remove_user_from_project.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_remove_user_from_project] - with self.assertRaises(InternalServerError): - client.remove_user_from_project(project_guid=project_guid, user_guid=user_guid) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_remove_user_from_project_success(self, mock_request): - client = get_client() - project_guid = 'project-guid' - user_guid = 'user-guid' - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/project/{}/removeuser'.format( - project_guid) - expected_payload = {'userGUID': user_guid} - mock_remove_user_from_project = Mock() - mock_remove_user_from_project.status_code = requests.codes.OK - mock_remove_user_from_project.text = '{"success": true, "error": ""}' - mock_request.side_effect = [mock_remove_user_from_project] - client.remove_user_from_project(project_guid=project_guid, user_guid=user_guid) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) diff --git a/tests/routed_network_test.py b/tests/routed_network_test.py deleted file mode 100644 index 67706f03..00000000 --- a/tests/routed_network_test.py +++ /dev/null @@ -1,420 +0,0 @@ -from __future__ import absolute_import -import requests -import unittest -import json -from mock import patch, Mock, call -from rapyuta_io.clients.routed_network import RoutedNetwork, Parameters -from rapyuta_io.clients.package import ROSDistro -from rapyuta_io.clients.device import Device -from rapyuta_io.utils import to_objdict, ResourceNotFoundError -from rapyuta_io.utils.error import InvalidParameterException -from rapyuta_io.utils.partials import PartialMixin - -from tests.utils.client import get_client, remove_auth_token, add_auth_token, headers -from tests.utils.routed_networks_responses import ROUTED_NETWORK_CREATE_SUCCESS, ROUTED_NETWORK_GET_SUCCESS, \ - ROUTED_NETWORK_LIST_SUCCESS, ROUTED_NETWORK_NOT_FOUND -from six.moves import map -from rapyuta_io.clients.common_models import Limits - -class RoutedNetworkTest(unittest.TestCase): - - def setUp(self): - routed_network = { - "name": "test-network", - "guid": "net-testguid", - "internalDeploymentStatus": { - "error_code": [], - "phase": "Succeeded", - "status": "Running" - }, - } - self.routed_network = RoutedNetwork(to_objdict(routed_network)) - add_auth_token(self.routed_network) - - def test_create_cloud_routed_network_invalid_distro(self): - expected_err_msg = 'ROSDistro must be one of rapyuta_io.clients.package.ROSDistro' - - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_cloud_routed_network('test-network', 'invalid-distro', - True) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_cloud_routed_network_invalid_parameters(self): - expected_err_msg = 'parameters must be of type rapyuta_io.clients.routed_network.Parameters' - - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_cloud_routed_network('test-network', ROSDistro.KINETIC, - True, 'invalid parameters') - - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_create_device_noetic_routed_network_success(self, mock_request): - expected_payload = { - 'name': 'test-network', - 'runtime': 'device', - 'rosDistro': 'noetic', - 'shared': True, - 'parameters': { - 'NETWORK_INTERFACE': 'lo', - 'restart_policy': 'always', - 'device_id': 'test-device-id' - }, - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_create_network = Mock() - mock_create_network.text = ROUTED_NETWORK_CREATE_SUCCESS - mock_create_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network] - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network, mock_get_network] - - client = get_client() - device = Device._deserialize({'uuid': 'test-device-id', 'ip_interfaces': {'lo': 'ip'}, - 'config_variables': [], 'labels': [], 'deployments': []}) - routed_network = client.create_device_routed_network('test-network', ROSDistro.NOETIC, True, device, 'lo') - - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-testguid', method='GET', params=None) - ]) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertFalse(routed_network.is_partial) - - @patch('requests.request') - def test_create_device_routed_network_success(self, mock_request): - expected_payload = { - 'name': 'test-network', - 'runtime': 'device', - 'rosDistro': 'kinetic', - 'shared': True, - 'parameters': { - 'NETWORK_INTERFACE': 'lo', - 'restart_policy': 'always', - 'device_id': 'test-device-id' - }, - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_create_network = Mock() - mock_create_network.text = ROUTED_NETWORK_CREATE_SUCCESS - mock_create_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network] - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network, mock_get_network] - - client = get_client() - device = Device._deserialize({'uuid': 'test-device-id', 'ip_interfaces': {'lo': 'ip'}, - 'config_variables': [], 'labels': [], 'deployments': []}) - routed_network = client.create_device_routed_network('test-network', ROSDistro.KINETIC, True, device, 'lo') - - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-testguid', method='GET', params=None) - ]) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertFalse(routed_network.is_partial) - - def test_create_device_routed_network_invalid_distro(self): - expected_err_msg = 'ROSDistro must be one of rapyuta_io.clients.package.ROSDistro' - - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_device_routed_network('test-network', 'invalid-distro', - True, None, None) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_device_routed_network_without_device(self): - expected_err_msg = 'device must be of type rapyuta_io.clients.device.Device' - - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.create_device_routed_network('test-network', ROSDistro.KINETIC, - True, None, None) - - self.assertEqual(str(e.exception), expected_err_msg) - - def test_create_routed_network_without_network_interface(self): - expected_err_msg = 'NETWORK_INTERFACE should be in [\'wlp4s0\']' - - client = get_client() - device = Device._deserialize({'uuid': 'test-device-id', 'ip_interfaces': {'wlp4s0': 'ip'}, - 'config_variables': [], 'labels': [], 'deployments': []}) - with self.assertRaises(InvalidParameterException) as e: - client.create_device_routed_network('test-network', ROSDistro.KINETIC, - True, device, 'lo') - - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_create_cloud_noetic_routed_network_success(self, mock_request): - expected_payload = { - 'name': 'test-network', - 'runtime': 'cloud', - 'rosDistro': 'noetic', - 'shared': True, - 'parameters': { - }, - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_create_network = Mock() - mock_create_network.text = ROUTED_NETWORK_CREATE_SUCCESS - mock_create_network.status_code = requests.codes.OK - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network, mock_get_network] - client = get_client() - routed_network = client.create_cloud_routed_network('test-network', ROSDistro.NOETIC, - True) - - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-testguid', method='GET', params=None) - ]) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertFalse(routed_network.is_partial) - - @patch('requests.request') - def test_create_cloud_routed_network_success(self, mock_request): - expected_payload = { - 'name': 'test-network', - 'runtime': 'cloud', - 'rosDistro': 'kinetic', - 'shared': True, - 'parameters': { - }, - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_create_network = Mock() - mock_create_network.text = ROUTED_NETWORK_CREATE_SUCCESS - mock_create_network.status_code = requests.codes.OK - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network, mock_get_network] - client = get_client() - routed_network = client.create_cloud_routed_network('test-network', ROSDistro.KINETIC, - True) - - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-testguid', method='GET', params=None) - ]) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertFalse(routed_network.is_partial) - - @patch('requests.request') - def test_create_cloud_routed_network_with_parameters_success(self, mock_request): - expected_payload = { - 'name': 'test-network', - 'runtime': 'cloud', - 'rosDistro': 'kinetic', - 'shared': True, - 'parameters': {'limits': {'cpu': 1, 'memory': 1024}}, - } - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_create_network = Mock() - mock_create_network.text = ROUTED_NETWORK_CREATE_SUCCESS - mock_create_network.status_code = requests.codes.OK - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_create_network, mock_get_network] - client = get_client() - routed_network = client.create_cloud_routed_network('test-network', ROSDistro.KINETIC, - True, Parameters(limits=Limits(1, 1024))) - - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-testguid', method='GET', params=None) - ]) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertFalse(routed_network.is_partial) - - @patch('requests.request') - def test_get_routed_network(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format('net-testguid') - - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_network] - client = get_client() - routed_network = client.get_routed_network('net-testguid') - remove_auth_token(routed_network) - expected_response = json.loads(ROUTED_NETWORK_GET_SUCCESS) - expected_response['phase'] = "Succeeded" - expected_response["error_code"] = [] - expected_response["status"] = "Running" - expected_response[PartialMixin.PARTIAL_ATTR] = False - - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params=None) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertEqual(routed_network.to_dict(), expected_response) - self.assertFalse(routed_network.is_partial) - - @patch('requests.request') - def test_list_routed_network(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_list_network = Mock() - mock_list_network.text = ROUTED_NETWORK_LIST_SUCCESS - mock_list_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_network] - client = get_client() - routed_networks = client.get_all_routed_networks() - list(map(remove_auth_token, routed_networks)) - expected_response = json.loads(ROUTED_NETWORK_LIST_SUCCESS) - expected_response[0]['phase'] = "Succeeded" - expected_response[0]["error_code"] = [] - - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params=None) - self.assertEqual([routed_networks[0].to_dict()], expected_response) - for net in routed_networks: - self.assertTrue(net.is_partial) - - @patch('requests.request') - def test_routed_network_refresh(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork' - - mock_list_network = Mock() - mock_list_network.text = ROUTED_NETWORK_LIST_SUCCESS - mock_list_network.status_code = requests.codes.OK - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_list_network, mock_get_network] - expected_response = json.loads(ROUTED_NETWORK_GET_SUCCESS) - expected_response['phase'] = "Succeeded" - expected_response["error_code"] = [] - expected_response[PartialMixin.PARTIAL_ATTR] = False - - client = get_client() - routed_networks = client.get_all_routed_networks() - routed_network = routed_networks[0] - self.assertTrue(routed_network.is_partial) - routed_network.refresh() - remove_auth_token(routed_network) - - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params=None), - call(headers=headers, json=None, url=expected_url + '/' + 'net-testguid', method='GET', params={}), - ]) - self.assertFalse(routed_network.is_partial) - self.assertEqual(routed_network.guid, 'net-testguid') - self.assertEqual(routed_network.name, 'test-network') - self.assertEqual(routed_network.to_dict(), expected_response) - - def test_delete_routed_network_invalid_guid(self): - expected_err_msg = 'guid needs to be a non empty string' - client = get_client() - with self.assertRaises(InvalidParameterException) as e: - client.delete_routed_network(123) - self.assertEqual(str(e.exception), expected_err_msg) - - @patch('requests.request') - def test_delete_routed_network_not_found(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format('network-guid') - mock_delete_network = Mock() - mock_delete_network.text = ROUTED_NETWORK_NOT_FOUND - mock_delete_network.status_code = requests.codes.NOT_FOUND - mock_request.side_effect = [mock_delete_network] - client = get_client() - with self.assertRaises(ResourceNotFoundError) as e: - client.delete_routed_network('network-guid') - mock_request.assert_called_once_with(headers=headers, url=expected_url, json=None, - method='DELETE', params=None) - self.assertEqual(str(e.exception), 'routed network not found in db') - - @patch('requests.request') - def test_delete_routed_network_success(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format('network-guid') - mock_delete_network = Mock() - mock_delete_network.text = 'null' - mock_delete_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_delete_network] - client = get_client() - client.delete_routed_network('network-guid') - mock_request.assert_called_once_with(headers=headers, url=expected_url, json=None, - method='DELETE', params=None) - - @patch('requests.request') - def test_delete_routed_network_success_with_routed_network_object(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format(self.routed_network.guid) - - mock_delete_network = Mock() - mock_delete_network.text = 'null' - mock_delete_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_delete_network] - self.routed_network.delete() - - mock_request.assert_called_once_with(headers=headers, url=expected_url, json=None, - method='DELETE', params={}) - - @patch('requests.request') - def test_get_status_routed_network(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format(self.routed_network.guid) - expected_result = self.routed_network.internalDeploymentStatus - expected_result.errors = [expected_result.error_code] - mock_delete_network = Mock() - mock_delete_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_delete_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_delete_network] - deployment_status = self.routed_network.get_status() - - mock_request.assert_called_once() - self.assertEqual(mock_request.call_args_list[0][1]['url'], expected_url) - self.assertEqual(mock_request.call_args_list[0][1]['method'], 'GET') - self.assertEqual(deployment_status, expected_result) - - @patch('requests.request') - def test_get_status_routed_network(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format(self.routed_network.guid) - expected_result = self.routed_network.internalDeploymentStatus - expected_result.errors = expected_result.error_code - mock_delete_network = Mock() - mock_delete_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_delete_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_delete_network] - deployment_status = self.routed_network.get_status() - - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params={}) - self.assertEqual(deployment_status, expected_result) - - @patch('requests.request') - def test_poll_till_ready_routed_network(self, mock_request): - expected_url = 'https://gacatalog.apps.okd4v2.prod.rapyuta.io/routednetwork/{}'.format(self.routed_network.guid) - expected_result = self.routed_network.internalDeploymentStatus - expected_result.errors = expected_result.error_code - - mock_get_network = Mock() - mock_get_network.text = ROUTED_NETWORK_GET_SUCCESS - mock_get_network.status_code = requests.codes.OK - mock_request.side_effect = [mock_get_network] - deployment_status = self.routed_network.get_status() - - mock_request.assert_called_once_with(headers=headers, json=None, - url=expected_url, method='GET', params={}) - self.assertEqual(deployment_status, expected_result) diff --git a/tests/secret_test.py b/tests/secret_test.py deleted file mode 100644 index 505e0d1e..00000000 --- a/tests/secret_test.py +++ /dev/null @@ -1,352 +0,0 @@ -from __future__ import absolute_import -import base64 -from http.client import INTERNAL_SERVER_ERROR -import requests -import unittest - -from mock import Mock, call, patch - -from rapyuta_io.clients.secret import SecretType, \ - SecretConfigDocker, DOCKER_HUB_REGISTRY, Secret -from rapyuta_io.utils import InvalidParameterException, InternalServerError, ResourceNotFoundError -from tests.utils.client import get_client, headers, AUTH_TOKEN -from tests.utils.secrets_responses import SECRET_CREATE_SUCCESS, SECRET_LIST_SUCCESS, SECRET_UPDATE_SUCCESS - - -class SecretConfigTests(unittest.TestCase): - def test_bad_secret_config_docker_empty_username(self): - expected_err_msg = 'username cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigDocker(username='', password='password', email='test@example.com') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_config_docker_empty_password(self): - expected_err_msg = 'password cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigDocker(username='username', password='', email='test@example.com') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_config_docker_empty_email(self): - expected_err_msg = 'email cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigDocker(username='username', password='password', email='') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_config_docker_empty_registry(self): - expected_err_msg = 'registry cannot be empty' - with self.assertRaises(InvalidParameterException) as e: - SecretConfigDocker(username='username', password='password', email='test@example.com', registry='') - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_secret_config_docker_default_registry(self): - secret_config = SecretConfigDocker(username='username', password='password', email='test@example.com') - docker_config = '{"https://index.docker.io/v1/": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - expected_serialize = {'.dockercfg': base64.b64encode(docker_config.encode()).decode()} - self.assertEqual('username', secret_config.username) - self.assertEqual('password', secret_config.password) - self.assertEqual('test@example.com', secret_config.email) - self.assertEqual(DOCKER_HUB_REGISTRY, secret_config.registry) - self.assertEqual(SecretType.DOCKER, secret_config.get_type()) - self.assertEqual(expected_serialize, secret_config.serialize()) - - def test_secret_config_docker_private_registry(self): - secret_config = SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io') - docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - expected_serialize = {'.dockercfg': base64.b64encode(docker_config.encode()).decode()} - self.assertEqual('username', secret_config.username) - self.assertEqual('password', secret_config.password) - self.assertEqual('test@example.com', secret_config.email) - self.assertEqual('quay.io', secret_config.registry) - self.assertEqual(SecretType.DOCKER, secret_config.get_type()) - self.assertEqual(expected_serialize, secret_config.serialize()) - - -class SecretTests(unittest.TestCase): - def test_bad_secret_name_length(self): - expected_err_msg = 'length of name must be between 3 and 253 characters' - with self.assertRaises(InvalidParameterException) as e: - Secret(name='a' * 300, secret_config=SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_name_pattern(self): - expected_err_msg = 'name must consist of lower case alphanumeric characters or - and must start and end with ' \ - 'an alphanumeric character' - with self.assertRaises(InvalidParameterException) as e: - Secret(name='-SECRET-', secret_config=SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - self.assertEqual(expected_err_msg, str(e.exception)) - - def test_bad_secret_name_type(self): - expected_err_msg = 'name must be a string' - with self.assertRaises(InvalidParameterException) as e: - Secret(name=123, secret_config=SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - self.assertEqual(expected_err_msg, str(e.exception)) - - @patch('requests.request') - def test_create_secret_internal_server_error(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - client = get_client() - expected_payload = { - 'type': str(SecretType.DOCKER), - 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, - 'name': 'test-secret' - } - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/create' - mock_secret = Mock() - mock_secret.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_secret] - with self.assertRaises(InternalServerError) as e: - client.create_secret(secret) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params={}), - ]) - - @patch('requests.request') - def test_create_secret_success(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - client = get_client() - expected_payload = { - 'type': str(SecretType.DOCKER), - 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, - 'name': 'test-secret' - } - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/create' - mock_secret = Mock() - mock_secret.text = SECRET_CREATE_SUCCESS - mock_secret.status_code = requests.codes.OK - mock_request.side_effect = [mock_secret] - result = client.create_secret(secret) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='POST', params={}), - ]) - self.assertIsInstance(result, Secret) - - @patch('rapyuta_io.utils.rest_client.DEFAULT_RETRY_COUNT', 0) - @patch('requests.request') - def test_get_secret_internal_server_error(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/get' - mock_secret = Mock() - mock_secret.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_secret] - with self.assertRaises(InternalServerError): - client.get_secret('secret-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}), - ]) - - @patch('requests.request') - def test_get_secret_not_found(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/get' - mock_secret = Mock() - mock_secret.status_code = requests.codes.NOT_FOUND - mock_request.side_effect = [mock_secret] - with self.assertRaises(ResourceNotFoundError): - client.get_secret('secret-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}), - ]) - - @patch('requests.request') - def test_get_secret_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/get' - mock_secret = Mock() - mock_secret.text = SECRET_CREATE_SUCCESS - mock_secret.status_code = requests.codes.OK - mock_request.side_effect = [mock_secret] - result = client.get_secret('secret-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}), - ]) - self.assertIsInstance(result, Secret) - - @patch('rapyuta_io.utils.rest_client.DEFAULT_RETRY_COUNT', 0) - @patch('requests.request') - def test_list_secret_internal_server_error(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/list' - mock_secrets = Mock() - mock_secrets.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_secrets] - with self.assertRaises(InternalServerError): - client.list_secrets() - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}) - ]) - - @patch('requests.request') - def test_list_secret_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/list' - mock_secrets = Mock() - mock_secrets.status_code = requests.codes.OK - mock_secrets.text = SECRET_LIST_SUCCESS - mock_request.side_effect = [mock_secrets] - secret_list = client.list_secrets() - mock_request.assert_has_calls([ - call(headers=headers, json=None, url=expected_url, method='GET', params={}) - ]) - self.assertIsInstance(secret_list, list) - for secret in secret_list: - self.assertIsInstance(secret, Secret) - - @patch('requests.request') - def test_delete_secret_internal_server_error(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/delete' - expected_payload = {'guid': 'secret-guid'} - mock_secret = Mock() - mock_secret.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_secret] - with self.assertRaises(InternalServerError): - client.delete_secret('secret-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_delete_secret_no_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/delete' - expected_payload = {'guid': 'secret-guid'} - mock_secret = Mock() - mock_secret.status_code = requests.codes.OK - mock_secret.text = '{"success": false, "error": ""}' - mock_request.side_effect = [mock_secret] - result = client.delete_secret('secret-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - self.assertFalse(result) - - @patch('requests.request') - def test_delete_secret_success(self, mock_request): - client = get_client() - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/delete' - expected_payload = {'guid': 'secret-guid'} - mock_secret = Mock() - mock_secret.status_code = requests.codes.OK - mock_secret.text = '{"success": true, "error": ""}' - mock_request.side_effect = [mock_secret] - client.delete_secret('secret-guid') - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_delete_method_internal_server_error(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com')) - setattr(secret, '_core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') - setattr(secret, '_auth_token', 'Bearer ' + AUTH_TOKEN) - setattr(secret, '_project', 'test_project') - setattr(secret, 'guid', 'secret-guid') - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/delete' - expected_payload = {'guid': 'secret-guid'} - mock_secret = Mock() - mock_secret.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_secret] - with self.assertRaises(InternalServerError): - secret.delete() - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - def test_delete_method_invalid_parameter(self): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com')) - expected_err_msg = 'Secret must be created first' - setattr(secret, 'guid', 'secret-guid') - with self.assertRaises(InvalidParameterException) as e: - secret.delete() - self.assertEqual(expected_err_msg, str(e.exception)) - - @patch('requests.request') - def test_delete_method_success(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - setattr(secret, '_core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') - setattr(secret, '_auth_token', 'Bearer ' + AUTH_TOKEN) - setattr(secret, '_project', 'test_project') - setattr(secret, 'guid', 'secret-guid') - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/delete' - expected_payload = {'guid': 'secret-guid'} - mock_secret = Mock() - mock_secret.status_code = requests.codes.OK - mock_secret.text = '{"success": true, "error":""}' - mock_request.side_effect = [mock_secret] - secret.delete() - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='DELETE', params={}) - ]) - - @patch('requests.request') - def test_update_method_success(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - client = get_client() - expected_payload = { - 'type': str(SecretType.DOCKER), - 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, - 'name': 'test-secret' - } - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/update' - mock_secret = Mock() - mock_secret.text = SECRET_UPDATE_SUCCESS - mock_secret.status_code = requests.codes.OK - mock_request.side_effect = [mock_secret] - result = client.update_secret('secret-guid', secret) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='PUT', params={}), - ]) - self.assertIsInstance(result, Secret) - - @patch('requests.request') - def test_update_method_internal_server_error(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - client = get_client() - expected_payload = { - 'type': str(SecretType.DOCKER), - 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, - 'name': 'test-secret' - } - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/update' - mock_secret = Mock() - mock_secret.status_code = requests.codes.INTERNAL_SERVER_ERROR - mock_request.side_effect = [mock_secret] - with self.assertRaises(InternalServerError) as e: - client.update_secret("secret-guid", secret) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='PUT', params={}), - ]) - - @patch('requests.request') - def test_update_method_not_found_error(self, mock_request): - secret = Secret('test-secret', SecretConfigDocker(username='username', password='password', email='test@example.com', - registry='quay.io')) - docker_config = '{"quay.io": {"username": "username", "password": "password", "email": "test@example.com", "auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}' - client = get_client() - expected_payload = { - 'type': str(SecretType.DOCKER), - 'data': {'.dockercfg': base64.b64encode(docker_config.encode()).decode()}, - 'name': 'test-secret' - } - expected_url = 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io/api/secret/secret-guid/update' - mock_secret = Mock() - mock_secret.status_code = requests.codes.NOT_FOUND - mock_request.side_effect = [mock_secret] - with self.assertRaises(ResourceNotFoundError) as e: - client.update_secret("secret-guid", secret) - mock_request.assert_has_calls([ - call(headers=headers, json=expected_payload, url=expected_url, method='PUT', params={}), - ]) \ No newline at end of file diff --git a/tests/user_test.py b/tests/user_test.py index 11e132c8..72e81314 100644 --- a/tests/user_test.py +++ b/tests/user_test.py @@ -31,7 +31,6 @@ def test_get_authenticated_user_details_success(self, mock_request): for project in user.projects: self.assertIsInstance(project, Project) self.assertIsNotNone(project.guid) - self.assertTrue(hasattr(project, 'organization')) self.assertTrue(len(user.projects)) self.assertIsInstance(user.organization, Organization)