예제 #1
0
def test_cluster_unsafe_init():
    cluster = Cluster(http_address=config.http_cluster.url,
                      grpc_address=config.grpc_cluster.url,
                      grpc_credentials=ssl_channel_credentials(),
                      check_connection=False)
    assert cluster.http_address == config.http_cluster.url
    assert cluster.channel is not None
예제 #2
0
def get_cluster_connection(path: str = CONFIG_PATH) -> Cluster:
    current_cluster = read_current_cluster(path)
    if current_cluster is None:
        raise ClickException(
            "Can't establish connection to Hydrosphere cluster: cluster config is missing. Use `hs cluster` commands."
        )
    return Cluster(http_address=current_cluster.cluster.server)
예제 #3
0
    def create(cluster: Cluster, name: str, modelversion_id: int,
               config: MetricSpecConfig) -> 'MetricSpec':
        """
        Create MetricSpec and returns corresponding instance.

        :param cluster: active cluster
        :param name: name of the metric
        :param modelversion_id: ModelVersion for which to create a MetricSpec
        :param config: MetricSpecConfig, describing MetricSpec
        :return: metricSpec
        """
        metric_spec_json = {
            'name': name,
            'modelVersionId': modelversion_id,
            'config': {
                'modelVersionId': config.modelversion_id,
                'threshold': config.threshold,
                'thresholdCmpOperator': {
                    'kind': config.threshold_op
                }
            }
        }
        resp = cluster.request("POST",
                               MetricSpec._BASE_URL,
                               json=metric_spec_json)
        handle_request_error(
            resp,
            f"Failed to create a MetricSpec for name={name}, modelversion_id={modelversion_id}. {resp.status_code} {resp.text}"
        )
        return MetricSpec._from_json(cluster, resp.json())
예제 #4
0
    def create(
        cluster: Cluster,
        model_name: str,
        version: int,
        metadata: Optional[Dict[str, str]] = None,
        deployment_configuration: Optional[DeploymentConfiguration] = None
    ) -> 'Servable':
        """
        Deploy an instance of uploaded model version at your cluster.

        :param deployment_configuration: k8s configurations used to run this servable
        :param cluster: Cluster connected to Hydrosphere
        :param model_name: Name of uploaded model
        :param version: Version of uploaded model
        :param metadata: Information which you can attach to your servable in a form of Dict[str, str]
        :raises ServableException:
        :return: servable
        """
        msg = {
            "modelName": model_name,
            "version": version,
        }
        if metadata:
            msg['metadata'] = metadata

        if deployment_configuration:
            msg['deploymentConfigName'] = deployment_configuration.name

        resp = cluster.request('POST', Servable._BASE_URL, json=msg)
        handle_request_error(
            resp,
            f"Failed to create a servable. {resp.status_code} {resp.text}")
        return Servable._from_json(cluster, resp.json())
    def build(self, cluster: Cluster) -> Application:
        """
        Create an Application in your Hydrosphere cluster.

        :return: Application object
        """
        if not self.stages:
            raise ValueError("No execution stages were provided")

        execution_graph = ExecutionGraph(stages=self.stages)
        application_json = {
            "name": self.name,
            "kafkaStreaming":
            [sp.to_dict() for sp in self.streaming_parameters],
            "executionGraph": execution_graph.to_dict(),
            "metadata": self.metadata
        }

        resp = cluster.request("POST",
                               Application._BASE_URL,
                               json=application_json)
        handle_request_error(
            resp,
            f"Failed to create an application {self.name}. {resp.status_code} {resp.text}"
        )
        return Application._from_json(cluster, resp.json())
예제 #6
0
def test_pipeline_apply(mock_app_build, mock_app_delete, mock_app_find,
                        mock_mv_find, singular_yaml_path):
    conn = Cluster("http://")
    mock_mv_find.return_value = ModelVersion(cluster=conn,
                                             id=1,
                                             model_id=1,
                                             name="claims-model",
                                             version=1,
                                             signature=ModelSignature(),
                                             status="Released",
                                             image=DockerImage(name="aaa",
                                                               tag="aaa"),
                                             runtime=DockerImage(name="aaa",
                                                                 tag="aaa"),
                                             is_external=False)
    mock_app_find.return_value = HS_APP(cluster=conn,
                                        id=1,
                                        name="app",
                                        execution_graph=ExecutionGraph([]),
                                        status="Ready",
                                        signature=ModelSignature(),
                                        kafka_streaming=[])
    mock_app_build.return_value = HS_APP(cluster=conn,
                                         id=1,
                                         name="app",
                                         execution_graph=ExecutionGraph([]),
                                         status="Ready",
                                         signature=ModelSignature(),
                                         kafka_streaming=[])
    app: Application = Application.parse_file(singular_yaml_path)
    app.apply(conn, os.path.dirname(singular_yaml_path))
def test_training_data_upload(cluster: Cluster,
                              model_version_builder: ModelVersionBuilder,
                              training_data: str):
    mv: ModelVersion = model_version_builder.build(cluster)
    mv.training_data = training_data
    data_upload_response = mv.upload_training_data()
    data_upload_response.wait(sleep=5)
    resp = cluster.request("GET", f"/monitoring/profiles/batch/{mv.id}/status")
    assert "Success" == resp.json()["kind"]
예제 #8
0
def test_depconf_apply(mock_request, dep_conf_path):
    resp = Response()
    resp.status_code = 200
    resp._content = json.dumps({"name": "test"}).encode("utf-8")
    mock_request.return_value = resp
    conn = Cluster("http://")
    with open(dep_conf_path, "r") as f:
        obj = yaml.safe_load(f)
    dc: DeploymentConfig = DeploymentConfig.parse_obj(obj)
    dc.apply(conn)
def _upload_s3_file(cluster: Cluster, modelversion_id: int, path: str) -> requests.Response:
    """
    Internal method for submitting training data from S3 to Hydrosphere.

    :param cluster: Cluster instance
    :param url: url to which submit S3 path
    :param path: S3 path to training data
    """
    url = f'/monitoring/profiles/batch/{modelversion_id}/s3'
    return cluster.request("POST", url, json={"path": path})
    def list(cluster: Cluster) -> List['DeploymentConfiguration']:
        """
        List all deployment configurations

        :param cluster:
        :return:
        """
        resp = cluster.request("GET", DeploymentConfiguration._BASE_URL)
        handle_request_error(resp, f"Failed to get a list of Deployment Configurations - {resp.status_code} {resp.text}")
        return [DeploymentConfiguration.parse_obj(app_json) for app_json in resp.json()]
    def delete(cluster: Cluster, name: str) -> dict:
        """
        Delete deployment configuration from Hydrosphere cluster

        :param cluster: Active Cluster
        :param name: Deployment configuration name
        :return: json response from the server
        """
        resp = cluster.request("DELETE", f"{DeploymentConfiguration._BASE_URL}/{name}")
        handle_request_error(resp, f"Failed to delete Deployment Configuration named {name}: {resp.status_code} {resp.text}")
        return resp
    def find(cluster: Cluster, name: str) -> 'DeploymentConfiguration':
        """
        Search a deployment configuration by name

        :param cluster:
        :param name:
        :return:
        """
        resp = cluster.request("GET", f"{DeploymentConfiguration._BASE_URL}/{name}")
        handle_request_error(resp, f"Failed to find Deployment Configuration named {name} {resp.status_code} {resp.text}")
        return DeploymentConfiguration.parse_obj(resp.json())
예제 #13
0
    def list(cluster: Cluster) -> List['MetricSpec']:
        """
        Send request and returns list with all available metric specs.

        :param cluster: active cluster
        :return: list with all available metric specs
        """
        resp = cluster.request("GET", MetricSpec._BASE_URL)
        handle_request_error(
            resp,
            f"Failed to list MetricSpecs. {resp.status_code} {resp.text}")
        return [MetricSpec._from_json(cluster, x) for x in resp.json()]
예제 #14
0
    def delete(cluster: Cluster, id: int) -> dict:
        """
        Delete MetricSpec.

        :return: result of deletion
        """
        resp = cluster.request("DELETE", f"{MetricSpec._BASE_URL}/{id}")
        handle_request_error(
            resp,
            f"Failed to delete MetricSpec for id={id}. {resp.status_code} {resp.text}"
        )
        return resp.json()
def _upload_local_file(cluster: Cluster, modelversion_id: int, path: str, 
                       chunk_size=1024) -> requests.Response:
    """
    Internal method for uploading local training data to Hydrosphere.

    :param cluster: active cluster
    :param modelversion_id: modelversion_id for which to upload training data
    :param path: path to a local file
    :param chunk_size: chunk size to use for streaming
    """    
    gen = read_in_chunks(path, chunk_size)
    url = f'/monitoring/profiles/batch/{modelversion_id}'
    return cluster.request("POST", url, data=gen, stream=True)
    def find(cluster: Cluster, name: str, version: int) -> 'ModelVersion':
        """
        Find a ModelVersion on the cluster by model name and a version.

        :param cluster: active cluster
        :param name: name of the model
        :param version: version of the model
        :return: ModelVersion object
        """
        resp = cluster.request("GET", f"{ModelVersion._BASE_URL}/version/{name}/{version}")
        handle_request_error(
            resp, f"Failed to find model_version for name={name}, version={version}. {resp.status_code} {resp.text}")
        return ModelVersion._from_json(cluster, resp.json())
    def list(cluster: Cluster) -> List['ModelVersion']:
        """
        List all model versions on the cluster.

        :param cluster: active cluster
        :return: list of ModelVersions 
        """

        resp = cluster.request("GET", f"{ModelVersion._BASE_URL}/version")
        handle_request_error(
            resp, f"Failed to list model versions. {resp.status_code} {resp.text}")
        return [ModelVersion._from_json(cluster, modelversion_json)
                for modelversion_json in resp.json()]
예제 #18
0
    def list(cluster: Cluster) -> List['Servable']:
        """
        Retrieve a list of all servables available at your cluster

        :param cluster: Hydrosphere cluster
        :return: List of all Servables available at your cluster
        """
        resp = cluster.request("GET", Servable._BASE_URL)
        handle_request_error(
            resp, f"Failed to list servables. {resp.status_code} {resp.text}")
        return [
            Servable._from_json(cluster, servable_json)
            for servable_json in resp.json()
        ]
예제 #19
0
    def find_by_id(cluster: Cluster, id: int) -> 'MetricSpec':
        """
        Find MetricSpec by id.

        :param cluster: active cluster
        :param id: 
        :return: MetricSpec
        """
        resp = cluster.request("GET", f"{MetricSpec._BASE_URL}/{id}")
        handle_request_error(
            resp,
            f"Failed to retrieve MetricSpec for id={id}. {resp.status_code} {resp.text}"
        )
        return MetricSpec._from_json(cluster, resp.json())
    def find(cluster: Cluster, application_name: str) -> 'Application':
        """
        Search for an application by name. 

        :param cluster: active cluster
        :param application_name: application name
        :return: deserialized application object
        """
        resp = cluster.request("GET",
                               f"{Application._BASE_URL}/{application_name}")
        handle_request_error(
            resp,
            f"Failed to find an application by name={application_name}. {resp.status_code} {resp.text}"
        )
        return Application._from_json(cluster, resp.json())
    def delete(cluster: Cluster, application_name: str) -> dict:
        """
        Delete an application by name.

        :param cluster: active cluster
        :param application_name: application name
        :return: response from the cluster
        """
        resp = cluster.request("DELETE",
                               f"{Application._BASE_URL}/{application_name}")
        handle_request_error(
            resp,
            f"Failed to delete application for name={application_name}. {resp.status_code} {resp.text}"
        )
        return resp.json()
    def find_by_id(cluster: Cluster, id: int) -> 'ModelVersion':
        """
        Find a ModelVersion on the cluster by id.

        :param cluster: active cluster
        :param id: model version id
        :return: ModelVersion object
        """
        resp = cluster.request("GET", f"{ModelVersion._BASE_URL}/version")
        handle_request_error(
            resp, f"Failed to retrieve model_version by id={id}. {resp.status_code} {resp.text}")
        for modelversion_json in resp.json():
            if modelversion_json['id'] == id:
                return ModelVersion._from_json(cluster, modelversion_json)
        raise BadRequestException(f"Failed to retrieve model_version by id={id}.")
    def list(cluster: Cluster) -> List['Application']:
        """
        List all available applications from the cluster.

        :param cluster: active cluster
        :return: deserialized list of application objects
        """
        resp = cluster.request("GET", Application._BASE_URL)
        handle_request_error(
            resp,
            f"Failed to list all applications. {resp.status_code} {resp.text}")
        applications = [
            Application._from_json(cluster, app_json)
            for app_json in resp.json()
        ]
        return applications
예제 #24
0
    def find_by_name(cluster: Cluster, servable_name: str) -> 'Servable':
        """
        Finds a serving servable in a cluster

        :param cluster: active cluster
        :param servable_name: a name of the servable
        :raises ServableException:
        :return: Servable
        """

        resp = cluster.request("GET", f"{Servable._BASE_URL}/{servable_name}")
        handle_request_error(
            resp,
            f"Failed to find servable for name={servable_name}. {resp.status_code} {resp.text}"
        )
        return Servable._from_json(cluster, resp.json())
    def build(self, cluster: Cluster) -> DeploymentConfiguration:
        """
        Create the Deployment Configuration in your Hydrosphere cluster.

        :return: Deployment Configuration object from the Hydrosphere cluster
        """
        config = DeploymentConfiguration(
            name=self.name,
            hpa=self.hpa,
            pod=self.pod_spec,
            container=self.container_spec,
            deployment=self.deployment_spec
        )
        resp = cluster.request("POST", DeploymentConfiguration._BASE_URL, json=config.to_dict())
        handle_request_error(resp, f"Failed to upload new Deployment Configuration. {resp.status_code} {resp.text}")
        return DeploymentConfiguration.parse_obj(resp.json())
예제 #26
0
    def find_by_modelversion(cluster: Cluster,
                             modelversion_id: int) -> List['MetricSpec']:
        """
        Find MetricSpecs by model version.

        :param cluster: active cluster
        :param modelversion_id: ModelVersions for which to return metrics.
        :return: list of MetricSpec objects
        """
        resp = cluster.request(
            "GET", f"{MetricSpec._BASE_URL}/modelversion/{modelversion_id}")
        handle_request_error(
            resp,
            f"Failed to list MetricSpecs for modelversion_id={modelversion_id}. {resp.status_code} {resp.text}"
        )
        return [MetricSpec._from_json(cluster, x) for x in resp.json()]
예제 #27
0
def test_external_model_parse():
    import json
    json_str = """
    {
    "id": 138,
    "created": "2021-09-24T11:29:09.624Z",
    "finished": "2021-09-24T11:29:09.624Z",
    "modelVersion": 1,
    "modelSignature": {
        "signatureName": "predict",
        "inputs": [{
            "name": "in",
            "dtype": "DT_DOUBLE",
            "shape": {
                "dims": []
            },
            "profile": "NONE"
        }],
        "outputs": [{
            "name": "out",
            "dtype": "DT_DOUBLE",
            "shape": {
                "dims": []
            },
            "profile": "NONE"
        }]
    },
    "model": {
        "id": 18,
        "name": "external-model"
    },
    "status": "Released",
    "metadata": {},
    "applications": [],
    "image": null,
    "runtime": null,
    "monitoringConfiguration": {
        "batchSize": 100
    },
    "isExternal": true
    }"""
    json_dict = json.loads(json_str)
    cl = Cluster("asdasd:9091", "asdasd:9090", check_connection=False)

    mv = ModelVersion._from_json(cl, json_dict)
    print(mv)
    assert False
예제 #28
0
def test_model_analyze_without_grpc():
    cl = Cluster("asdasd:9091", check_connection=False)
    model = ModelVersion(cluster=cl,
                         id=1,
                         model_id=2,
                         name="test",
                         version=3,
                         signature=ModelSignature(),
                         status=None,
                         image="",
                         runtime="",
                         is_external=True)
    req_id = "asdsad"
    request = PredictRequest()
    error = "error"
    with pytest.raises(HydrosphereException):
        model.analyze(request_id=req_id, request=request, error=error)
예제 #29
0
    def delete(cluster: Cluster, servable_name: str) -> dict:
        """
        Shut down and delete servable instance.

        :param cluster: active cluster
        :param servable_name: name of the servable
        :return: json response from serve

        .. warnings also::
          Use with caution. Predictors previously associated with this servable will not be able to connect to it.

        """
        resp = cluster.request("DELETE",
                               f"{Servable._BASE_URL}/{servable_name}")
        handle_request_error(
            resp,
            f"Failed to delete the servable with name={servable_name}. {resp.status_code} {resp.text}"
        )
        return resp.json()
    def build_external(self, cluster: Cluster) -> 'ModelVersion':
        """
        Create an external ModelVersion on the cluster. 

        :param cluster: active cluster
        :return: ModelVersion object
        """
        if self.signature is None:
            raise ValueError("signature is not specified for the model")
        model = {
            "name": self.name,
            "signature": ModelSignature_to_signature_dict(self.signature),
            "metadata": self.metadata
        }
        resp = cluster.request("POST", "/api/v2/externalmodel", json=model)
        handle_request_error(
            resp, f"Failed to create an external model. {resp.status_code} {resp.text}")
        json_dict = resp.json()
        json_dict['isExternal'] = True
        return ModelVersion._from_json(cluster, json_dict)