From a48079ed239960284c920c48c800195b209b774d Mon Sep 17 00:00:00 2001 From: Yaswanth Kumar Togarapu Date: Fri, 20 Sep 2024 10:07:31 +0530 Subject: [PATCH] chore: removes some unsupported client apis --- rapyuta_io/__init__.py | 3 - rapyuta_io/clients/__init__.py | 2 - rapyuta_io/clients/build.py | 603 ------------------------ rapyuta_io/clients/buildoperation.py | 64 --- rapyuta_io/clients/deployment.py | 7 - rapyuta_io/clients/validation_schema.py | 44 -- rapyuta_io/rio_client.py | 2 - sdk_test/util.py | 398 ---------------- setup.py | 9 +- 9 files changed, 6 insertions(+), 1126 deletions(-) delete mode 100644 rapyuta_io/clients/build.py delete mode 100644 rapyuta_io/clients/buildoperation.py delete mode 100644 rapyuta_io/clients/validation_schema.py diff --git a/rapyuta_io/__init__.py b/rapyuta_io/__init__.py index 9c2039e5..a6f5d6c3 100644 --- a/rapyuta_io/__init__.py +++ b/rapyuta_io/__init__.py @@ -7,9 +7,6 @@ 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 diff --git a/rapyuta_io/clients/__init__.py b/rapyuta_io/clients/__init__.py index b55616f1..e9d8c84a 100644 --- a/rapyuta_io/clients/__init__.py +++ b/rapyuta_io/clients/__init__.py @@ -3,7 +3,5 @@ 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 .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/deployment.py b/rapyuta_io/clients/deployment.py index a6eec2e9..8d7c2fcc 100644 --- a/rapyuta_io/clients/deployment.py +++ b/rapyuta_io/clients/deployment.py @@ -294,10 +294,3 @@ def poll_deployment_till_ready(self, retry_count=DEPLOYMENT_STATUS_RETRY_COUNT, """ 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/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 19503d65..943bc4d1 100644 --- a/rapyuta_io/rio_client.py +++ b/rapyuta_io/rio_client.py @@ -104,7 +104,6 @@ def set_project(self, project_guid): >>> 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) @@ -399,7 +398,6 @@ def create_rosbag_job(self, rosbag_job): >>> 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, ... component_instance_id=component_instance_id, diff --git a/sdk_test/util.py b/sdk_test/util.py index bf9e5542..780ea8f8 100644 --- a/sdk_test/util.py +++ b/sdk_test/util.py @@ -1,17 +1,8 @@ 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,345 +11,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') - logger = logging.getLogger('RIO_SDK Logger') - logger.setLevel(logging.DEBUG) - if not logger.handlers: - console_handler = logging.StreamHandler() - console_handler.setFormatter(formatter) - logger.handler_set = True - logger.addHandler(console_handler) - return logger - def start_roscore(device, bg=True): command = Command(cmd='source /opt/ros/melodic/setup.bash && roscore', shell='/bin/bash', bg=bg) @@ -370,53 +22,3 @@ 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/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",