From 6290613b95c7246107c63d80bd71c7de3cbf5df9 Mon Sep 17 00:00:00 2001 From: Madhava Jay Date: Tue, 9 Jul 2024 13:33:30 +1000 Subject: [PATCH 1/5] Renamed domain, Domain and DOMAIN to datasite, Datasite and DATASITE - Removed old protocol version versions due to conflict --- .github/workflows/pr-tests-stack.yml | 12 +- README.md | 22 +- ...omain-00.gif => 00-deploy-datasite-00.gif} | Bin ...omain-01.jpg => 00-deploy-datasite-01.jpg} | Bin .../api_reference/syft.client.registry.rst | 2 +- ...node.domain.rst => syft.node.datasite.rst} | 6 +- docs/source/api_reference/syft.node.rst | 2 +- docs/source/deployment/glossary.rst | 34 +- docs/source/getting_started/index.rst | 6 +- docs/source/guides/index.rst | 14 +- .../admin/Custom API + Custom Worker.ipynb | 523 -------- notebooks/api/0.8/00-load-data.ipynb | 48 +- notebooks/api/0.8/01-submit-code.ipynb | 28 +- .../api/0.8/02-review-code-and-approve.ipynb | 18 +- .../03-data-scientist-download-result.ipynb | 18 +- notebooks/api/0.8/04-pytorch-example.ipynb | 28 +- notebooks/api/0.8/05-custom-policy.ipynb | 20 +- .../api/0.8/06-multiple-code-requests.ipynb | 10 +- ...> 07-datasite-register-control-flow.ipynb} | 12 +- notebooks/api/0.8/08-code-version.ipynb | 12 +- notebooks/api/0.8/09-blob-storage.ipynb | 16 +- notebooks/api/0.8/10-container-images.ipynb | 98 +- .../api/0.8/11-container-images-k8s.ipynb | 94 +- .../api/0.8/12-custom-api-endpoint.ipynb | 82 +- ....ipynb => 01-primary-datasite-setup.ipynb} | 0 .../enclave/02-manual-enclave-setup.ipynb | 70 +- ...pynb => 03-secondary-datasite-setup.ipynb} | 0 .../enclave/04-data-scientist-join.ipynb | 96 +- ...review.ipynb => 05-datasites-review.ipynb} | 0 .../enclave/07-audit-project-logs.ipynb | 66 +- ...in.ipynb => 03-configuring-datasite.ipynb} | 0 ...nb => 04-setup-datasite-with-tunnel.ipynb} | 0 .../reverse-tunnel/06-proxy-clients.ipynb | 66 +- .../01-uploading-private-data.ipynb | 4 +- .../data-owner/02-account-management.ipynb | 2 +- .../data-owner/03-messages-and-requests.ipynb | 2 +- .../data-owner/05-syft-services-api.ipynb | 2 +- .../data-scientist/02-finding-datasets.ipynb | 2 +- .../03-working-with-private-datasets.ipynb | 2 +- .../data-scientist/04-syft-functions.ipynb | 8 +- .../05-messaging-and-requests.ipynb | 2 +- .../deployments/01-deploy-python.ipynb | 16 +- .../deployments/02-deploy-container.ipynb | 16 +- .../deployments/03-deploy-k8s-k3d.ipynb | 10 +- .../deployments/07-deploy-devspace.ipynb | 6 +- .../tutorials/hello-syft/01-hello-syft.ipynb | 12 +- .../model-auditing/colab/01-user-log.ipynb | 20 +- .../00-data-owner-upload-data.ipynb | 4 +- .../01-data-scientist-submit-code.ipynb | 6 +- .../02-data-owner-review-approve-code.ipynb | 2 +- .../03-data-scientist-download-results.ipynb | 2 +- .../01-reading-from-a-csv.ipynb | 24 +- ...lecting-data-finding-common-complain.ipynb | 26 +- ...orough-has-the-most-noise-complaints.ipynb | 24 +- ...-weekday-bike-most-groupby-aggregate.ipynb | 20 +- ...ing-dataframes-scraping-weather-data.ipynb | 24 +- ...rations-which-month-was-the-snowiest.ipynb | 20 +- .../07-cleaning-up-messy-data.ipynb | 18 +- .../08-how-to-deal-with-timestamps.ipynb | 20 +- packages/grid/backend/backend.dockerfile | 2 +- packages/grid/backend/grid/core/config.py | 6 +- packages/grid/backend/grid/core/node.py | 6 +- packages/grid/backend/grid/start.sh | 2 +- packages/grid/default.env | 8 +- packages/grid/devspace.yaml | 16 +- packages/grid/frontend/README.md | 2 +- .../src/_routes/(app)/account/+page.svelte | 10 +- .../src/_routes/(app)/config/+page.svelte | 18 +- .../src/_routes/(app)/users/+page.svelte | 2 +- .../(app)/users/[slug]/+page.server.ts | 2 +- .../_routes/(app)/users/[slug]/+page.svelte | 2 +- .../src/_routes/(auth)/login/+page.svelte | 14 +- .../src/_routes/(auth)/signup/+page.svelte | 4 +- .../src/lib/components/AccountSettings.svelte | 4 +- .../Datasets/DatasetListItem.svelte | 2 +- .../Datasets/DatasetNoneFound.svelte | 2 +- ....svelte => DatasiteOnlineIndicator.svelte} | 4 +- .../Navigation/SideNavDOHandbook.svelte | 2 +- .../lib/components/Navigation/TopNav.svelte | 4 +- .../src/lib/components/OnboardingModal.svelte | 36 +- .../components/Users/UserCreateModal.svelte | 10 +- .../lib/components/Users/UserListItem.svelte | 2 +- ...el.svelte => DatasiteMetadataPanel.svelte} | 14 +- packages/grid/frontend/src/lib/utils.ts | 2 +- .../frontend/src/routes/[...all]/+page.svelte | 4 +- .../src/types/{domain => datasite}/dataset.ts | 0 .../types/{domain => datasite}/metadata.ts | 2 +- .../src/types/datasite/onlineIndicator.ts | 1 + .../src/types/{domain => datasite}/syft.ts | 0 .../src/types/{domain => datasite}/users.ts | 0 .../src/types/domain/onlineIndicator.ts | 1 - .../frontend/tests/e2e/metadata-panel.spec.ts | 4 +- ...omain.tunnel.yaml => datasite.tunnel.yaml} | 2 +- .../backend/backend-statefulset.yaml | 2 +- packages/grid/helm/syft/values.yaml | 4 +- packages/grid/quickstart/template.json | 30 +- packages/grid/scripts/helm_upgrade.sh | 6 +- packages/syft/PYPI.md | 22 +- packages/syft/src/syft/__init__.py | 12 +- packages/syft/src/syft/abstract_node.py | 2 +- packages/syft/src/syft/client/api.py | 2 +- packages/syft/src/syft/client/client.py | 12 +- .../{domain_client.py => datasite_client.py} | 4 +- .../syft/src/syft/client/gateway_client.py | 18 +- packages/syft/src/syft/client/registry.py | 86 +- packages/syft/src/syft/client/search.py | 12 +- packages/syft/src/syft/client/syncing.py | 10 +- .../src/syft/node/{domain.py => datasite.py} | 2 +- packages/syft/src/syft/node/node.py | 6 +- packages/syft/src/syft/node/routes.py | 4 +- packages/syft/src/syft/node/server.py | 8 +- packages/syft/src/syft/orchestra.py | 6 +- .../src/syft/protocol/protocol_version.json | 1162 +++++++++++++---- .../src/syft/service/action/action_object.py | 2 +- .../syft/src/syft/service/action/numpy.py | 4 +- .../syft/src/syft/service/code/code_parse.py | 2 +- .../syft/src/syft/service/code/user_code.py | 30 +- .../syft/service/code/user_code_service.py | 4 +- packages/syft/src/syft/service/code/utils.py | 2 +- .../syft/service/metadata/node_metadata.py | 8 +- .../syft/service/network/network_service.py | 2 +- .../service/notification/email_templates.py | 4 +- .../syft/service/notifier/notifier_service.py | 6 +- .../syft/src/syft/service/policy/policy.py | 6 +- .../syft/src/syft/service/request/request.py | 4 +- .../src/syft/service/settings/settings.py | 6 +- .../syft/service/settings/settings_service.py | 2 +- .../syft/src/syft/service/sync/diff_state.py | 4 +- .../syft/src/syft/service/user/user_roles.py | 2 +- .../src/syft/service/user/user_service.py | 2 +- .../src/syft/service/worker/image_registry.py | 8 +- packages/syft/src/syft/util/schema.py | 4 +- packages/syft/tests/conftest.py | 10 +- packages/syft/tests/syft/action_test.py | 28 +- packages/syft/tests/syft/api_test.py | 10 +- .../syft/blob_storage/blob_storage_test.py | 6 +- packages/syft/tests/syft/dataset/fixtures.py | 6 +- packages/syft/tests/syft/eager_test.py | 70 +- packages/syft/tests/syft/grid_url_test.py | 4 +- packages/syft/tests/syft/request/fixtures.py | 6 +- .../tests/syft/request/request_stash_test.py | 22 +- .../syft/service/action/action_object_test.py | 16 +- .../service/action/action_service_test.py | 4 +- .../service/dataset/dataset_service_test.py | 24 +- .../service/sync/sync_resolve_single_test.py | 24 +- .../tests/syft/service_permission_test.py | 20 +- packages/syft/tests/syft/settings/fixtures.py | 4 +- .../syft/settings/settings_service_test.py | 18 +- .../tests/syft/users/local_execution_test.py | 10 +- .../syft/tests/syft/users/user_code_test.py | 96 +- .../syft/tests/syft/users/user_stash_test.py | 73 +- packages/syft/tests/syft/users/user_test.py | 60 +- .../worker_pool/worker_pool_service_test.py | 4 +- scripts/get_k8s_secret_ci.sh | 2 +- scripts/patch_hosts.py | 34 +- tests/integration/conftest.py | 4 +- .../container_workload/blob_storage_test.py | 10 +- .../container_workload/pool_image_test.py | 80 +- .../frontend/frontend_start_test.py | 6 +- tests/integration/local/gateway_local_test.py | 122 +- tests/integration/local/job_test.py | 6 +- tests/integration/local/syft_function_test.py | 6 +- tests/integration/local/twin_api_sync_test.py | 14 +- tests/integration/network/client_test.py | 6 +- tests/integration/network/gateway_test.py | 565 ++++---- tests/integration/orchestra/orchestra_test.py | 4 +- tox.ini | 80 +- 167 files changed, 2517 insertions(+), 2336 deletions(-) rename docs/source/_static/personas-image/data-owner/{00-deploy-domain-00.gif => 00-deploy-datasite-00.gif} (100%) rename docs/source/_static/personas-image/data-owner/{00-deploy-domain-01.jpg => 00-deploy-datasite-01.jpg} (100%) rename docs/source/api_reference/{syft.node.domain.rst => syft.node.datasite.rst} (64%) delete mode 100644 notebooks/admin/Custom API + Custom Worker.ipynb rename notebooks/api/0.8/{07-domain-register-control-flow.ipynb => 07-datasite-register-control-flow.ipynb} (95%) rename notebooks/scenarios/enclave/{01-primary-domain-setup.ipynb => 01-primary-datasite-setup.ipynb} (100%) rename notebooks/scenarios/enclave/{03-secondary-domain-setup.ipynb => 03-secondary-datasite-setup.ipynb} (100%) rename notebooks/scenarios/enclave/{05-domains-review.ipynb => 05-datasites-review.ipynb} (100%) rename notebooks/scenarios/getting-started/{03-configuring-domain.ipynb => 03-configuring-datasite.ipynb} (100%) rename notebooks/scenarios/reverse-tunnel/{04-setup-domain-with-tunnel.ipynb => 04-setup-datasite-with-tunnel.ipynb} (100%) rename packages/grid/frontend/src/lib/components/{DomainOnlineIndicator.svelte => DatasiteOnlineIndicator.svelte} (68%) rename packages/grid/frontend/src/lib/components/authentication/{DomainMetadataPanel.svelte => DatasiteMetadataPanel.svelte} (76%) rename packages/grid/frontend/src/types/{domain => datasite}/dataset.ts (100%) rename packages/grid/frontend/src/types/{domain => datasite}/metadata.ts (88%) create mode 100644 packages/grid/frontend/src/types/datasite/onlineIndicator.ts rename packages/grid/frontend/src/types/{domain => datasite}/syft.ts (100%) rename packages/grid/frontend/src/types/{domain => datasite}/users.ts (100%) delete mode 100644 packages/grid/frontend/src/types/domain/onlineIndicator.ts rename packages/grid/helm/examples/dev/{domain.tunnel.yaml => datasite.tunnel.yaml} (67%) rename packages/syft/src/syft/client/{domain_client.py => datasite_client.py} (99%) rename packages/syft/src/syft/node/{domain.py => datasite.py} (85%) diff --git a/.github/workflows/pr-tests-stack.yml b/.github/workflows/pr-tests-stack.yml index eae0b555c3e..f593ca1c0e7 100644 --- a/.github/workflows/pr-tests-stack.yml +++ b/.github/workflows/pr-tests-stack.yml @@ -252,9 +252,9 @@ jobs: run: | mkdir -p ./k8s-logs kubectl describe all -A --context k3d-test-gateway-1 --namespace syft > ./k8s-logs/test-gateway-1-desc-${{ steps.date.outputs.date }}.txt - kubectl describe all -A --context k3d-test-domain-1 --namespace syft > ./k8s-logs/test-domain-1-desc-${{ steps.date.outputs.date }}.txt + kubectl describe all -A --context k3d-test-datasite-1 --namespace syft > ./k8s-logs/test-datasite-1-desc-${{ steps.date.outputs.date }}.txt kubectl logs -l app.kubernetes.io/name!=random --prefix=true --context k3d-test-gateway-1 --namespace syft > ./k8s-logs/test-gateway-1-logs-${{ steps.date.outputs.date }}.txt - kubectl logs -l app.kubernetes.io/name!=random --prefix=true --context k3d-test-domain-1 --namespace syft > ./k8s-logs/test-domain-1-logs-${{ steps.date.outputs.date }}.txt + kubectl logs -l app.kubernetes.io/name!=random --prefix=true --context k3d-test-datasite-1 --namespace syft > ./k8s-logs/test-datasite-1-logs-${{ steps.date.outputs.date }}.txt ls -la ./k8s-logs - name: Upload logs to GitHub @@ -270,7 +270,7 @@ jobs: run: | export PATH=`pwd`:$PATH k3d cluster delete test-gateway-1 || true - k3d cluster delete test-domain-1 || true + k3d cluster delete test-datasite-1 || true k3d registry delete k3d-registry.localhost || true pr-tests-notebook-k8s: @@ -395,9 +395,9 @@ jobs: run: | mkdir -p ./k8s-logs kubectl describe all -A --context k3d-test-gateway-1 --namespace syft > ./k8s-logs/test-gateway-1-desc-${{ steps.date.outputs.date }}.txt - kubectl describe all -A --context k3d-test-domain-1 --namespace syft > ./k8s-logs/test-domain-1-desc-${{ steps.date.outputs.date }}.txt + kubectl describe all -A --context k3d-test-datasite-1 --namespace syft > ./k8s-logs/test-datasite-1-desc-${{ steps.date.outputs.date }}.txt kubectl logs -l app.kubernetes.io/name!=random --prefix=true --context k3d-test-gateway-1 --namespace syft > ./k8s-logs/test-gateway-1-logs-${{ steps.date.outputs.date }}.txt - kubectl logs -l app.kubernetes.io/name!=random --prefix=true --context k3d-test-domain-1 --namespace syft > ./k8s-logs/test-domain-1-logs-${{ steps.date.outputs.date }}.txt + kubectl logs -l app.kubernetes.io/name!=random --prefix=true --context k3d-test-datasite-1 --namespace syft > ./k8s-logs/test-datasite-1-logs-${{ steps.date.outputs.date }}.txt ls -la ./k8s-logs - name: Upload logs to GitHub @@ -413,5 +413,5 @@ jobs: run: | export PATH=`pwd`:$PATH k3d cluster delete test-gateway-1 || true - k3d cluster delete test-domain-1 || true + k3d cluster delete test-datasite-1 || true k3d registry delete k3d-registry.localhost || true diff --git a/README.md b/README.md index 8c602a27f0f..6c650e86a2c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ $ pip install -U syft[data_science] import syft as sy sy.requires(">=0.8.6,<0.8.7") node = sy.orchestra.launch( - name="my-domain", + name="my-datasite", port=8080, create_producer=True, n_consumers=1, @@ -36,7 +36,7 @@ node = sy.orchestra.launch( ```bash # or from the command line -$ syft launch --name=my-domain --port=8080 --reset=True +$ syft launch --name=my-datasite --port=8080 --reset=True Starting syft-node server on 0.0.0.0:8080 ``` @@ -46,7 +46,7 @@ Starting syft-node server on 0.0.0.0:8080 ```python import syft as sy sy.requires(">=0.8.6,<0.8.7") -domain_client = sy.login( +datasite_client = sy.login( port=8080, email="info@openmined.org", password="changethis" @@ -64,7 +64,7 @@ domain_client = sy.login( - 04-pytorch-example.ipynb - 05-custom-policy.ipynb - 06-multiple-code-requests.ipynb -- 07-domain-register-control-flow.ipynb +- 07-datasite-register-control-flow.ipynb - 08-code-version.ipynb - 09-blob-storage.ipynb - 10-container-images.ipynb @@ -104,7 +104,7 @@ SYFT_VERSION="" #### 4. Provisioning Helm Charts ```sh -helm install my-domain openmined/syft --version $SYFT_VERSION --namespace syft --create-namespace --set ingress.className="traefik" +helm install my-datasite openmined/syft --version $SYFT_VERSION --namespace syft --create-namespace --set ingress.className="traefik" ``` ### Ingress Controllers @@ -179,9 +179,9 @@ PySyft (Beta): `pip install -U syft --pre` ### Why should I use Syft? -`Syft` allows a `Data Scientist` to ask `questions` about a `dataset` and, within `privacy limits` set by the `data owner`, get `answers` to those `questions`, all without obtaining a `copy` of the data itself. We call this process `Remote Data Science`. It means in a wide variety of `domains` across society, the current `risks` of sharing information (`copying` data) with someone such as, privacy invasion, IP theft and blackmail will no longer prevent the vast `benefits` such as innovation, insights and scientific discovery which secure access will provide. +`Syft` allows a `Data Scientist` to ask `questions` about a `dataset` and, within `privacy limits` set by the `data owner`, get `answers` to those `questions`, all without obtaining a `copy` of the data itself. We call this process `Remote Data Science`. It means in a wide variety of `datasites` across society, the current `risks` of sharing information (`copying` data) with someone such as, privacy invasion, IP theft and blackmail will no longer prevent the vast `benefits` such as innovation, insights and scientific discovery which secure access will provide. -No more cold calls to get `access` to a dataset. No more weeks of `wait times` to get a `result` on your `query`. It also means `1000x more data` in every domain. PySyft opens the doors to a streamlined Data Scientist `workflow`, all with the individual's `privacy` at its heart. +No more cold calls to get `access` to a dataset. No more weeks of `wait times` to get a `result` on your `query`. It also means `1000x more data` in every datasite. PySyft opens the doors to a streamlined Data Scientist `workflow`, all with the individual's `privacy` at its heart. -Provides services to a group of `Data Owners` and `Data Scientists`, such as dataset `search` and bulk `project approval` (legal / technical) to participate in a project. A gateway server acts as a bridge between it's members (`Domains`) and their subscribers (`Data Scientists`) and can provide access to a collection of `domains` at once. +Provides services to a group of `Data Owners` and `Data Scientists`, such as dataset `search` and bulk `project approval` (legal / technical) to participate in a project. A gateway server acts as a bridge between it's members (`Datasites`) and their subscribers (`Data Scientists`) and can provide access to a collection of `datasites` at once. diff --git a/docs/source/_static/personas-image/data-owner/00-deploy-domain-00.gif b/docs/source/_static/personas-image/data-owner/00-deploy-datasite-00.gif similarity index 100% rename from docs/source/_static/personas-image/data-owner/00-deploy-domain-00.gif rename to docs/source/_static/personas-image/data-owner/00-deploy-datasite-00.gif diff --git a/docs/source/_static/personas-image/data-owner/00-deploy-domain-01.jpg b/docs/source/_static/personas-image/data-owner/00-deploy-datasite-01.jpg similarity index 100% rename from docs/source/_static/personas-image/data-owner/00-deploy-domain-01.jpg rename to docs/source/_static/personas-image/data-owner/00-deploy-datasite-01.jpg diff --git a/docs/source/api_reference/syft.client.registry.rst b/docs/source/api_reference/syft.client.registry.rst index 57f0136d312..9b2987bfb78 100644 --- a/docs/source/api_reference/syft.client.registry.rst +++ b/docs/source/api_reference/syft.client.registry.rst @@ -17,7 +17,7 @@ syft.client.registry .. autosummary:: - DomainRegistry + DatasiteRegistry NetworkRegistry diff --git a/docs/source/api_reference/syft.node.domain.rst b/docs/source/api_reference/syft.node.datasite.rst similarity index 64% rename from docs/source/api_reference/syft.node.domain.rst rename to docs/source/api_reference/syft.node.datasite.rst index eb4ff6334a5..bb6b582d067 100644 --- a/docs/source/api_reference/syft.node.domain.rst +++ b/docs/source/api_reference/syft.node.datasite.rst @@ -1,7 +1,7 @@ -syft.node.domain +syft.node.datasite ================ -.. automodule:: syft.node.domain +.. automodule:: syft.node.datasite @@ -17,7 +17,7 @@ syft.node.domain .. autosummary:: - Domain + Datasite diff --git a/docs/source/api_reference/syft.node.rst b/docs/source/api_reference/syft.node.rst index a94e20d906d..d2850a28469 100644 --- a/docs/source/api_reference/syft.node.rst +++ b/docs/source/api_reference/syft.node.rst @@ -28,7 +28,7 @@ :recursive: syft.node.credentials - syft.node.domain + syft.node.datasite syft.node.gateway syft.node.node syft.node.routes diff --git a/docs/source/deployment/glossary.rst b/docs/source/deployment/glossary.rst index a257c3ca4f8..68df75e3c9f 100644 --- a/docs/source/deployment/glossary.rst +++ b/docs/source/deployment/glossary.rst @@ -12,20 +12,20 @@ General terms Data Consortium ~~~~~~~~~~~~~~~~~~~~~ -A legal agreement under which multiple data owners delegate legal authority (IRB authority) to a central party, such that a data scientist need only enter into legal contract with that central party in order to perform analysis across all relevant participating domains in the ``data consortium``. +A legal agreement under which multiple data owners delegate legal authority (IRB authority) to a central party, such that a data scientist need only enter into legal contract with that central party in order to perform analysis across all relevant participating datasites in the ``data consortium``. Differential Privacy ~~~~~~~~~~~~~~~~~~~~~ While the textbook definition can be found here_, within the context of remote data science, ``differential privacy`` is a set of algorithms which empower a data owner to limit the probability that a data scientist will be able to use their statistical results to reverse engineer the data owner's def. of too much information about the underlying data that generated those results. In a nutshell, its aim is to prevent a Data Scientist from identifying any individual from the dataset through reverse-engineering. -Domain Node +Datasite Node ~~~~~~~~~~~~~~~~~~~~~ A ``computer system`` (or collection of computer systems) which manages the remote study of a data owner's data by a data scientist. It is responsible for allowing the `Data Owner` to manage the data, as well as incoming ``requests`` from data scientists and for gatekeeping the data scientist's access to data, compute, and experimental results stored within the data owner's compute infrastructure. Network Node ~~~~~~~~~~~~~~~~~~~~~ -A server which exists outside of any data owner's institution, providing services to the network of data owners and data scientists such as dataset search and bulk project approval (simultaneous legal/technical approval to participate in a project across groups of domains and data scientists at a time). A Network acts as a bridge between between its members and subscribers. The members are ``Domains`` while subscribers are the ``end users`` (e.g. Data Scientist) who explore and perform analysis on the datasets hosted by the members. -A network is used to provide access to a collection of domains at once i.e. if a user agrees to a ``Network Agreement``, then they automatically agree to the conditions to the Domains enlisted in that Network. +A server which exists outside of any data owner's institution, providing services to the network of data owners and data scientists such as dataset search and bulk project approval (simultaneous legal/technical approval to participate in a project across groups of datasites and data scientists at a time). A Network acts as a bridge between between its members and subscribers. The members are ``Datasites`` while subscribers are the ``end users`` (e.g. Data Scientist) who explore and perform analysis on the datasets hosted by the members. +A network is used to provide access to a collection of datasites at once i.e. if a user agrees to a ``Network Agreement``, then they automatically agree to the conditions to the Datasites enlisted in that Network. Privacy Budget ~~~~~~~~~~~~~~~~~~~~~ @@ -54,25 +54,25 @@ Data Owner ~~~~~~~~~~~~~~~~~~~~~ Within the field of remote data science, a data owner is someone who has a (digital) dataset which they would like to make available for study by an outside party whom they may or may not fully trust to have good intentions. -Domain Owner +Datasite Owner ~~~~~~~~~~~~~~~~~~~~~ -A user of ``PyGrid`` who has deployed a domain node. +A user of ``PyGrid`` who has deployed a datasite node. Network Owner ~~~~~~~~~~~~~~~~~~~~~ -Within the field of remote data science, a network owner provides technical and legal services helping to connect data scientists with data owners (domains) by helping them find each other (dataset search) and by helping them enter into bulk legal agreements through the hosting of a network-level data consortium to which such data owners and data scientist may apply. +Within the field of remote data science, a network owner provides technical and legal services helping to connect data scientists with data owners (datasites) by helping them find each other (dataset search) and by helping them enter into bulk legal agreements through the hosting of a network-level data consortium to which such data owners and data scientist may apply. Data Scientist ~~~~~~~~~~~~~~~~~~~~~ -Within the context of remote data science, a data scientist is a persona which desires to answer a specific question using data owned by someone else. This user is required to sign a ``Data Access Agreement`` if you have required one in the ``Domain Settings Configurations``. +Within the context of remote data science, a data scientist is a persona which desires to answer a specific question using data owned by someone else. This user is required to sign a ``Data Access Agreement`` if you have required one in the ``Datasite Settings Configurations``. -Domain Compliance Officer +Datasite Compliance Officer ~~~~~~~~~~~~~~~~~~~~~~~~~~~ All the personas in an institution that are in charge of making sure that the utilization of data at an institution occurs within legal boundaries and under their supervision and with their liability/responsibility. Network Compliance Officer ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -All the personas in an institution that are in charge of making sure that the access and utilization of data between their network's domains and members (data scientists) fall within the bounds outlined in the network's legal agreements. +All the personas in an institution that are in charge of making sure that the access and utilization of data between their network's datasites and members (data scientists) fall within the bounds outlined in the network's legal agreements. User roles ============ @@ -82,7 +82,7 @@ Default roles Data Scientist """""""""""""" -This role is for users who will be performing computations on your datasets. They may be users you know directly or those who found your domain through search and discovery. By default this user can see a ``list of your datasets`` and can request to get results. This user will also be required to sign a ``Data Access Agreement`` if you have required one in the ``Domain Settings Configurations``. +This role is for users who will be performing computations on your datasets. They may be users you know directly or those who found your datasite through search and discovery. By default this user can see a ``list of your datasets`` and can request to get results. This user will also be required to sign a ``Data Access Agreement`` if you have required one in the ``Datasite Settings Configurations``. Default permissions: @@ -90,7 +90,7 @@ Default permissions: Compliance Officer """""""""""""""""""" -This role is for users who will help you manage requests made on your node. They should be users you trust. They are not able to change ``Domain Settings`` or edit roles but they are by default able to accept or deny ``user requests`` on behalf of the ``domain node``. +This role is for users who will help you manage requests made on your node. They should be users you trust. They are not able to change ``Datasite Settings`` or edit roles but they are by default able to accept or deny ``user requests`` on behalf of the ``datasite node``. Default permissions: @@ -100,7 +100,7 @@ Default permissions: Admin """""" -This role is for users who will help you manage your node. This should be users you trust. The main difference between this ``user`` and a ``Compliance Officer`` is that this user by default not only can manage requests but can also edit ``Domain Settings.`` This is the highest level permission outside of an Owner. +This role is for users who will help you manage your node. This should be users you trust. The main difference between this ``user`` and a ``Compliance Officer`` is that this user by default not only can manage requests but can also edit ``Datasite Settings.`` This is the highest level permission outside of an Owner. Default permissions: @@ -108,7 +108,7 @@ Default permissions: Owner """""""" -There is only one Owner account assigned to any one domain node. The owner account is the highest level permission and is a requirement for deploying a domain node. If you should ever want to transfer ownership of your domain node to someone, please contact us at support@openmined.org. +There is only one Owner account assigned to any one datasite node. The owner account is the highest level permission and is a requirement for deploying a datasite node. If you should ever want to transfer ownership of your datasite node to someone, please contact us at support@openmined.org. Default permissions: @@ -116,16 +116,16 @@ Default permissions: * Cannot disable permissions by default -Domain membership roles +Datasite membership roles ~~~~~~~~~~~~~~~~~~~~~~~~~~ Guest """""""""""""" -The lowest level of ``network membership``, a guest domain is listed within a network node's registry and its datasets are searchable/discoverable by all users of the network, but the network has no legal relationship to the domain nor any authority to grant data scientists access to its data. As such, upon discovering a domain on the network, such a data scientist must apply directly to the domain for access by creating an account on such a domain and signing a legal agreement (a "data-sharing agreement") directly with its corresponding data owner. +The lowest level of ``network membership``, a guest datasite is listed within a network node's registry and its datasets are searchable/discoverable by all users of the network, but the network has no legal relationship to the datasite nor any authority to grant data scientists access to its data. As such, upon discovering a datasite on the network, such a data scientist must apply directly to the datasite for access by creating an account on such a datasite and signing a legal agreement (a "data-sharing agreement") directly with its corresponding data owner. Member """""""""""""" -The highest level of ``network membership``, a full domain member is greater than a guest member because, beyond its listing within a network node's registry, the domain has entered into a legal relationship with the network owner such that the network owner can unilaterally give its full data scientists access to data hosted by the domain. Note that this does not mean that the network can control access to all potential users of the ``registered domain``, because the domain's membership in the network is non-exclusive (domains can register in multiple networks and also accept direct data-scientist users on the side). A network node only has authority to give its own full data scientists access to any full domain within its registry. +The highest level of ``network membership``, a full datasite member is greater than a guest member because, beyond its listing within a network node's registry, the datasite has entered into a legal relationship with the network owner such that the network owner can unilaterally give its full data scientists access to data hosted by the datasite. Note that this does not mean that the network can control access to all potential users of the ``registered datasite``, because the datasite's membership in the network is non-exclusive (datasites can register in multiple networks and also accept direct data-scientist users on the side). A network node only has authority to give its own full data scientists access to any full datasite within its registry. .. |image0| image:: ../_static/deployment/image2.png :width: 95% diff --git a/docs/source/getting_started/index.rst b/docs/source/getting_started/index.rst index 4633130e472..c6c7cee9873 100644 --- a/docs/source/getting_started/index.rst +++ b/docs/source/getting_started/index.rst @@ -7,9 +7,9 @@ Getting Started .. toctree:: :maxdepth: 3 -Welcome to the domain deployment installation tutorials! +Welcome to the datasite deployment installation tutorials! This section of our documentation is designed to be the -simplest way to get you started deploying your data to a domain node +simplest way to get you started deploying your data to a datasite node on an OSX, Linux, or Windows machine and interacting with it as a data scientist using PySyft. @@ -20,7 +20,7 @@ as a data scientist using PySyft. `advanced deployment documentation `__. The purpose of these tutorials is to help you install everything -you need to run a Domain node from your personal machine (such +you need to run a Datasite node from your personal machine (such as if you're running through OpenMined `courses `__ or diff --git a/docs/source/guides/index.rst b/docs/source/guides/index.rst index ff7da37d0b1..7f60ef9e966 100644 --- a/docs/source/guides/index.rst +++ b/docs/source/guides/index.rst @@ -34,12 +34,12 @@ an ``outside party`` they may or may not ``fully trust`` has good intentions. You Will Learn ⬇️ """""""""""""""""""" -| :doc:`Part 1: Deploying your own Domain Server ` -| :doc:`Part 2: Uploading Private Data to a Domain Server ` -| :doc:`Part 3: Creating User Accounts on your Domain Server ` +| :doc:`Part 1: Deploying your own Datasite Server ` +| :doc:`Part 2: Uploading Private Data to a Datasite Server ` +| :doc:`Part 3: Creating User Accounts on your Datasite Server ` | :doc:`Part 4: Joining a Network ` | :doc:`Part 5: Creating a Network <04-create-network>` -| :doc:`Part 6: Configuring Privacy Budget on your Domain Server <05-configure-pb>` +| :doc:`Part 6: Configuring Privacy Budget on your Datasite Server <05-configure-pb>` B. Getting Started with Data Scientist 👩🏽‍🔬 @@ -50,9 +50,9 @@ specific ``question`` using one or more data owners' ``datasets``. You Will Learn ⬇️ """""""""""""""""""" -| :doc:`Part 7: Connect to a Domain` -| :doc:`Part 8: Searching for Datasets on the Domain` -| :doc:`Part 9: Exploring a Dataset in the Domain` +| :doc:`Part 7: Connect to a Datasite` +| :doc:`Part 8: Searching for Datasets on the Datasite` +| :doc:`Part 9: Exploring a Dataset in the Datasite` | :doc:`Part 10: Training a Model` | :doc:`Part 11: Retrieving Secure Results <>` diff --git a/notebooks/admin/Custom API + Custom Worker.ipynb b/notebooks/admin/Custom API + Custom Worker.ipynb deleted file mode 100644 index d50c1f1b4f2..00000000000 --- a/notebooks/admin/Custom API + Custom Worker.ipynb +++ /dev/null @@ -1,523 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "0", - "metadata": {}, - "source": [ - "## Custom API + Custom Worker" - ] - }, - { - "cell_type": "markdown", - "id": "1", - "metadata": {}, - "source": [ - "#### Import dependencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2", - "metadata": {}, - "outputs": [], - "source": [ - "# syft absolute\n", - "import syft as sy\n", - "from syft.service.settings.settings import NodeSettingsUpdate\n", - "from syft.service.worker.worker_image import SyftWorkerImage" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3", - "metadata": {}, - "outputs": [], - "source": [ - "## remote mode\n", - "# os.environ[\"ORCHESTRA_DEPLOYMENT_TYPE\"] = \"remote\"\n", - "# os.environ[\"DEV_MODE\"] = \"True\"\n", - "domain_client = sy.login(\n", - " email=\"info@openmined.org\",\n", - " url=\"http://127.0.0.1\",\n", - " password=\"\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4", - "metadata": {}, - "outputs": [], - "source": [ - "# # python mode\n", - "# # !uv pip install google-cloud-bigquery db_dtypes\n", - "# node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True, reset=True)\n", - "# domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5", - "metadata": {}, - "outputs": [], - "source": [ - "domain_client.worker_pools" - ] - }, - { - "cell_type": "markdown", - "id": "6", - "metadata": {}, - "source": [ - "## Register a custom Image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7", - "metadata": {}, - "outputs": [], - "source": [ - "registry = \"us-central1-docker.pkg.dev/reddit-testing-415005/syft-registry-us\"\n", - "backend_version = None\n", - "assert backend_version is not None\n", - "\n", - "custom_dockerfile_str = f\"\"\"\n", - "FROM {registry}/openmined/grid-backend:{backend_version}\n", - "\n", - "RUN uv pip install google-cloud-bigquery[all]==3.20.1 db-dtypes==1.2.0\n", - "\n", - "\"\"\".strip()\n", - "print(custom_dockerfile_str)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8", - "metadata": {}, - "outputs": [], - "source": [ - "docker_config = sy.DockerWorkerConfig(dockerfile=custom_dockerfile_str)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9", - "metadata": {}, - "outputs": [], - "source": [ - "submit_result = domain_client.api.services.worker_image.submit(\n", - " worker_config=docker_config\n", - ")\n", - "submit_result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "10", - "metadata": {}, - "outputs": [], - "source": [ - "dockerfile_list = domain_client.images.get_all()\n", - "dockerfile_list" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "11", - "metadata": {}, - "outputs": [], - "source": [ - "workerimage = next(\n", - " (\n", - " image\n", - " for image in dockerfile_list\n", - " if not image.is_prebuilt and image.config.dockerfile == custom_dockerfile_str\n", - " ),\n", - " None,\n", - ")\n", - "\n", - "assert isinstance(workerimage, SyftWorkerImage), str(workerimage)\n", - "workerimage" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12", - "metadata": {}, - "outputs": [], - "source": [ - "external_registry = registry\n", - "worker_docker_tag = \"openmined/bigquery:0.0.1\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13", - "metadata": {}, - "outputs": [], - "source": [ - "registry_add_result = domain_client.api.services.image_registry.add(external_registry)\n", - "registry_add_result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "14", - "metadata": {}, - "outputs": [], - "source": [ - "registries = domain_client.api.services.image_registry.get_all()\n", - "registry_uid = next((r.id for r in registries if r.url == external_registry), None)\n", - "registry_uid" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15", - "metadata": {}, - "outputs": [], - "source": [ - "# build with registry_uid\n", - "docker_build_result = domain_client.api.services.worker_image.build(\n", - " image_uid=workerimage.id,\n", - " tag=worker_docker_tag,\n", - " registry_uid=registry_uid,\n", - ")\n", - "print(docker_build_result)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16", - "metadata": {}, - "outputs": [], - "source": [ - "image_list = domain_client.images.get_all()\n", - "# we can also index with string using the repo_with_tag format\n", - "workerimage = next((image for image in image_list if image.id == workerimage.id), None)\n", - "assert workerimage.is_built is True\n", - "workerimage" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17", - "metadata": {}, - "outputs": [], - "source": [ - "push_result = domain_client.api.services.worker_image.push(workerimage.id)\n", - "push_result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18", - "metadata": {}, - "outputs": [], - "source": [ - "worker_pool_name = \"bigquery-pool\"\n", - "domain_client.api.services.worker_pool.launch(\n", - " pool_name=worker_pool_name,\n", - " image_uid=workerimage.id,\n", - " num_workers=1,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "19", - "metadata": {}, - "outputs": [], - "source": [ - "domain_client.worker_pools[1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "20", - "metadata": {}, - "outputs": [], - "source": [ - "new_default_worker_pool = NodeSettingsUpdate(default_worker_pool=worker_pool_name)\n", - "domain_client.settings.update(settings=new_default_worker_pool)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "21", - "metadata": {}, - "outputs": [], - "source": [ - "SERVICE_ACCOUNT = {}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22", - "metadata": {}, - "outputs": [], - "source": [ - "# debug manually\n", - "# from google.oauth2 import service_account\n", - "# from google.cloud import bigquery\n", - "# credentials = service_account.Credentials.from_service_account_info(SERVICE_ACCOUNT)\n", - "# scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/bigquery'])\n", - "# scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/cloud-platform'])\n", - "\n", - "# client = bigquery.Client(\n", - "# credentials=scoped_credentials,\n", - "# location=\"us-west1\",\n", - "# )\n", - "# sql=\"SELECT * FROM reddit-testing-415005.test_1gb.accounts limit 10\"\n", - "# rows = client.query_and_wait(\n", - "# sql\n", - "# )\n", - "# g = sy.ActionObject.from_obj(rows)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "23", - "metadata": {}, - "outputs": [], - "source": [ - "@sy.api_endpoint_method(\n", - " settings={\"credentials\": SERVICE_ACCOUNT, \"project_id\": \"reddit-testing-415005\"}\n", - ")\n", - "def public_function(context, sql: str) -> str:\n", - " # third party\n", - " from google.cloud import bigquery\n", - " from google.oauth2 import service_account\n", - "\n", - " credentials = service_account.Credentials.from_service_account_info(\n", - " context.settings[\"credentials\"]\n", - " )\n", - " scoped_credentials = credentials.with_scopes(\n", - " [\"https://www.googleapis.com/auth/bigquery\"]\n", - " )\n", - " scoped_credentials = credentials.with_scopes(\n", - " [\"https://www.googleapis.com/auth/cloud-platform\"]\n", - " )\n", - "\n", - " client = bigquery.Client(\n", - " credentials=scoped_credentials,\n", - " location=\"us-west1\",\n", - " )\n", - "\n", - " rows = client.query_and_wait(\n", - " sql,\n", - " project=context.settings[\"project_id\"],\n", - " )\n", - "\n", - " return rows\n", - "\n", - "\n", - "@sy.api_endpoint_method(\n", - " settings={\"credentials\": SERVICE_ACCOUNT, \"project_id\": \"reddit-testing-415005\"}\n", - ")\n", - "def private_function(context, sql: str) -> str:\n", - " # third party\n", - " from google.cloud import bigquery\n", - " from google.oauth2 import service_account\n", - "\n", - " credentials = service_account.Credentials.from_service_account_info(\n", - " context.settings[\"credentials\"]\n", - " )\n", - " scoped_credentials = credentials.with_scopes(\n", - " [\"https://www.googleapis.com/auth/bigquery\"]\n", - " )\n", - " scoped_credentials = credentials.with_scopes(\n", - " [\"https://www.googleapis.com/auth/cloud-platform\"]\n", - " )\n", - "\n", - " client = bigquery.Client(\n", - " credentials=scoped_credentials,\n", - " location=\"us-west1\",\n", - " )\n", - "\n", - " rows = client.query_and_wait(\n", - " sql,\n", - " project=context.settings[\"project_id\"],\n", - " )\n", - "\n", - " return rows" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24", - "metadata": {}, - "outputs": [], - "source": [ - "new_endpoint = sy.TwinAPIEndpoint(\n", - " path=\"bigquery.query\",\n", - " mock_function=public_function,\n", - " private_function=private_function,\n", - " description=\"Lore ipsulum ...\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25", - "metadata": {}, - "outputs": [], - "source": [ - "response = domain_client.api.services.api.delete(endpoint_path=\"bigquery.query\")\n", - "response" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "26", - "metadata": {}, - "outputs": [], - "source": [ - "response = domain_client.api.services.api.add(endpoint=new_endpoint)\n", - "response" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "27", - "metadata": {}, - "outputs": [], - "source": [ - "domain_client.refresh()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28", - "metadata": {}, - "outputs": [], - "source": [ - "@sy.syft_function_single_use(\n", - " endpoint=domain_client.api.services.bigquery.query,\n", - " worker_pool_name=worker_pool_name,\n", - ")\n", - "def job_function(endpoint):\n", - " result = endpoint(\n", - " sql=\"SELECT * FROM reddit-testing-415005.test_1gb.accounts limit 10\"\n", - " )\n", - " return result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29", - "metadata": {}, - "outputs": [], - "source": [ - "new_project = sy.Project(\n", - " name=\"My Cool UN Project\",\n", - " description=\"Hi, I want to calculate the trade volume in million's with my cool code.\",\n", - " members=[domain_client],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "30", - "metadata": {}, - "outputs": [], - "source": [ - "result = new_project.create_code_request(job_function, domain_client)\n", - "domain_client.requests[-1].approve()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "31", - "metadata": {}, - "outputs": [], - "source": [ - "domain_client.settings.get().default_worker_pool" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32", - "metadata": {}, - "outputs": [], - "source": [ - "job = domain_client.code.job_function(\n", - " endpoint=domain_client.api.services.bigquery.query, blocking=False\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "33", - "metadata": {}, - "outputs": [], - "source": [ - "job" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34", - "metadata": {}, - "outputs": [], - "source": [ - "domain_client.jobs" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/api/0.8/00-load-data.ipynb b/notebooks/api/0.8/00-load-data.ipynb index ad98a5ac361..1b13340871c 100644 --- a/notebooks/api/0.8/00-load-data.ipynb +++ b/notebooks/api/0.8/00-load-data.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Loading data into Syft Domain Server as a Data Owner\n", + "# Loading data into Syft Datasite Server as a Data Owner\n", "\n", "Welcome to Syft! This tutorial consists of 4 Jupyter notebooks that covers the basics of Syft which includes\n", "* [Uploading a private dataset as a Data Owner](./00-load-data.ipynb)\n", @@ -55,7 +55,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Launch a Syft Domain Server" + "### Launch a Syft Datasite Server" ] }, { @@ -66,8 +66,10 @@ }, "outputs": [], "source": [ - "# Launch a fresh domain server named \"test-domain-1\" in dev mode on the local machine\n", - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True, reset=True)" + "# Launch a fresh datasite server named \"test-datasite-1\" in dev mode on the local machine\n", + "node = sy.orchestra.launch(\n", + " name=\"test-datasite-1\", port=\"auto\", dev_mode=True, reset=True\n", + ")" ] }, { @@ -79,7 +81,7 @@ "outputs": [], "source": [ "# log into the node with default root credentials\n", - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -91,7 +93,7 @@ "outputs": [], "source": [ "# List the available API\n", - "domain_client.api" + "datasite_client.api" ] }, { @@ -114,7 +116,7 @@ "outputs": [], "source": [ "# Check for existing Data Subjects\n", - "data_subjects = domain_client.data_subject_registry.get_all()" + "data_subjects = datasite_client.data_subject_registry.get_all()" ] }, { @@ -206,7 +208,7 @@ "outputs": [], "source": [ "# Adds the data subject and all its members to the registry\n", - "response = domain_client.data_subject_registry.add_data_subject(country)\n", + "response = datasite_client.data_subject_registry.add_data_subject(country)\n", "response" ] }, @@ -230,7 +232,7 @@ "outputs": [], "source": [ "# Lets look at the data subjects added to the data\n", - "data_subjects = domain_client.data_subject_registry.get_all()\n", + "data_subjects = datasite_client.data_subject_registry.get_all()\n", "data_subjects" ] }, @@ -375,7 +377,7 @@ "dataset.add_contributor(\n", " name=\"Andrew Trask\",\n", " email=\"andrew@openmined.org\",\n", - " note=\"Andrew runs this domain and prepared the dataset metadata.\",\n", + " note=\"Andrew runs this datasite and prepared the dataset metadata.\",\n", ")\n", "\n", "dataset.add_contributor(\n", @@ -439,7 +441,7 @@ "ctf.add_contributor(\n", " name=\"Andrew Trask\",\n", " email=\"andrew@openmined.org\",\n", - " note=\"Andrew runs this domain and prepared the asset.\",\n", + " note=\"Andrew runs this datasite and prepared the asset.\",\n", ")" ] }, @@ -539,7 +541,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Upload Syft Dataset to Domain Server" + "### Upload Syft Dataset to Datasite Server" ] }, { @@ -550,7 +552,7 @@ }, "outputs": [], "source": [ - "upload_res = domain_client.upload_dataset(dataset)\n", + "upload_res = datasite_client.upload_dataset(dataset)\n", "upload_res" ] }, @@ -569,8 +571,8 @@ "metadata": {}, "outputs": [], "source": [ - "# We can list all the datasets on the Domain Server by invoking the following\n", - "datasets = domain_client.datasets.get_all()\n", + "# We can list all the datasets on the Datasite Server by invoking the following\n", + "datasets = datasite_client.datasets.get_all()\n", "datasets" ] }, @@ -604,7 +606,7 @@ } }, "source": [ - "### Reading the Syft Dataset from Domain Server\n", + "### Reading the Syft Dataset from Datasite Server\n", "\n", "Following the logical hierarchy of `Dataset`, `Asset`, and its variant, we can read the data as follows" ] @@ -618,7 +620,7 @@ "outputs": [], "source": [ "# Reading the mock dataset\n", - "mock = domain_client.datasets[0].assets[0].mock" + "mock = datasite_client.datasets[0].assets[0].mock" ] }, { @@ -642,7 +644,7 @@ "source": [ "# Reading the real dataset\n", "# NOTE: Private data can be accessed by the Data Owners, but NOT the Data Scientists\n", - "real = domain_client.datasets[0].assets[0].data" + "real = datasite_client.datasets[0].assets[0].data" ] }, { @@ -660,12 +662,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Create a new Data Scientist account on the Domain Server\n", + "### Create a new Data Scientist account on the Datasite Server\n", "\n", "Signup is disabled by default.\n", - "An Admin/DO can enable it by `domain_client.settings.allow_guest_signup(enable=True)`\n", + "An Admin/DO can enable it by `datasite_client.settings.allow_guest_signup(enable=True)`\n", "\n", - "Refer to notebook [07-domain-register-control-flow](./07-domain-register-control-flow.ipynb) for more information." + "Refer to notebook [07-datasite-register-control-flow](./07-datasite-register-control-flow.ipynb) for more information." ] }, { @@ -674,7 +676,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.register(\n", + "datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -692,7 +694,7 @@ }, "outputs": [], "source": [ - "# Cleanup local domain server\n", + "# Cleanup local datasite server\n", "if node.node_type.value == \"python\":\n", " node.land()" ] diff --git a/notebooks/api/0.8/01-submit-code.ipynb b/notebooks/api/0.8/01-submit-code.ipynb index 3d360c88b18..7402e37a69b 100644 --- a/notebooks/api/0.8/01-submit-code.ipynb +++ b/notebooks/api/0.8/01-submit-code.ipynb @@ -50,7 +50,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Launch a Syft Domain Server" + "### Launch a Syft Datasite Server" ] }, { @@ -61,8 +61,8 @@ }, "outputs": [], "source": [ - "# Launch and connect to test-domain-1 server we setup in the previous notebook\n", - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True)" + "# Launch and connect to test-datasite-1 server we setup in the previous notebook\n", + "node = sy.orchestra.launch(name=\"test-datasite-1\", port=\"auto\", dev_mode=True)" ] }, { @@ -80,7 +80,7 @@ }, "outputs": [], "source": [ - "guest_domain_client = node.client" + "guest_datasite_client = node.client" ] }, { @@ -92,7 +92,7 @@ "outputs": [], "source": [ "# Print this to see the few commands that are available for the guest client\n", - "guest_domain_client" + "guest_datasite_client" ] }, { @@ -102,14 +102,14 @@ "outputs": [], "source": [ "# This will return the public credentials of the guest client\n", - "guest_credentials = guest_domain_client.credentials" + "guest_credentials = guest_datasite_client.credentials" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Login into the Domain with Data Scientist credentials that we created in [00-load-data.ipynb](./00-load-data.ipynb) notebook" + "Login into the Datasite with Data Scientist credentials that we created in [00-load-data.ipynb](./00-load-data.ipynb) notebook" ] }, { @@ -120,7 +120,7 @@ }, "outputs": [], "source": [ - "jane_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")\n", + "jane_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")\n", "jane_client" ] }, @@ -139,7 +139,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Explore available Syft Datasets in the Domain Node" + "### Explore available Syft Datasets in the Datasite Node" ] }, { @@ -307,7 +307,7 @@ "\n", " aggregate = 0.0\n", " base_lap = dp.m.make_laplace(\n", - " dp.atom_domain(T=float),\n", + " dp.atom_datasite(T=float),\n", " dp.absolute_distance(T=float),\n", " scale=5.0,\n", " )\n", @@ -338,7 +338,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You can validate your code against the mock data, before submitting it to the Domain Server" + "You can validate your code against the mock data, before submitting it to the Datasite Server" ] }, { @@ -409,7 +409,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Submit your code to the Domain Server\n", + "### Submit your code to the Datasite Server\n", "\n", "We start by creating new Syft Project" ] @@ -481,7 +481,7 @@ }, "outputs": [], "source": [ - "# Once we start the project, it will submit the project along with the code request to the Domain Server\n", + "# Once we start the project, it will submit the project along with the code request to the Datasite Server\n", "project = new_project.send()\n", "project" ] @@ -569,7 +569,7 @@ }, "outputs": [], "source": [ - "# Cleanup local domain server\n", + "# Cleanup local datasite server\n", "\n", "if node.node_type.value == \"python\":\n", " node.land()" diff --git a/notebooks/api/0.8/02-review-code-and-approve.ipynb b/notebooks/api/0.8/02-review-code-and-approve.ipynb index cd84910030b..bae19250dbe 100644 --- a/notebooks/api/0.8/02-review-code-and-approve.ipynb +++ b/notebooks/api/0.8/02-review-code-and-approve.ipynb @@ -45,7 +45,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Login to Syft Domain Server" + "### Login to Syft Datasite Server" ] }, { @@ -56,8 +56,8 @@ }, "outputs": [], "source": [ - "# Launch and connect to test-domain-1 server we setup in the previous notebook\n", - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True)" + "# Launch and connect to test-datasite-1 server we setup in the previous notebook\n", + "node = sy.orchestra.launch(name=\"test-datasite-1\", port=\"auto\", dev_mode=True)" ] }, { @@ -69,16 +69,16 @@ "outputs": [], "source": [ "# Log into the node with default root credentials\n", - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Selecting Project in the Syft Domain Server\n", + "### Selecting Project in the Syft Datasite Server\n", "\n", - "Let's see all the projects that are created by Data Scientists in this Domain Server" + "Let's see all the projects that are created by Data Scientists in this Datasite Server" ] }, { @@ -89,7 +89,7 @@ }, "outputs": [], "source": [ - "domain_client.projects" + "datasite_client.projects" ] }, { @@ -101,7 +101,7 @@ "outputs": [], "source": [ "# Select the project you want to work with\n", - "project = domain_client.projects[0]\n", + "project = datasite_client.projects[0]\n", "project" ] }, @@ -471,7 +471,7 @@ }, "outputs": [], "source": [ - "# Cleanup local domain server\n", + "# Cleanup local datasite server\n", "\n", "if node.node_type.value == \"python\":\n", " node.land()" diff --git a/notebooks/api/0.8/03-data-scientist-download-result.ipynb b/notebooks/api/0.8/03-data-scientist-download-result.ipynb index 81ce4f783fc..1e8ce2427f6 100644 --- a/notebooks/api/0.8/03-data-scientist-download-result.ipynb +++ b/notebooks/api/0.8/03-data-scientist-download-result.ipynb @@ -43,7 +43,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Login to Syft Domain Server" + "### Login to Syft Datasite Server" ] }, { @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"test-domain-1\", dev_mode=True)" + "node = sy.orchestra.launch(name=\"test-datasite-1\", dev_mode=True)" ] }, { @@ -65,7 +65,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "datasite_client = node.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -86,7 +86,7 @@ "outputs": [], "source": [ "# Get the canada_trade_flow asset from the Canada Trade dataset\n", - "asset = domain_client.datasets[0].assets[0]\n", + "asset = datasite_client.datasets[0].assets[0]\n", "asset" ] }, @@ -103,7 +103,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.code.sum_trade_value_mil" + "datasite_client.code.sum_trade_value_mil" ] }, { @@ -121,7 +121,7 @@ }, "outputs": [], "source": [ - "result_pointer = domain_client.code.sum_trade_value_mil(trade_data=asset)\n", + "result_pointer = datasite_client.code.sum_trade_value_mil(trade_data=asset)\n", "result_pointer" ] }, @@ -161,7 +161,7 @@ }, "outputs": [], "source": [ - "ops = domain_client.code[-1].output_policy\n", + "ops = datasite_client.code[-1].output_policy\n", "ops" ] }, @@ -171,7 +171,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.code" + "datasite_client.code" ] }, { @@ -201,7 +201,7 @@ }, "outputs": [], "source": [ - "# Cleanup local domain\n", + "# Cleanup local datasite\n", "\n", "if node.node_type.value == \"python\":\n", " node.land()" diff --git a/notebooks/api/0.8/04-pytorch-example.ipynb b/notebooks/api/0.8/04-pytorch-example.ipynb index 5cbc89ecaea..bd0e6d9f00a 100644 --- a/notebooks/api/0.8/04-pytorch-example.ipynb +++ b/notebooks/api/0.8/04-pytorch-example.ipynb @@ -43,7 +43,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"test-domain-1\", dev_mode=True, reset=True)" + "node = sy.orchestra.launch(name=\"test-datasite-1\", dev_mode=True, reset=True)" ] }, { @@ -55,7 +55,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -128,8 +128,8 @@ }, "outputs": [], "source": [ - "train_domain_obj = train.send(domain_client)\n", - "type(train_domain_obj)" + "train_datasite_obj = train.send(datasite_client)\n", + "type(train_datasite_obj)" ] }, { @@ -139,7 +139,7 @@ "metadata": {}, "outputs": [], "source": [ - "train_domain_obj" + "train_datasite_obj" ] }, { @@ -149,7 +149,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert torch.round(train_domain_obj.syft_action_data.sum()) == 1557" + "assert torch.round(train_datasite_obj.syft_action_data.sum()) == 1557" ] }, { @@ -233,7 +233,7 @@ }, "outputs": [], "source": [ - "weight_domain_obj = w.send(domain_client)" + "weight_datasite_obj = w.send(datasite_client)" ] }, { @@ -246,7 +246,9 @@ "outputs": [], "source": [ "@sy.syft_function(\n", - " input_policy=sy.ExactMatch(weights=weight_domain_obj.id, data=train_domain_obj.id),\n", + " input_policy=sy.ExactMatch(\n", + " weights=weight_datasite_obj.id, data=train_datasite_obj.id\n", + " ),\n", " output_policy=sy.SingleExecutionExactOutput(),\n", ")\n", "def train_mlp(weights, data):\n", @@ -292,7 +294,7 @@ }, "outputs": [], "source": [ - "pointer = train_mlp(weights=weight_domain_obj, data=train_domain_obj)\n", + "pointer = train_mlp(weights=weight_datasite_obj, data=train_datasite_obj)\n", "output = pointer.get()" ] }, @@ -315,7 +317,7 @@ }, "outputs": [], "source": [ - "request = domain_client.code.request_code_execution(train_mlp)\n", + "request = datasite_client.code.request_code_execution(train_mlp)\n", "request" ] }, @@ -340,8 +342,8 @@ }, "outputs": [], "source": [ - "domain_client._api = None\n", - "_ = domain_client.api" + "datasite_client._api = None\n", + "_ = datasite_client.api" ] }, { @@ -353,7 +355,7 @@ }, "outputs": [], "source": [ - "result_ptr = domain_client.code.train_mlp(weights=w.id, data=train.id)" + "result_ptr = datasite_client.code.train_mlp(weights=w.id, data=train.id)" ] }, { diff --git a/notebooks/api/0.8/05-custom-policy.ipynb b/notebooks/api/0.8/05-custom-policy.ipynb index ddd043c6f43..cebbe1fceeb 100644 --- a/notebooks/api/0.8/05-custom-policy.ipynb +++ b/notebooks/api/0.8/05-custom-policy.ipynb @@ -41,7 +41,9 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True, reset=True)" + "node = sy.orchestra.launch(\n", + " name=\"test-datasite-1\", port=\"auto\", dev_mode=True, reset=True\n", + ")" ] }, { @@ -53,7 +55,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -63,7 +65,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.register(\n", + "datasite_client.register(\n", " email=\"newuser@openmined.org\", name=\"John Doe\", password=\"pw\", password_verify=\"pw\"\n", ")" ] @@ -231,7 +233,7 @@ "metadata": {}, "outputs": [], "source": [ - "x_pointer = x_pointer.send(domain_client)" + "x_pointer = x_pointer.send(datasite_client)" ] }, { @@ -286,7 +288,7 @@ " root_context = AuthedServiceContext(\n", " node=context.node, credentials=context.node.verify_key\n", " )\n", - " if context.node.node_type == NodeType.DOMAIN:\n", + " if context.node.node_type == NodeType.DATASITE:\n", " for var_name, arg_id in allowed_inputs.items():\n", " kwarg_value = action_service._get(\n", " context=root_context,\n", @@ -313,7 +315,7 @@ " from syft import NodeType\n", " from syft import UID\n", "\n", - " if context.node.node_type == NodeType.DOMAIN:\n", + " if context.node.node_type == NodeType.DATASITE:\n", " node_identity = NodeIdentity(\n", " node_name=context.node.name,\n", " node_id=context.node.id,\n", @@ -383,7 +385,7 @@ " from syft import UID\n", " from syft.client.api import NodeIdentity\n", "\n", - " if context.node.node_type == NodeType.DOMAIN:\n", + " if context.node.node_type == NodeType.DATASITE:\n", " node_identity = NodeIdentity(\n", " node_name=context.node.name,\n", " node_id=context.node.id,\n", @@ -469,7 +471,7 @@ "metadata": {}, "outputs": [], "source": [ - "for request in domain_client.requests:\n", + "for request in datasite_client.requests:\n", " if request.id == request_id:\n", " break" ] @@ -589,7 +591,7 @@ }, "outputs": [], "source": [ - "for code in domain_client.code.get_all():\n", + "for code in datasite_client.code.get_all():\n", " if code.service_func_name == \"func\":\n", " break\n", "print(code.output_policy.state)\n", diff --git a/notebooks/api/0.8/06-multiple-code-requests.ipynb b/notebooks/api/0.8/06-multiple-code-requests.ipynb index a52b5c6b38f..6958dd5d52b 100644 --- a/notebooks/api/0.8/06-multiple-code-requests.ipynb +++ b/notebooks/api/0.8/06-multiple-code-requests.ipynb @@ -41,7 +41,9 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", reset=True, dev_mode=True)" + "node = sy.orchestra.launch(\n", + " name=\"test-datasite-1\", port=\"auto\", reset=True, dev_mode=True\n", + ")" ] }, { @@ -204,7 +206,7 @@ " # compute sum\n", " res = data.sum()\n", " base_lap = dp.m.make_laplace(\n", - " dp.atom_domain(T=float),\n", + " dp.atom_datasite(T=float),\n", " dp.absolute_distance(T=float),\n", " scale=10.0,\n", " )\n", @@ -305,7 +307,7 @@ " # compute mean\n", " mean = data.mean()\n", " base_lap = dp.m.make_laplace(\n", - " dp.atom_domain(T=float),\n", + " dp.atom_datasite(T=float),\n", " dp.absolute_distance(T=float),\n", " scale=10.0,\n", " )\n", @@ -358,7 +360,7 @@ "metadata": {}, "outputs": [], "source": [ - "# The Domain Owner retrieves by name or uid for approval\n", + "# The Datasite Owner retrieves by name or uid for approval\n", "root_client_project = root_client.projects.get_by_uid(project.id)\n", "assert isinstance(root_client_project, sy.service.project.project.Project)" ] diff --git a/notebooks/api/0.8/07-domain-register-control-flow.ipynb b/notebooks/api/0.8/07-datasite-register-control-flow.ipynb similarity index 95% rename from notebooks/api/0.8/07-domain-register-control-flow.ipynb rename to notebooks/api/0.8/07-datasite-register-control-flow.ipynb index 5bd493a47c9..259ee5a9832 100644 --- a/notebooks/api/0.8/07-domain-register-control-flow.ipynb +++ b/notebooks/api/0.8/07-datasite-register-control-flow.ipynb @@ -5,9 +5,9 @@ "id": "0", "metadata": {}, "source": [ - "# Registering Users in Syft Domain Server\n", + "# Registering Users in Syft Datasite Server\n", "\n", - "By default users are not allowed to create a new account on the Syft Domain Server. This notebook is a tutorial for Data Owners to enable guest signups on their deployments." + "By default users are not allowed to create a new account on the Syft Datasite Server. This notebook is a tutorial for Data Owners to enable guest signups on their deployments." ] }, { @@ -48,7 +48,7 @@ "id": "4", "metadata": {}, "source": [ - "### Launch a Syft Domain Server" + "### Launch a Syft Datasite Server" ] }, { @@ -58,7 +58,9 @@ "metadata": {}, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True, reset=True)" + "node = sy.orchestra.launch(\n", + " name=\"test-datasite-1\", port=\"auto\", dev_mode=True, reset=True\n", + ")" ] }, { @@ -329,7 +331,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Cleanup local domain server\n", + "# Cleanup local datasite server\n", "\n", "if node.node_type.value == \"python\":\n", " node.land()" diff --git a/notebooks/api/0.8/08-code-version.ipynb b/notebooks/api/0.8/08-code-version.ipynb index 4168770ca88..d900f749ef2 100644 --- a/notebooks/api/0.8/08-code-version.ipynb +++ b/notebooks/api/0.8/08-code-version.ipynb @@ -41,7 +41,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Launch a Syft Domain Server" + "### Launch a Syft Datasite Server" ] }, { @@ -50,7 +50,7 @@ "metadata": {}, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"test-domain-1\", port=\"auto\", dev_mode=True)" + "node = sy.orchestra.launch(name=\"test-datasite-1\", port=\"auto\", dev_mode=True)" ] }, { @@ -60,14 +60,14 @@ "outputs": [], "source": [ "# log into the node with default root credentials\n", - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Create a new Data Scientist account on the Domain Server\n" + "### Create a new Data Scientist account on the Datasite Server\n" ] }, { @@ -76,7 +76,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.register(\n", + "datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -268,7 +268,7 @@ "metadata": {}, "outputs": [], "source": [ - "# # Cleanup local domain server\n", + "# # Cleanup local datasite server\n", "if node.node_type.value == \"python\":\n", " node.land()" ] diff --git a/notebooks/api/0.8/09-blob-storage.ipynb b/notebooks/api/0.8/09-blob-storage.ipynb index 93491499896..8417c6dd81b 100644 --- a/notebooks/api/0.8/09-blob-storage.ipynb +++ b/notebooks/api/0.8/09-blob-storage.ipynb @@ -34,7 +34,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"test-domain-1\",\n", + " name=\"test-datasite-1\",\n", " dev_mode=True,\n", " reset=True,\n", " create_producer=True,\n", @@ -47,7 +47,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -89,7 +89,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_ptr = domain_client.upload_files([a_file, b_file])" + "data_ptr = datasite_client.upload_files([a_file, b_file])" ] }, { @@ -127,7 +127,7 @@ "metadata": {}, "outputs": [], "source": [ - "lines_file_pptr = domain_client.upload_files(x_file)[0].syft_action_data" + "lines_file_pptr = datasite_client.upload_files(x_file)[0].syft_action_data" ] }, { @@ -169,7 +169,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.upload_dataset(ds)" + "datasite_client.upload_dataset(ds)" ] }, { @@ -219,7 +219,7 @@ "metadata": {}, "outputs": [], "source": [ - "single_data_ptr = domain_client.upload_files(a_file)\n", + "single_data_ptr = datasite_client.upload_files(a_file)\n", "single_data_ptr" ] }, @@ -237,14 +237,14 @@ "outputs": [], "source": [ "if False:\n", - " domain_client.upload_files(\"./path/to/folder\")" + " datasite_client.upload_files(\"./path/to/folder\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Cleanup local domain server" + "#### Cleanup local datasite server" ] }, { diff --git a/notebooks/api/0.8/10-container-images.ipynb b/notebooks/api/0.8/10-container-images.ipynb index 6ce97e012e1..f3412e4def9 100644 --- a/notebooks/api/0.8/10-container-images.ipynb +++ b/notebooks/api/0.8/10-container-images.ipynb @@ -101,8 +101,8 @@ "metadata": {}, "outputs": [], "source": [ - "domain = sy.orchestra.launch(\n", - " name=\"test-domain-1\",\n", + "datasite = sy.orchestra.launch(\n", + " name=\"test-datasite-1\",\n", " dev_mode=True,\n", " create_producer=True,\n", " reset=True,\n", @@ -117,7 +117,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client = domain.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = datasite.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -135,7 +135,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.worker_pools" + "datasite_client.worker_pools" ] }, { @@ -227,7 +227,7 @@ "metadata": {}, "outputs": [], "source": [ - "submit_result = domain_client.api.services.worker_image.submit(\n", + "submit_result = datasite_client.api.services.worker_image.submit(\n", " worker_config=docker_config\n", ")" ] @@ -259,7 +259,7 @@ "metadata": {}, "outputs": [], "source": [ - "dockerfile_list = domain_client.images.get_all()\n", + "dockerfile_list = datasite_client.images.get_all()\n", "dockerfile_list" ] }, @@ -270,7 +270,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.images.get_all()) == 2" + "assert len(datasite_client.images.get_all()) == 2" ] }, { @@ -314,7 +314,7 @@ "metadata": {}, "outputs": [], "source": [ - "registry_add_result = domain_client.api.services.image_registry.add(\"localhost:5678\")\n", + "registry_add_result = datasite_client.api.services.image_registry.add(\"localhost:5678\")\n", "registry_add_result" ] }, @@ -335,7 +335,7 @@ "metadata": {}, "outputs": [], "source": [ - "images = domain_client.api.services.image_registry.get_all()\n", + "images = datasite_client.api.services.image_registry.get_all()\n", "assert len(images) == 1\n", "images" ] @@ -389,7 +389,7 @@ "source": [ "registry_uid = local_registry.id if running_as_container else local_registry.id\n", "\n", - "docker_build_result = domain_client.api.services.worker_image.build(\n", + "docker_build_result = datasite_client.api.services.worker_image.build(\n", " image_uid=workerimage.id,\n", " tag=docker_tag,\n", " registry_uid=registry_uid,\n", @@ -425,7 +425,7 @@ "metadata": {}, "outputs": [], "source": [ - "image_list = domain_client.images.get_all()\n", + "image_list = datasite_client.images.get_all()\n", "image_list" ] }, @@ -501,7 +501,7 @@ " local_registry_container.start()\n", " sleep(5)\n", "\n", - " push_result = domain_client.api.services.worker_image.push(workerimage.id)\n", + " push_result = datasite_client.api.services.worker_image.push(workerimage.id)\n", " assert isinstance(push_result, sy.SyftSuccess), str(push_result)" ] }, @@ -599,7 +599,7 @@ "outputs": [], "source": [ "worker_pool_name = \"opendp-pool\"\n", - "worker_pool_res = domain_client.api.services.worker_pool.launch(\n", + "worker_pool_res = datasite_client.api.services.worker_pool.launch(\n", " pool_name=worker_pool_name,\n", " image_uid=workerimage.id,\n", " num_workers=2,\n", @@ -638,7 +638,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_pool_list = domain_client.worker_pools\n", + "worker_pool_list = datasite_client.worker_pools\n", "worker_pool_list" ] }, @@ -649,7 +649,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.worker_pools.get_all()) == 2\n", + "assert len(datasite_client.worker_pools.get_all()) == 2\n", "worker_pool = None\n", "for pool in worker_pool_list:\n", " if pool.name == worker_pool_name:\n", @@ -667,7 +667,7 @@ "outputs": [], "source": [ "# We can filter pools based on the image id upon which the pools were built\n", - "domain_client.api.services.worker_pool.filter_by_image_id(image_uid=workerimage.id)" + "datasite_client.api.services.worker_pool.filter_by_image_id(image_uid=workerimage.id)" ] }, { @@ -706,7 +706,7 @@ "metadata": {}, "outputs": [], "source": [ - "raw_worker_logs = domain_client.api.services.worker.logs(\n", + "raw_worker_logs = datasite_client.api.services.worker.logs(\n", " uid=second_worker.id,\n", " raw=True,\n", ")\n", @@ -730,7 +730,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_logs = domain_client.api.services.worker.logs(\n", + "worker_logs = datasite_client.api.services.worker.logs(\n", " uid=second_worker.id,\n", ")\n", "worker_logs" @@ -761,7 +761,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_delete_res = domain_client.api.services.worker.delete(\n", + "worker_delete_res = datasite_client.api.services.worker.delete(\n", " uid=second_worker.id, force=True\n", ")" ] @@ -795,7 +795,7 @@ "source": [ "# Refetch the worker pool\n", "# Ensure that the deleted worker's id is not present\n", - "for pool in domain_client.api.services.worker_pool.get_all():\n", + "for pool in datasite_client.api.services.worker_pool.get_all():\n", " if pool.name == worker_pool_name:\n", " worker_pool = pool\n", "assert len(worker_pool.workers) == 1\n", @@ -831,7 +831,7 @@ "data = np.array([1, 2, 3])\n", "data_action_obj = sy.ActionObject.from_obj(data)\n", "\n", - "data_pointer = data_action_obj.send(domain_client)\n", + "data_pointer = data_action_obj.send(datasite_client)\n", "data_pointer" ] }, @@ -880,7 +880,7 @@ "metadata": {}, "outputs": [], "source": [ - "request = domain_client.code.request_code_execution(custom_worker_func)\n", + "request = datasite_client.code.request_code_execution(custom_worker_func)\n", "request" ] }, @@ -891,7 +891,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.requests[-1].approve(approve_nested=True)" + "datasite_client.requests[-1].approve(approve_nested=True)" ] }, { @@ -901,7 +901,7 @@ "metadata": {}, "outputs": [], "source": [ - "job = domain_client.code.custom_worker_func(x=data_pointer, blocking=False)\n", + "job = datasite_client.code.custom_worker_func(x=data_pointer, blocking=False)\n", "job" ] }, @@ -912,7 +912,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_pool = domain_client.worker_pools[worker_pool_name]\n", + "worker_pool = datasite_client.worker_pools[worker_pool_name]\n", "worker_pool" ] }, @@ -943,7 +943,7 @@ "metadata": {}, "outputs": [], "source": [ - "job = domain_client.jobs[-1]\n", + "job = datasite_client.jobs[-1]\n", "job" ] }, @@ -988,7 +988,7 @@ "source": [ "# Once the work is done by the worker, its state is returned to idle again.\n", "consuming_worker_is_now_idle = False\n", - "for worker in domain_client.worker_pools[worker_pool_name].workers:\n", + "for worker in datasite_client.worker_pools[worker_pool_name].workers:\n", " if worker.id == job.job_worker_id:\n", " consuming_worker_is_now_idle = worker.consumer_state.value.lower() == \"idle\"\n", "\n", @@ -1025,7 +1025,7 @@ "source": [ "# delete the remaining workers\n", "for worker in worker_pool.workers:\n", - " res = domain_client.api.services.worker.delete(\n", + " res = datasite_client.api.services.worker.delete(\n", " uid=worker.id,\n", " )\n", " assert isinstance(res, sy.SyftSuccess), str(res)" @@ -1038,7 +1038,7 @@ "metadata": {}, "outputs": [], "source": [ - "delete_res = domain_client.api.services.worker_image.remove(workerimage.id)\n", + "delete_res = datasite_client.api.services.worker_image.remove(workerimage.id)\n", "delete_res" ] }, @@ -1095,7 +1095,7 @@ "metadata": {}, "outputs": [], "source": [ - "submit_result = domain_client.api.services.worker_image.submit(\n", + "submit_result = datasite_client.api.services.worker_image.submit(\n", " worker_config=docker_config_2\n", ")\n", "submit_result" @@ -1108,7 +1108,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.images" + "datasite_client.images" ] }, { @@ -1120,7 +1120,7 @@ "source": [ "# get the image that's not built\n", "workerimage_2 = None\n", - "for im in domain_client.images:\n", + "for im in datasite_client.images:\n", " if im.config == docker_config_2:\n", " workerimage_2 = im" ] @@ -1142,7 +1142,7 @@ "source": [ "docker_tag_2 = \"openmined/custom-worker-opendp:latest\"\n", "\n", - "docker_build_result = domain_client.api.services.worker_image.build(\n", + "docker_build_result = datasite_client.api.services.worker_image.build(\n", " image_uid=workerimage_2.id,\n", " tag=docker_tag_2,\n", " pull=pull,\n", @@ -1158,7 +1158,7 @@ "outputs": [], "source": [ "opendp_pool_name = \"second-opendp-pool\"\n", - "pool_create_request = domain_client.api.services.worker_pool.pool_creation_request(\n", + "pool_create_request = datasite_client.api.services.worker_pool.pool_creation_request(\n", " pool_name=opendp_pool_name, num_workers=2, image_uid=workerimage_2.id\n", ")\n", "pool_create_request" @@ -1203,7 +1203,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.worker_pools[opendp_pool_name]" + "datasite_client.worker_pools[opendp_pool_name]" ] }, { @@ -1213,8 +1213,8 @@ "metadata": {}, "outputs": [], "source": [ - "assert domain_client.worker_pools[opendp_pool_name]\n", - "assert len(domain_client.worker_pools[opendp_pool_name].workers) == 2" + "assert datasite_client.worker_pools[opendp_pool_name]\n", + "assert len(datasite_client.worker_pools[opendp_pool_name].workers) == 2" ] }, { @@ -1225,7 +1225,7 @@ "outputs": [], "source": [ "# default, opendp-pool, second-opendp-pool\n", - "assert len(domain_client.worker_pools.get_all()) == 3" + "assert len(datasite_client.worker_pools.get_all()) == 3" ] }, { @@ -1243,11 +1243,11 @@ "metadata": {}, "outputs": [], "source": [ - "for worker in domain_client.worker_pools[\"second-opendp-pool\"].workers:\n", - " res = domain_client.api.services.worker.delete(uid=worker.id, force=True)\n", + "for worker in datasite_client.worker_pools[\"second-opendp-pool\"].workers:\n", + " res = datasite_client.api.services.worker.delete(uid=worker.id, force=True)\n", " assert isinstance(res, sy.SyftSuccess), str(res)\n", "\n", - "assert len(domain_client.worker_pools[\"second-opendp-pool\"].workers) == 0" + "assert len(datasite_client.worker_pools[\"second-opendp-pool\"].workers) == 0" ] }, { @@ -1265,7 +1265,7 @@ "metadata": {}, "outputs": [], "source": [ - "delete_res = domain_client.api.services.worker_image.remove(workerimage_2.id)\n", + "delete_res = datasite_client.api.services.worker_image.remove(workerimage_2.id)\n", "delete_res" ] }, @@ -1315,7 +1315,7 @@ "source": [ "recordlinkage_pool_name = \"recordlinkage-pool\"\n", "pool_image_create_request = (\n", - " domain_client.api.services.worker_pool.create_image_and_pool_request(\n", + " datasite_client.api.services.worker_pool.create_image_and_pool_request(\n", " pool_name=recordlinkage_pool_name,\n", " num_workers=2,\n", " tag=docker_tag_3,\n", @@ -1370,7 +1370,7 @@ "outputs": [], "source": [ "# Get updated request object and status\n", - "for req in domain_client.requests:\n", + "for req in datasite_client.requests:\n", " if req.id == pool_image_create_request.id:\n", " pool_image_create_request = req\n", "\n", @@ -1387,7 +1387,7 @@ "image_exists = False\n", "recordlinkage_image = None\n", "\n", - "for im in domain_client.images.get_all():\n", + "for im in datasite_client.images.get_all():\n", " if im.image_identifier and im.image_identifier.repo_with_tag == docker_tag_3:\n", " image_exists = True\n", " recordlinkage_image = im\n", @@ -1403,7 +1403,7 @@ "metadata": {}, "outputs": [], "source": [ - "recordlinkage_pool = domain_client.worker_pools[recordlinkage_pool_name]\n", + "recordlinkage_pool = datasite_client.worker_pools[recordlinkage_pool_name]\n", "\n", "assert recordlinkage_pool\n", "assert len(recordlinkage_pool.workers) == 2" @@ -1425,7 +1425,7 @@ "outputs": [], "source": [ "for worker in recordlinkage_pool.workers:\n", - " res = domain_client.api.services.worker.delete(uid=worker.id, force=True)\n", + " res = datasite_client.api.services.worker.delete(uid=worker.id, force=True)\n", " assert isinstance(res, sy.SyftSuccess), str(res)" ] }, @@ -1444,7 +1444,7 @@ "metadata": {}, "outputs": [], "source": [ - "delete_res = domain_client.api.services.worker_image.remove(recordlinkage_image.id)\n", + "delete_res = datasite_client.api.services.worker_image.remove(recordlinkage_image.id)\n", "delete_res" ] }, @@ -1455,7 +1455,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain.land()" + "datasite.land()" ] }, { diff --git a/notebooks/api/0.8/11-container-images-k8s.ipynb b/notebooks/api/0.8/11-container-images-k8s.ipynb index 5b78acff06e..4c731807b44 100644 --- a/notebooks/api/0.8/11-container-images-k8s.ipynb +++ b/notebooks/api/0.8/11-container-images-k8s.ipynb @@ -93,8 +93,8 @@ "metadata": {}, "outputs": [], "source": [ - "domain = sy.orchestra.launch(\n", - " name=\"test-domain-1\",\n", + "datasite = sy.orchestra.launch(\n", + " name=\"test-datasite-1\",\n", " dev_mode=True,\n", ")" ] @@ -106,8 +106,8 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client = domain.login(email=\"info@openmined.org\", password=\"changethis\")\n", - "domain_client" + "datasite_client = datasite.login(email=\"info@openmined.org\", password=\"changethis\")\n", + "datasite_client" ] }, { @@ -133,7 +133,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.worker_pools" + "datasite_client.worker_pools" ] }, { @@ -151,7 +151,7 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_client.api.services.worker_pool.scale(\n", + "result = datasite_client.api.services.worker_pool.scale(\n", " number=3, pool_name=\"default-pool\"\n", ")\n", "assert not isinstance(result, sy.SyftError), str(result)\n", @@ -165,7 +165,7 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_client.api.services.worker_pool.get_by_name(pool_name=\"default-pool\")\n", + "result = datasite_client.api.services.worker_pool.get_by_name(pool_name=\"default-pool\")\n", "assert len(result.workers) == 3, str(result.to_dict())\n", "result" ] @@ -199,7 +199,7 @@ "metadata": {}, "outputs": [], "source": [ - "default_pool_scale_res = domain_client.api.services.worker_pool.scale(\n", + "default_pool_scale_res = datasite_client.api.services.worker_pool.scale(\n", " number=1, pool_name=\"default-pool\"\n", ")\n", "assert not isinstance(default_pool_scale_res, sy.SyftError), str(default_pool_scale_res)\n", @@ -213,7 +213,7 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_client.api.services.worker_pool.get_by_name(pool_name=\"default-pool\")\n", + "result = datasite_client.api.services.worker_pool.get_by_name(pool_name=\"default-pool\")\n", "assert len(result.workers) == 1, str(result.to_dict())\n", "result" ] @@ -225,7 +225,7 @@ "metadata": {}, "outputs": [], "source": [ - "default_worker_pool = domain_client.api.services.worker_pool.get_by_name(\n", + "default_worker_pool = datasite_client.api.services.worker_pool.get_by_name(\n", " pool_name=\"default-pool\"\n", ")\n", "default_worker_pool" @@ -300,7 +300,7 @@ "metadata": {}, "outputs": [], "source": [ - "submit_result = domain_client.api.services.worker_image.submit(\n", + "submit_result = datasite_client.api.services.worker_image.submit(\n", " worker_config=docker_config\n", ")\n", "submit_result" @@ -323,7 +323,7 @@ "metadata": {}, "outputs": [], "source": [ - "dockerfile_list = domain_client.images.get_all()\n", + "dockerfile_list = datasite_client.images.get_all()\n", "dockerfile_list" ] }, @@ -389,7 +389,7 @@ "metadata": {}, "outputs": [], "source": [ - "registry_add_result = domain_client.api.services.image_registry.add(external_registry)\n", + "registry_add_result = datasite_client.api.services.image_registry.add(external_registry)\n", "registry_add_result" ] }, @@ -410,7 +410,7 @@ "metadata": {}, "outputs": [], "source": [ - "image_registry_list = domain_client.api.services.image_registry.get_all()\n", + "image_registry_list = datasite_client.api.services.image_registry.get_all()\n", "image_registry_list" ] }, @@ -474,7 +474,7 @@ "docker_tag = \"openmined/custom-worker:0.7.8\"\n", "\n", "\n", - "docker_build_result = domain_client.api.services.worker_image.build(\n", + "docker_build_result = datasite_client.api.services.worker_image.build(\n", " image_uid=workerimage.id,\n", " tag=docker_tag,\n", " registry_uid=registry_uid,\n", @@ -499,7 +499,7 @@ "metadata": {}, "outputs": [], "source": [ - "image_list = domain_client.images.get_all()\n", + "image_list = datasite_client.images.get_all()\n", "image_list" ] }, @@ -545,7 +545,7 @@ "outputs": [], "source": [ "push_result = None\n", - "push_result = domain_client.api.services.worker_image.push(\n", + "push_result = datasite_client.api.services.worker_image.push(\n", " workerimage.id,\n", " username=external_registry_username,\n", " password=external_registry_password,\n", @@ -603,7 +603,7 @@ "worker_pool_name = \"custom-pool\"\n", "custom_pool_pod_annotations = {\"test-custom-pool\": \"Test annotation for custom pool\"}\n", "custom_pool_pod_labels = {\"test-custom-pool\": \"test_label_for_custom_pool\"}\n", - "worker_pool_res = domain_client.api.services.worker_pool.launch(\n", + "worker_pool_res = datasite_client.api.services.worker_pool.launch(\n", " pool_name=worker_pool_name,\n", " image_uid=workerimage.id,\n", " num_workers=3,\n", @@ -643,7 +643,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_pool_list = domain_client.worker_pools.get_all()\n", + "worker_pool_list = datasite_client.worker_pools.get_all()\n", "worker_pool_list" ] }, @@ -711,7 +711,7 @@ "outputs": [], "source": [ "# We can filter pools based on the image id upon which the pools were built\n", - "filtered_result = domain_client.api.services.worker_pool.filter_by_image_id(\n", + "filtered_result = datasite_client.api.services.worker_pool.filter_by_image_id(\n", " image_uid=workerimage.id\n", ")\n", "filtered_result" @@ -753,7 +753,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_logs = domain_client.api.services.worker.logs(\n", + "worker_logs = datasite_client.api.services.worker.logs(\n", " uid=second_worker.id,\n", ")\n", "worker_logs" @@ -797,7 +797,7 @@ "data = np.array([1, 2, 3])\n", "data_action_obj = sy.ActionObject.from_obj(data)\n", "\n", - "data_pointer = data_action_obj.send(domain_client)\n", + "data_pointer = data_action_obj.send(datasite_client)\n", "data_pointer" ] }, @@ -848,7 +848,7 @@ "metadata": {}, "outputs": [], "source": [ - "request = domain_client.code.request_code_execution(custom_worker_func)\n", + "request = datasite_client.code.request_code_execution(custom_worker_func)\n", "request" ] }, @@ -859,7 +859,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.requests[-1].approve(approve_nested=True)" + "datasite_client.requests[-1].approve(approve_nested=True)" ] }, { @@ -869,7 +869,7 @@ "metadata": {}, "outputs": [], "source": [ - "job = domain_client.code.custom_worker_func(x=data_pointer, blocking=False)\n", + "job = datasite_client.code.custom_worker_func(x=data_pointer, blocking=False)\n", "job" ] }, @@ -880,7 +880,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_pool = domain_client.worker_pools[worker_pool_name]\n", + "worker_pool = datasite_client.worker_pools[worker_pool_name]\n", "worker_pool" ] }, @@ -911,7 +911,7 @@ "metadata": {}, "outputs": [], "source": [ - "job_list = domain_client.jobs.get_by_user_code_id(job.user_code_id)" + "job_list = datasite_client.jobs.get_by_user_code_id(job.user_code_id)" ] }, { @@ -956,7 +956,7 @@ "outputs": [], "source": [ "# Scale Down the workers\n", - "custom_pool_scale_res = domain_client.api.services.worker_pool.scale(\n", + "custom_pool_scale_res = datasite_client.api.services.worker_pool.scale(\n", " number=1, pool_name=worker_pool_name\n", ")\n", "assert not isinstance(custom_pool_scale_res, sy.SyftError), str(custom_pool_scale_res)\n", @@ -970,7 +970,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.worker_pools[worker_pool_name].worker_list) == 1" + "assert len(datasite_client.worker_pools[worker_pool_name].worker_list) == 1" ] }, { @@ -1005,7 +1005,7 @@ "outputs": [], "source": [ "submit_result = None\n", - "submit_result = domain_client.api.services.worker_image.submit(\n", + "submit_result = datasite_client.api.services.worker_image.submit(\n", " worker_config=docker_config_opendp\n", ")\n", "submit_result" @@ -1028,7 +1028,7 @@ "metadata": {}, "outputs": [], "source": [ - "_images = domain_client.images\n", + "_images = datasite_client.images\n", "assert not isinstance(_images, sy.SyftError), str(_images)" ] }, @@ -1063,7 +1063,7 @@ "source": [ "docker_tag_opendp = \"openmined/custom-worker-opendp:latest\"\n", "\n", - "docker_build_result = domain_client.api.services.worker_image.build(\n", + "docker_build_result = datasite_client.api.services.worker_image.build(\n", " image_uid=workerimage_opendp.id,\n", " tag=docker_tag_opendp,\n", " registry_uid=registry_uid,\n", @@ -1089,7 +1089,7 @@ "metadata": {}, "outputs": [], "source": [ - "_images = domain_client.images\n", + "_images = datasite_client.images\n", "assert not isinstance(_images, sy.SyftError), str(_images)" ] }, @@ -1125,7 +1125,7 @@ "source": [ "# Push OpenDP Image to registry\n", "push_result = None\n", - "push_result = domain_client.api.services.worker_image.push(\n", + "push_result = datasite_client.api.services.worker_image.push(\n", " workerimage_opendp.id,\n", " username=external_registry_username,\n", " password=external_registry_password,\n", @@ -1143,7 +1143,7 @@ "pool_name_opendp = \"opendp-pool\"\n", "opendp_pod_annotations = {\"test-opendp-pool\": \"Test annotation for opendp pool\"}\n", "opendp_pod_labels = {\"test-opendp-pool\": \"test_label_for_opendp_pool\"}\n", - "pool_create_request = domain_client.api.services.worker_pool.pool_creation_request(\n", + "pool_create_request = datasite_client.api.services.worker_pool.pool_creation_request(\n", " pool_name=pool_name_opendp,\n", " num_workers=3,\n", " image_uid=workerimage_opendp.id,\n", @@ -1196,7 +1196,7 @@ "metadata": {}, "outputs": [], "source": [ - "pool_opendp = domain_client.worker_pools[pool_name_opendp]\n", + "pool_opendp = datasite_client.worker_pools[pool_name_opendp]\n", "assert not isinstance(pool_opendp, sy.SyftError), str(pool_opendp)\n", "assert len(pool_opendp.worker_list) == 3" ] @@ -1208,7 +1208,7 @@ "metadata": {}, "outputs": [], "source": [ - "worker_pool_list = domain_client.worker_pools.get_all()\n", + "worker_pool_list = datasite_client.worker_pools.get_all()\n", "\n", "assert not isinstance(worker_pool_list, sy.SyftError), str(worker_pool_list)\n", "assert len(worker_pool_list) == 3" @@ -1251,7 +1251,7 @@ "outputs": [], "source": [ "# Scale Down the workers\n", - "opendp_pool_scale_res = domain_client.api.services.worker_pool.scale(\n", + "opendp_pool_scale_res = datasite_client.api.services.worker_pool.scale(\n", " number=1, pool_name=pool_name_opendp\n", ")\n", "assert not isinstance(opendp_pool_scale_res, sy.SyftError), str(opendp_pool_scale_res)\n", @@ -1265,7 +1265,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.worker_pools[pool_name_opendp].worker_list) == 1" + "assert len(datasite_client.worker_pools[pool_name_opendp].worker_list) == 1" ] }, { @@ -1308,7 +1308,7 @@ "recordlinkage_pod_labels = {\n", " \"test-recordlinkage-pool\": \"test_label_for_recordlinkage_pool\"\n", "}\n", - "pool_image_create_request = domain_client.api.services.worker_pool.create_image_and_pool_request(\n", + "pool_image_create_request = datasite_client.api.services.worker_pool.create_image_and_pool_request(\n", " pool_name=pool_name_recordlinkage,\n", " num_workers=2,\n", " tag=docker_tag_recordlinkage,\n", @@ -1377,7 +1377,7 @@ "metadata": {}, "outputs": [], "source": [ - "_requests = domain_client.requests\n", + "_requests = datasite_client.requests\n", "assert not isinstance(_requests, sy.SyftError), str(_requests)" ] }, @@ -1430,7 +1430,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.images" + "datasite_client.images" ] }, { @@ -1441,7 +1441,7 @@ "outputs": [], "source": [ "image_exists = False\n", - "for im in domain_client.images.get_all():\n", + "for im in datasite_client.images.get_all():\n", " if (\n", " im.image_identifier\n", " and im.image_identifier.repo_with_tag == docker_tag_recordlinkage\n", @@ -1458,8 +1458,8 @@ "metadata": {}, "outputs": [], "source": [ - "assert domain_client.worker_pools[pool_name_recordlinkage]\n", - "assert len(domain_client.worker_pools[pool_name_recordlinkage].worker_list) == 2" + "assert datasite_client.worker_pools[pool_name_recordlinkage]\n", + "assert len(datasite_client.worker_pools[pool_name_recordlinkage].worker_list) == 2" ] }, { @@ -1470,7 +1470,7 @@ "outputs": [], "source": [ "# Scale down the workers\n", - "recordlinkage_pool_scale_res = domain_client.api.services.worker_pool.scale(\n", + "recordlinkage_pool_scale_res = datasite_client.api.services.worker_pool.scale(\n", " number=1, pool_name=pool_name_recordlinkage\n", ")\n", "assert not isinstance(recordlinkage_pool_scale_res, sy.SyftError), str(\n", @@ -1486,7 +1486,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.worker_pools[pool_name_recordlinkage].worker_list) == 1" + "assert len(datasite_client.worker_pools[pool_name_recordlinkage].worker_list) == 1" ] } ], diff --git a/notebooks/api/0.8/12-custom-api-endpoint.ipynb b/notebooks/api/0.8/12-custom-api-endpoint.ipynb index 20c269ea286..60078a76342 100644 --- a/notebooks/api/0.8/12-custom-api-endpoint.ipynb +++ b/notebooks/api/0.8/12-custom-api-endpoint.ipynb @@ -29,7 +29,7 @@ "from syft import SyftSuccess\n", "\n", "node = sy.orchestra.launch(\n", - " name=\"test-domain-1\",\n", + " name=\"test-datasite-1\",\n", " dev_mode=True,\n", " create_producer=True,\n", " n_consumers=3,\n", @@ -37,14 +37,14 @@ " port=8081,\n", ")\n", "\n", - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")\n", - "domain_client.register(\n", + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")\n", + "datasite_client.register(\n", " email=\"user@openmined.org\",\n", " password=\"verysecurepassword\",\n", " password_verify=\"verysecurepassword\",\n", " name=\"New User\",\n", ")\n", - "domain_guest = node.login(email=\"user@openmined.org\", password=\"verysecurepassword\")" + "datasite_guest = node.login(email=\"user@openmined.org\", password=\"verysecurepassword\")" ] }, { @@ -74,7 +74,7 @@ "\n", "\n", "# Add it to the node.\n", - "response = domain_client.api.services.api.add(endpoint=public_endpoint_method)\n", + "response = datasite_client.api.services.api.add(endpoint=public_endpoint_method)\n", "response" ] }, @@ -93,7 +93,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.api.services.api.api_endpoints()" + "datasite_client.api.services.api.api_endpoints()" ] }, { @@ -102,7 +102,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.api.services.api.api_endpoints()) == 1" + "assert len(datasite_client.api.services.api.api_endpoints()) == 1" ] }, { @@ -112,8 +112,8 @@ "outputs": [], "source": [ "# Once api refresh is done, remove this cell\n", - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")\n", - "domain_guest = node.login(email=\"user@openmined.org\", password=\"verysecurepassword\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")\n", + "datasite_guest = node.login(email=\"user@openmined.org\", password=\"verysecurepassword\")" ] }, { @@ -122,7 +122,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert domain_client.api.services.first.query(query=\"SELECT *\")" + "assert datasite_client.api.services.first.query(query=\"SELECT *\")" ] }, { @@ -131,7 +131,7 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_guest.api.services.first.query(query=\"SELECT *\")\n", + "result = datasite_guest.api.services.first.query(query=\"SELECT *\")\n", "result" ] }, @@ -172,7 +172,7 @@ ")\n", "\n", "# # Add it to the node.\n", - "response = domain_client.api.services.api.add(endpoint=new_endpoint)" + "response = datasite_client.api.services.api.add(endpoint=new_endpoint)" ] }, { @@ -181,7 +181,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.api.services.api.api_endpoints()" + "datasite_client.api.services.api.api_endpoints()" ] }, { @@ -191,7 +191,7 @@ "outputs": [], "source": [ "assert isinstance(response, SyftSuccess)\n", - "assert len(domain_client.api.services.api.api_endpoints()) == 2" + "assert len(datasite_client.api.services.api.api_endpoints()) == 2" ] }, { @@ -201,8 +201,8 @@ "outputs": [], "source": [ "# Once api refresh is done, remove this cell\n", - "domain_client.refresh()\n", - "domain_guest.refresh()" + "datasite_client.refresh()\n", + "datasite_guest.refresh()" ] }, { @@ -211,7 +211,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.api.services.third.query()" + "datasite_client.api.services.third.query()" ] }, { @@ -220,7 +220,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert domain_client.api.services.third.query() == \"Private Function Execution\"" + "assert datasite_client.api.services.third.query() == \"Private Function Execution\"" ] }, { @@ -229,7 +229,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert domain_guest.api.services.third.query() == \"Public Function Execution\"" + "assert datasite_guest.api.services.third.query() == \"Public Function Execution\"" ] }, { @@ -238,7 +238,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_guest.api.services.third.query()" + "datasite_guest.api.services.third.query()" ] }, { @@ -248,7 +248,7 @@ "outputs": [], "source": [ "@sy.syft_function_single_use(\n", - " endpoint=domain_guest.api.services.third.query,\n", + " endpoint=datasite_guest.api.services.third.query,\n", ")\n", "def job_function(endpoint):\n", " return endpoint()\n", @@ -258,10 +258,10 @@ "new_project = sy.Project(\n", " name=\"My Cool UN Project\",\n", " description=\"Hi, I want to calculate the trade volume in million's with my cool code.\",\n", - " members=[domain_guest],\n", + " members=[datasite_guest],\n", ")\n", "\n", - "result = new_project.create_code_request(job_function, domain_guest)\n", + "result = new_project.create_code_request(job_function, datasite_guest)\n", "assert isinstance(result, SyftSuccess)" ] }, @@ -272,7 +272,7 @@ "outputs": [], "source": [ "res = None\n", - "for r in domain_client.requests.get_all():\n", + "for r in datasite_client.requests.get_all():\n", " if r.requesting_user_email == \"user@openmined.org\":\n", " res = r.approve()\n", "assert res is not None, res\n", @@ -285,7 +285,9 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_guest.code.job_function(endpoint=domain_client.api.services.third.query)\n", + "result = datasite_guest.code.job_function(\n", + " endpoint=datasite_client.api.services.third.query\n", + ")\n", "result" ] }, @@ -295,7 +297,9 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_guest.code.job_function(endpoint=domain_client.api.services.third.query)\n", + "result = datasite_guest.code.job_function(\n", + " endpoint=datasite_client.api.services.third.query\n", + ")\n", "result" ] }, @@ -305,7 +309,7 @@ "metadata": {}, "outputs": [], "source": [ - "domain_client.api.services.third.query" + "datasite_client.api.services.third.query" ] }, { @@ -314,7 +318,9 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_guest.code.job_function(endpoint=domain_client.api.services.third.query)\n", + "result = datasite_guest.code.job_function(\n", + " endpoint=datasite_client.api.services.third.query\n", + ")\n", "result" ] }, @@ -342,7 +348,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert isinstance(domain_guest.api.services.third.query.private(), SyftError)" + "assert isinstance(datasite_guest.api.services.third.query.private(), SyftError)" ] }, { @@ -351,7 +357,7 @@ "metadata": {}, "outputs": [], "source": [ - "result = domain_client.api.services.api.delete(endpoint_path=\"third.query\")\n", + "result = datasite_client.api.services.api.delete(endpoint_path=\"third.query\")\n", "assert isinstance(result, SyftSuccess), result" ] }, @@ -361,7 +367,7 @@ "metadata": {}, "outputs": [], "source": [ - "assert len(domain_client.api.services.api.api_endpoints()) == 1" + "assert len(datasite_client.api.services.api.api_endpoints()) == 1" ] }, { @@ -396,7 +402,7 @@ "\n", "\n", "# Add it to the node.\n", - "response = domain_client.api.services.api.add(endpoint=new_public_function)\n", + "response = datasite_client.api.services.api.add(endpoint=new_public_function)\n", "assert isinstance(response, SyftSuccess), response\n", "response" ] @@ -421,7 +427,7 @@ " return \"Updated Public Function Execution\"\n", "\n", "\n", - "response = domain_client.api.services.api.update(\n", + "response = datasite_client.api.services.api.update(\n", " endpoint_path=\"test.update\", mock_function=updated_public_function\n", ")\n", "assert isinstance(response, SyftSuccess), response\n", @@ -448,7 +454,7 @@ " return \"Updated Private Function Execution\"\n", "\n", "\n", - "response = domain_client.api.services.api.update(\n", + "response = datasite_client.api.services.api.update(\n", " endpoint_path=\"test.update\", private_function=updated_private_function\n", ")\n", "assert isinstance(response, SyftSuccess), response\n", @@ -478,7 +484,7 @@ " return \"Updated Private Function Execution\"\n", "\n", "\n", - "response = domain_client.api.services.api.update(\n", + "response = datasite_client.api.services.api.update(\n", " endpoint_path=\"test.update\",\n", " mock_function=new_sig_public_function,\n", " private_function=new_sig_private_function,\n", @@ -510,7 +516,7 @@ "metadata": {}, "outputs": [], "source": [ - "response = domain_client.api.services.api.update(endpoint_path=\"test.update\")\n", + "response = datasite_client.api.services.api.update(endpoint_path=\"test.update\")\n", "assert isinstance(response, SyftError), response" ] }, @@ -532,7 +538,7 @@ " return \"Updated Public Function Execution\"\n", "\n", "\n", - "response = domain_client.api.services.api.update(\n", + "response = datasite_client.api.services.api.update(\n", " endpoint_path=\"test.update\", mock_function=bad_public_function\n", ")\n", "assert isinstance(response, SyftError), response" @@ -551,7 +557,7 @@ "metadata": {}, "outputs": [], "source": [ - "response = domain_client.api.services.api.update(\n", + "response = datasite_client.api.services.api.update(\n", " endpoint_path=\"nonexistent\", mock_function=bad_public_function\n", ")\n", "assert isinstance(response, SyftError), response" diff --git a/notebooks/scenarios/enclave/01-primary-domain-setup.ipynb b/notebooks/scenarios/enclave/01-primary-datasite-setup.ipynb similarity index 100% rename from notebooks/scenarios/enclave/01-primary-domain-setup.ipynb rename to notebooks/scenarios/enclave/01-primary-datasite-setup.ipynb diff --git a/notebooks/scenarios/enclave/02-manual-enclave-setup.ipynb b/notebooks/scenarios/enclave/02-manual-enclave-setup.ipynb index 0f1b68dddf2..616688c5bb1 100644 --- a/notebooks/scenarios/enclave/02-manual-enclave-setup.ipynb +++ b/notebooks/scenarios/enclave/02-manual-enclave-setup.ipynb @@ -1,38 +1,38 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "fac336b0-c1a6-46a0-8133-3a2b0704a2b3", - "metadata": {}, - "outputs": [], - "source": [ - "# -- create enclave node\n", - "# -- attach to primary domain\n", - "# -- phase 2 launch python enclave dynamically instead\n", - "# -- phase 3 run on cloud enclave with k3d (dynamically after)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "fac336b0-c1a6-46a0-8133-3a2b0704a2b3", + "metadata": {}, + "outputs": [], + "source": [ + "# -- create enclave node\n", + "# -- attach to primary datasite\n", + "# -- phase 2 launch python enclave dynamically instead\n", + "# -- phase 3 run on cloud enclave with k3d (dynamically after)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/scenarios/enclave/03-secondary-domain-setup.ipynb b/notebooks/scenarios/enclave/03-secondary-datasite-setup.ipynb similarity index 100% rename from notebooks/scenarios/enclave/03-secondary-domain-setup.ipynb rename to notebooks/scenarios/enclave/03-secondary-datasite-setup.ipynb diff --git a/notebooks/scenarios/enclave/04-data-scientist-join.ipynb b/notebooks/scenarios/enclave/04-data-scientist-join.ipynb index d49d09ccbe0..63e381836e9 100644 --- a/notebooks/scenarios/enclave/04-data-scientist-join.ipynb +++ b/notebooks/scenarios/enclave/04-data-scientist-join.ipynb @@ -1,51 +1,51 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "52c96d72-c333-4b5b-8631-caaf3c48e4d0", - "metadata": {}, - "outputs": [], - "source": [ - "# -- connect to domains\n", - "# -- associate domains?\n", - "# -- list enclaves\n", - "# -- find datasets\n", - "# -- execution policies\n", - "# -- phase 2 - add a hf model and custom worker image to execution policy\n", - "# -- phase 3 eager data scientist inference inputs in InputPolicy\n", - "# -- create usercode sum(a, b)\n", - "# -- submit project" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "52c96d72-c333-4b5b-8631-caaf3c48e4d0", + "metadata": {}, + "outputs": [], + "source": [ + "# -- connect to datasites\n", + "# -- associate datasites?\n", + "# -- list enclaves\n", + "# -- find datasets\n", + "# -- execution policies\n", + "# -- phase 2 - add a hf model and custom worker image to execution policy\n", + "# -- phase 3 eager data scientist inference inputs in InputPolicy\n", + "# -- create usercode sum(a, b)\n", + "# -- submit project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ebf6dc1-6b71-4c6b-826b-c35018a041e7", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "0ebf6dc1-6b71-4c6b-826b-c35018a041e7", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/scenarios/enclave/05-domains-review.ipynb b/notebooks/scenarios/enclave/05-datasites-review.ipynb similarity index 100% rename from notebooks/scenarios/enclave/05-domains-review.ipynb rename to notebooks/scenarios/enclave/05-datasites-review.ipynb diff --git a/notebooks/scenarios/enclave/07-audit-project-logs.ipynb b/notebooks/scenarios/enclave/07-audit-project-logs.ipynb index 3af993e7572..a044f10c62a 100644 --- a/notebooks/scenarios/enclave/07-audit-project-logs.ipynb +++ b/notebooks/scenarios/enclave/07-audit-project-logs.ipynb @@ -1,36 +1,36 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c36fced0-3a9d-439f-b237-64a71a1ee3ac", - "metadata": {}, - "outputs": [], - "source": [ - "# -- domain owners view logs from enclave on domain\n", - "# -- step through execution policy at each step who did what" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c36fced0-3a9d-439f-b237-64a71a1ee3ac", + "metadata": {}, + "outputs": [], + "source": [ + "# -- datasite owners view logs from enclave on datasite\n", + "# -- step through execution policy at each step who did what" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/scenarios/getting-started/03-configuring-domain.ipynb b/notebooks/scenarios/getting-started/03-configuring-datasite.ipynb similarity index 100% rename from notebooks/scenarios/getting-started/03-configuring-domain.ipynb rename to notebooks/scenarios/getting-started/03-configuring-datasite.ipynb diff --git a/notebooks/scenarios/reverse-tunnel/04-setup-domain-with-tunnel.ipynb b/notebooks/scenarios/reverse-tunnel/04-setup-datasite-with-tunnel.ipynb similarity index 100% rename from notebooks/scenarios/reverse-tunnel/04-setup-domain-with-tunnel.ipynb rename to notebooks/scenarios/reverse-tunnel/04-setup-datasite-with-tunnel.ipynb diff --git a/notebooks/scenarios/reverse-tunnel/06-proxy-clients.ipynb b/notebooks/scenarios/reverse-tunnel/06-proxy-clients.ipynb index 536dce15404..3c8d070e544 100644 --- a/notebooks/scenarios/reverse-tunnel/06-proxy-clients.ipynb +++ b/notebooks/scenarios/reverse-tunnel/06-proxy-clients.ipynb @@ -1,36 +1,36 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "1c3868bb-a140-451a-ac51-a4fd17bf2ab8", - "metadata": {}, - "outputs": [], - "source": [ - "# -- how to list domains on gateway\n", - "# -- getting a proxy client" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1c3868bb-a140-451a-ac51-a4fd17bf2ab8", + "metadata": {}, + "outputs": [], + "source": [ + "# -- how to list datasites on gateway\n", + "# -- getting a proxy client" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/tutorials/data-owner/01-uploading-private-data.ipynb b/notebooks/tutorials/data-owner/01-uploading-private-data.ipynb index 3a1863c0bdd..b44c31a72ca 100644 --- a/notebooks/tutorials/data-owner/01-uploading-private-data.ipynb +++ b/notebooks/tutorials/data-owner/01-uploading-private-data.ipynb @@ -51,7 +51,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"private-data-example-domain-1\", port=\"auto\", reset=True\n", + " name=\"private-data-example-datasite-1\", port=\"auto\", reset=True\n", ")" ] }, @@ -322,7 +322,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Cleanup local domain server\n", + "# Cleanup local datasite server\n", "if node.node_type.value == \"python\":\n", " node.land()" ] diff --git a/notebooks/tutorials/data-owner/02-account-management.ipynb b/notebooks/tutorials/data-owner/02-account-management.ipynb index a4e64b74698..a7e2ae2e533 100644 --- a/notebooks/tutorials/data-owner/02-account-management.ipynb +++ b/notebooks/tutorials/data-owner/02-account-management.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"account-management-example-domain-1\", port=8041, reset=True\n", + " name=\"account-management-example-datasite-1\", port=8041, reset=True\n", ")" ] }, diff --git a/notebooks/tutorials/data-owner/03-messages-and-requests.ipynb b/notebooks/tutorials/data-owner/03-messages-and-requests.ipynb index c05d6f7d556..3c3c5c189b3 100644 --- a/notebooks/tutorials/data-owner/03-messages-and-requests.ipynb +++ b/notebooks/tutorials/data-owner/03-messages-and-requests.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"messages-requests-example-domain-1-do\", port=7021, reset=True\n", + " name=\"messages-requests-example-datasite-1-do\", port=7021, reset=True\n", ")" ] }, diff --git a/notebooks/tutorials/data-owner/05-syft-services-api.ipynb b/notebooks/tutorials/data-owner/05-syft-services-api.ipynb index 7c3f409105a..98cbac0e43f 100644 --- a/notebooks/tutorials/data-owner/05-syft-services-api.ipynb +++ b/notebooks/tutorials/data-owner/05-syft-services-api.ipynb @@ -57,7 +57,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"services-api-example-domain-1\", port=\"auto\", reset=True\n", + " name=\"services-api-example-datasite-1\", port=\"auto\", reset=True\n", ")" ] }, diff --git a/notebooks/tutorials/data-scientist/02-finding-datasets.ipynb b/notebooks/tutorials/data-scientist/02-finding-datasets.ipynb index 30dcf080d8c..bfae6dbdb2d 100644 --- a/notebooks/tutorials/data-scientist/02-finding-datasets.ipynb +++ b/notebooks/tutorials/data-scientist/02-finding-datasets.ipynb @@ -21,7 +21,7 @@ "id": "2", "metadata": {}, "source": [ - "## Connecting to a Domain" + "## Connecting to a Datasite" ] }, { diff --git a/notebooks/tutorials/data-scientist/03-working-with-private-datasets.ipynb b/notebooks/tutorials/data-scientist/03-working-with-private-datasets.ipynb index acf4ec170df..f8302eaf4e7 100644 --- a/notebooks/tutorials/data-scientist/03-working-with-private-datasets.ipynb +++ b/notebooks/tutorials/data-scientist/03-working-with-private-datasets.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"private-datasets-example-domain-1\", port=8062, reset=True\n", + " name=\"private-datasets-example-datasite-1\", port=8062, reset=True\n", ")" ] }, diff --git a/notebooks/tutorials/data-scientist/04-syft-functions.ipynb b/notebooks/tutorials/data-scientist/04-syft-functions.ipynb index cbee1755a3d..84f4ee15536 100644 --- a/notebooks/tutorials/data-scientist/04-syft-functions.ipynb +++ b/notebooks/tutorials/data-scientist/04-syft-functions.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"syft-functions-example-domain-1\", port=7022, reset=True\n", + " name=\"syft-functions-example-datasite-1\", port=7022, reset=True\n", ")" ] }, @@ -252,7 +252,7 @@ "id": "23", "metadata": {}, "source": [ - "You have probably noticed that in the last example we also specified the output policy. Its purpose has to do with the release of information for a given function and controlling the parameters that this release comes with. For example, if a data owner and a data scientist agree on the content of a function run on a domain and on what private data that can be run on, their work might not be done yet. They might negotiate how many times that function can be run, whether or not the data scientist can have access or what happens before releasing the output (maybe we add some noise like in the case of differential privacy). At the moment we have policies that allow data scientist to ask for a certain amount of runs on function, but the ones you will find most often is `SingleExecutionExactOutput` that ask for a single use on a function. We have used it so much that we came with the `syft_function_single_use` decorator that use by default that output policy. What is also cool is that you can pass the input for an input policy to this decorator to get a shorter version like this:" + "You have probably noticed that in the last example we also specified the output policy. Its purpose has to do with the release of information for a given function and controlling the parameters that this release comes with. For example, if a data owner and a data scientist agree on the content of a function run on a datasite and on what private data that can be run on, their work might not be done yet. They might negotiate how many times that function can be run, whether or not the data scientist can have access or what happens before releasing the output (maybe we add some noise like in the case of differential privacy). At the moment we have policies that allow data scientist to ask for a certain amount of runs on function, but the ones you will find most often is `SingleExecutionExactOutput` that ask for a single use on a function. We have used it so much that we came with the `syft_function_single_use` decorator that use by default that output policy. What is also cool is that you can pass the input for an input policy to this decorator to get a shorter version like this:" ] }, { @@ -435,7 +435,7 @@ "id": "42", "metadata": {}, "source": [ - "Right! Our code was not approved, so we should wait for the review from the data owner. As we also deployed the domain, we will do that quickly here, but for more details on what is happening check the data owner sections under tutorials:" + "Right! Our code was not approved, so we should wait for the review from the data owner. As we also deployed the datasite, we will do that quickly here, but for more details on what is happening check the data owner sections under tutorials:" ] }, { @@ -509,7 +509,7 @@ "id": "50", "metadata": {}, "source": [ - "Notice that the result we see is still `1.0` which looks like the result on the mock data. That is because it actually is! The object returned is an `ActionObject` which here behaves like a pointer for the data on the domain:" + "Notice that the result we see is still `1.0` which looks like the result on the mock data. That is because it actually is! The object returned is an `ActionObject` which here behaves like a pointer for the data on the datasite:" ] }, { diff --git a/notebooks/tutorials/data-scientist/05-messaging-and-requests.ipynb b/notebooks/tutorials/data-scientist/05-messaging-and-requests.ipynb index 5d7ff62fa94..2b3c303473f 100644 --- a/notebooks/tutorials/data-scientist/05-messaging-and-requests.ipynb +++ b/notebooks/tutorials/data-scientist/05-messaging-and-requests.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"messages-requests-example-domain-1-ds\", port=7023, reset=True\n", + " name=\"messages-requests-example-datasite-1-ds\", port=7023, reset=True\n", ")" ] }, diff --git a/notebooks/tutorials/deployments/01-deploy-python.ipynb b/notebooks/tutorials/deployments/01-deploy-python.ipynb index cd80e629126..2a51a5459de 100644 --- a/notebooks/tutorials/deployments/01-deploy-python.ipynb +++ b/notebooks/tutorials/deployments/01-deploy-python.ipynb @@ -59,9 +59,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Working with Python Domain\n", + "## Working with Python Datasite\n", "\n", - "`PySyft` makes it very easy to develop against a domain in a notebook by providing the `sy.orchestra` interface. It allows you to start a domain with a webserver in a notebook in the background, which is a lightweight version of a Domain that would be used in production. You can specify options such as what kind of database you are using, whether you want to use networking and how many processes you want to use. You can launch a Domain by simply executing:" + "`PySyft` makes it very easy to develop against a datasite in a notebook by providing the `sy.orchestra` interface. It allows you to start a datasite with a webserver in a notebook in the background, which is a lightweight version of a Datasite that would be used in production. You can specify options such as what kind of database you are using, whether you want to use networking and how many processes you want to use. You can launch a Datasite by simply executing:" ] }, { @@ -81,7 +81,7 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(\n", - " name=\"dev-mode-example-domain-1\", port=8020, reset=True, dev_mode=True\n", + " name=\"dev-mode-example-datasite-1\", port=8020, reset=True, dev_mode=True\n", ")" ] }, @@ -92,12 +92,12 @@ "If we don't need a webserver (for development this is true in many cases), we can omit the port and instead use\n", "\n", "```python\n", - "node = sy.orchestra.launch(name=\"dev-mode-example-domain-1\", dev_mode=True, reset=True)\n", + "node = sy.orchestra.launch(name=\"dev-mode-example-datasite-1\", dev_mode=True, reset=True)\n", "```\n", "\n", "One of the benefits of not using a port is that you can use a debugger and set breakpoints within api calls. This makes debugging way faster in many cases.\n", "\n", - "Now, we are ready to start using the domain. The domain comes with test login credentials for the admin." + "Now, we are ready to start using the datasite. The datasite comes with test login credentials for the admin." ] }, { @@ -113,7 +113,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Once you are logged in, you are ready to start using the domain, for instance for creating a dataset (this one is empty, just as a example)." + "Once you are logged in, you are ready to start using the datasite, for instance for creating a dataset (this one is empty, just as a example)." ] }, { @@ -130,7 +130,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Lastly to stop or terminate your Domain, we can execute the following command:" + "Lastly to stop or terminate your Datasite, we can execute the following command:" ] }, { @@ -157,7 +157,7 @@ "- [04-pytorch-example.ipynb](../../api/0.8/04-pytorch-example.ipynb)\n", "- [05-custom-policy.ipynb](../../api/0.8/05-custom-policy.ipynb)\n", "- [06-multiple-code-requests.ipynb](../../api/0.8/06-multiple-code-requests.ipynb)\n", - "- [07-domain-register-control-flow.ipynb](../../api/0.8/07-domain-register-control-flow.ipynb)\n", + "- [07-datasite-register-control-flow.ipynb](../../api/0.8/07-datasite-register-control-flow.ipynb)\n", "- [08-code-version.ipynb](../../api/0.8/08-code-version.ipynb)\n", "- [09-blob-storage.ipynb](../../api/0.8/09-blob-storage.ipynb)\n", "- [10-container-images.ipynb](../../api/0.8/10-container-images.ipynb)\n", diff --git a/notebooks/tutorials/deployments/02-deploy-container.ipynb b/notebooks/tutorials/deployments/02-deploy-container.ipynb index a8717135d3f..11114d0434c 100644 --- a/notebooks/tutorials/deployments/02-deploy-container.ipynb +++ b/notebooks/tutorials/deployments/02-deploy-container.ipynb @@ -58,14 +58,14 @@ "source": [ "``` bash\n", "docker run -it \\\n", - " -e NODE_NAME=syft-example-domain-1 \\\n", - " -e NODE_TYPE=domain \\\n", + " -e NODE_NAME=syft-example-datasite-1 \\\n", + " -e NODE_TYPE=datasite \\\n", " -e N_CONSUMERS=1 \\\n", " -e SINGLE_CONTAINER_MODE=true \\\n", " -e CREATE_PRODUCER=true \\\n", " -e INMEMORY_WORKERS=true \\\n", " -p 8080:80 --add-host=host.docker.internal:host-gateway \\\n", - " --name syft-example-domain-1 openmined/grid-backend:$SYFT_VERSION\n", + " --name syft-example-datasite-1 openmined/grid-backend:$SYFT_VERSION\n", "```" ] }, @@ -75,7 +75,7 @@ "source": [ "## Working with the single container deployment\n", "\n", - "PySyft makes it very simple to connect to any existing Syft cluster by providing the `sy.orchestra` interface. You can connect to the domain by executing these steps in your jupyter notebook:" + "PySyft makes it very simple to connect to any existing Syft cluster by providing the `sy.orchestra` interface. You can connect to the datasite by executing these steps in your jupyter notebook:" ] }, { @@ -86,7 +86,7 @@ "# syft absolute\n", "import syft as sy\n", "\n", - "node = sy.orchestra.launch(name=\"syft-example-domain-1\", deploy_to=\"remote\")\n", + "node = sy.orchestra.launch(name=\"syft-example-datasite-1\", deploy_to=\"remote\")\n", "```" ] }, @@ -107,7 +107,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we are ready to start using the domain. The domain comes with default login credentials for the admin." + "Now, we are ready to start using the datasite. The datasite comes with default login credentials for the admin." ] }, { @@ -123,7 +123,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Once you are logged in, you are ready to start using the domain, for instance for creating a dataset (this one is empty, just as a example)." + "Once you are logged in, you are ready to start using the datasite, for instance for creating a dataset (this one is empty, just as a example)." ] }, { @@ -151,7 +151,7 @@ "- [04-pytorch-example.ipynb](../../api/0.8/04-pytorch-example.ipynb)\n", "- [05-custom-policy.ipynb](../../api/0.8/05-custom-policy.ipynb)\n", "- [06-multiple-code-requests.ipynb](../../api/0.8/06-multiple-code-requests.ipynb)\n", - "- [07-domain-register-control-flow.ipynb](../../api/0.8/07-domain-register-control-flow.ipynb)\n", + "- [07-datasite-register-control-flow.ipynb](../../api/0.8/07-datasite-register-control-flow.ipynb)\n", "- [08-code-version.ipynb](../../api/0.8/08-code-version.ipynb)\n", "- [09-blob-storage.ipynb](../../api/0.8/09-blob-storage.ipynb)\n", "- [10-container-images.ipynb](../../api/0.8/10-container-images.ipynb)\n", diff --git a/notebooks/tutorials/deployments/03-deploy-k8s-k3d.ipynb b/notebooks/tutorials/deployments/03-deploy-k8s-k3d.ipynb index 3ba644ee5dc..333220966f4 100644 --- a/notebooks/tutorials/deployments/03-deploy-k8s-k3d.ipynb +++ b/notebooks/tutorials/deployments/03-deploy-k8s-k3d.ipynb @@ -104,7 +104,7 @@ "source": [ "## Working with the local Kubernetes deployment\n", "\n", - "PySyft makes it very simple to connect to your existing Syft cluster by providing the `sy.orchestra` interface. You can connect to the domain by executing these steps in your jupyter notebook:" + "PySyft makes it very simple to connect to your existing Syft cluster by providing the `sy.orchestra` interface. You can connect to the datasite by executing these steps in your jupyter notebook:" ] }, { @@ -115,7 +115,7 @@ "# syft absolute\n", "import syft as sy\n", "\n", - "node = sy.orchestra.launch(name=\"syft-example-domain-1\", deploy_to=\"remote\")\n", + "node = sy.orchestra.launch(name=\"syft-example-datasite-1\", deploy_to=\"remote\")\n", "```" ] }, @@ -137,7 +137,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we are ready to start using the domain. Since helm is a product grade deployement stack, the domain comes with a randomized password for the default email credentials for the admin. Either run with Step 5 with your custom password or to extract the randomized password using `kubectl`, run the following command (in case you use a custom cluster name in step 1, replace `--context=k3d-$CLUSTER_NAME` appropriately): \n", + "Now, we are ready to start using the datasite. Since helm is a product grade deployement stack, the datasite comes with a randomized password for the default email credentials for the admin. Either run with Step 5 with your custom password or to extract the randomized password using `kubectl`, run the following command (in case you use a custom cluster name in step 1, replace `--context=k3d-$CLUSTER_NAME` appropriately): \n", "\n", "```sh\n", "kubectl --context=k3d-syft get secret backend-secret -n syft -o jsonpath='{.data.defaultRootPassword}' | base64 --decode\n", @@ -164,7 +164,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Once you are logged in, you are ready to start using the domain, for instance for creating a dataset (this one is empty, just as a example)." + "Once you are logged in, you are ready to start using the datasite, for instance for creating a dataset (this one is empty, just as a example)." ] }, { @@ -192,7 +192,7 @@ "- [04-pytorch-example.ipynb](../../api/0.8/04-pytorch-example.ipynb)\n", "- [05-custom-policy.ipynb](../../api/0.8/05-custom-policy.ipynb)\n", "- [06-multiple-code-requests.ipynb](../../api/0.8/06-multiple-code-requests.ipynb)\n", - "- [07-domain-register-control-flow.ipynb](../../api/0.8/07-domain-register-control-flow.ipynb)\n", + "- [07-datasite-register-control-flow.ipynb](../../api/0.8/07-datasite-register-control-flow.ipynb)\n", "- [08-code-version.ipynb](../../api/0.8/08-code-version.ipynb)\n", "- [09-blob-storage.ipynb](../../api/0.8/09-blob-storage.ipynb)\n", "- [10-container-images.ipynb](../../api/0.8/10-container-images.ipynb)\n", diff --git a/notebooks/tutorials/deployments/07-deploy-devspace.ipynb b/notebooks/tutorials/deployments/07-deploy-devspace.ipynb index 29632c99dec..c1f3c5788d1 100644 --- a/notebooks/tutorials/deployments/07-deploy-devspace.ipynb +++ b/notebooks/tutorials/deployments/07-deploy-devspace.ipynb @@ -84,10 +84,10 @@ "\n", "#### Launching Different Node Types\n", "Alternatively, you can also use the following commands to launch different node types:\n", - "* Launching a Domain: `CLUSTER_NAME=testdomain1 CLUSTER_HTTP_PORT=9082 tox -e dev.k8s.launch.domain`\n", + "* Launching a Datasite: `CLUSTER_NAME=testdatasite1 CLUSTER_HTTP_PORT=9082 tox -e dev.k8s.launch.datasite`\n", "* Launching a Gateway: `CLUSTER_NAME=testgateway1 CLUSTER_HTTP_PORT=9081 tox -e dev.k8s.launch.gateway`\n", "* Launching a Enclave: `CLUSTER_NAME=testenclave1 CLUSTER_HTTP_PORT=9083 tox -e dev.k8s.launch.enclave`\n", - "* Launching Nodes with `hotreload` using tox posargs: `tox -e dev.k8s.launch.domain -- hotreload`" + "* Launching Nodes with `hotreload` using tox posargs: `tox -e dev.k8s.launch.datasite -- hotreload`" ] }, { @@ -105,7 +105,7 @@ "- [04-jax-example.ipynb](../../api/0.8/04-jax-example.ipynb)\n", "- [05-custom-policy.ipynb](../../api/0.8/05-custom-policy.ipynb)\n", "- [06-multiple-code-requests.ipynb](../../api/0.8/06-multiple-code-requests.ipynb)\n", - "- [07-domain-register-control-flow.ipynb](../../api/0.8/07-domain-register-control-flow.ipynb)\n", + "- [07-datasite-register-control-flow.ipynb](../../api/0.8/07-datasite-register-control-flow.ipynb)\n", "- [08-code-version.ipynb](../../api/0.8/08-code-version.ipynb)\n", "- [09-blob-storage.ipynb](../../api/0.8/09-blob-storage.ipynb)\n", "- [10-container-images.ipynb](../../api/0.8/10-container-images.ipynb)\n", diff --git a/notebooks/tutorials/hello-syft/01-hello-syft.ipynb b/notebooks/tutorials/hello-syft/01-hello-syft.ipynb index e51018e2be0..2a771031677 100644 --- a/notebooks/tutorials/hello-syft/01-hello-syft.ipynb +++ b/notebooks/tutorials/hello-syft/01-hello-syft.ipynb @@ -94,8 +94,8 @@ "outputs": [], "source": [ "node = sy.orchestra.launch(name=\"hello-syft-usa-server\", port=9000, reset=True)\n", - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")\n", - "root_domain_client.register(\n", + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")\n", + "root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"janedoe@caltech.edu\",\n", " password=\"abc123\",\n", @@ -114,7 +114,7 @@ "source": [ "## Data owner - Part 1\n", "\n", - "### Upload Data to Domain" + "### Upload Data to Datasite" ] }, { @@ -162,7 +162,7 @@ " )\n", " ],\n", ")\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -319,7 +319,7 @@ "metadata": {}, "outputs": [], "source": [ - "root_domain_client.requests" + "root_datasite_client.requests" ] }, { @@ -329,7 +329,7 @@ "metadata": {}, "outputs": [], "source": [ - "request = root_domain_client.requests[0]" + "request = root_datasite_client.requests[0]" ] }, { diff --git a/notebooks/tutorials/model-auditing/colab/01-user-log.ipynb b/notebooks/tutorials/model-auditing/colab/01-user-log.ipynb index 6e1e438c04e..f4fd21c67ec 100644 --- a/notebooks/tutorials/model-auditing/colab/01-user-log.ipynb +++ b/notebooks/tutorials/model-auditing/colab/01-user-log.ipynb @@ -13,7 +13,7 @@ "id": "1", "metadata": {}, "source": [ - "In this tutorial, we show how external parties can audit internal AI systems without accessing them — mitigating privacy, security, and IP costs and risks. **This tutorial uses syft 0.8.2.b0, with a domain setup that does not use networking, to run the tutorial with networking read more in section 1.1.1**\n", + "In this tutorial, we show how external parties can audit internal AI systems without accessing them — mitigating privacy, security, and IP costs and risks. **This tutorial uses syft 0.8.2.b0, with a datasite setup that does not use networking, to run the tutorial with networking read more in section 1.1.1**\n", "\n", "You can read more about this tutorial and the follow up tutorials here on the [blog post](https://blog.openmined.org/)." ] @@ -70,7 +70,7 @@ "id": "6", "metadata": {}, "source": [ - "### Launch PySyft domain server" + "### Launch PySyft datasite server" ] }, { @@ -78,7 +78,7 @@ "id": "7", "metadata": {}, "source": [ - "To start we launch a `PySyft` domain server. This is the backend that stores the private data." + "To start we launch a `PySyft` datasite server. This is the backend that stores the private data." ] }, { @@ -90,7 +90,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"syft-domain\", reset=True)" + "node = sy.orchestra.launch(name=\"syft-datasite\", reset=True)" ] }, { @@ -98,15 +98,15 @@ "id": "9", "metadata": {}, "source": [ - "There are 3 ways to launch a `PySyft` domain\n", + "There are 3 ways to launch a `PySyft` datasite\n", "\n", "**A) From a notebook, with simulated networking \\*\\*THIS NOTEBOOK\\*\\***\n", " - Apart from the network calls, this uses exactly the same code as other setups\n", - " - run orchestra **without a port**: `sy.orchestra.launch(name=\"syft-domain\")`\n", + " - run orchestra **without a port**: `sy.orchestra.launch(name=\"syft-datasite\")`\n", " \n", "**B) From a notebook with networking (also supports docker)**\n", " - This spawns a separate process that starts a uvicorn webserver\n", - " - run orchestra **with a port**:`sy.orchestra.launch(name=\"syft-domain\", port=8080)`\n", + " - run orchestra **with a port**:`sy.orchestra.launch(name=\"syft-datasite\", port=8080)`\n", " \n", "**C) From the command line (supports docker/kubernetes)**\n", " - setup for production\n", @@ -129,7 +129,7 @@ "id": "11", "metadata": {}, "source": [ - "We can now login to our domain using the default admin credentials. In production we would change these." + "We can now login to our datasite using the default admin credentials. In production we would change these." ] }, { @@ -157,7 +157,7 @@ "id": "14", "metadata": {}, "source": [ - "For this tutorial we allow other users to create their own account. New accounts will get limited permissions and will only be able to see the mock version of any datasets we upload to the domain." + "For this tutorial we allow other users to create their own account. New accounts will get limited permissions and will only be able to see the mock version of any datasets we upload to the datasite." ] }, { @@ -223,7 +223,7 @@ "id": "21", "metadata": {}, "source": [ - "To upload our dataset to the domain we need to wrap it in a `Syft Dataset` object. We can add some metadata to the object." + "To upload our dataset to the datasite we need to wrap it in a `Syft Dataset` object. We can add some metadata to the object." ] }, { diff --git a/notebooks/tutorials/model-training/00-data-owner-upload-data.ipynb b/notebooks/tutorials/model-training/00-data-owner-upload-data.ipynb index 10287b41fe5..4f7c17a37b3 100644 --- a/notebooks/tutorials/model-training/00-data-owner-upload-data.ipynb +++ b/notebooks/tutorials/model-training/00-data-owner-upload-data.ipynb @@ -26,7 +26,7 @@ "id": "1", "metadata": {}, "source": [ - "## 1. Launch the domain, upload the data" + "## 1. Launch the datasite, upload the data" ] }, { @@ -36,7 +36,7 @@ "metadata": {}, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"mnist-torch-domain\", dev_mode=True, reset=True)\n", + "node = sy.orchestra.launch(name=\"mnist-torch-datasite\", dev_mode=True, reset=True)\n", "root_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, diff --git a/notebooks/tutorials/model-training/01-data-scientist-submit-code.ipynb b/notebooks/tutorials/model-training/01-data-scientist-submit-code.ipynb index 7b65d033cbc..ded7a2fbdd5 100644 --- a/notebooks/tutorials/model-training/01-data-scientist-submit-code.ipynb +++ b/notebooks/tutorials/model-training/01-data-scientist-submit-code.ipynb @@ -20,7 +20,7 @@ "id": "1", "metadata": {}, "source": [ - "## 1. DS logins to the domain with the credentials created by the DO" + "## 1. DS logins to the datasite with the credentials created by the DO" ] }, { @@ -30,7 +30,7 @@ "metadata": {}, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"mnist-torch-domain\", dev_mode=True)\n", + "node = sy.orchestra.launch(name=\"mnist-torch-datasite\", dev_mode=True)\n", "ds_client = node.login(email=\"sheldon@caltech.edu\", password=\"changethis\")" ] }, @@ -39,7 +39,7 @@ "id": "3", "metadata": {}, "source": [ - "### Inspect the datasets on the domain" + "### Inspect the datasets on the datasite" ] }, { diff --git a/notebooks/tutorials/model-training/02-data-owner-review-approve-code.ipynb b/notebooks/tutorials/model-training/02-data-owner-review-approve-code.ipynb index d4a9c70cc5e..ad4294e82f6 100644 --- a/notebooks/tutorials/model-training/02-data-owner-review-approve-code.ipynb +++ b/notebooks/tutorials/model-training/02-data-owner-review-approve-code.ipynb @@ -21,7 +21,7 @@ "metadata": {}, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"mnist-torch-domain\", dev_mode=True)\n", + "node = sy.orchestra.launch(name=\"mnist-torch-datasite\", dev_mode=True)\n", "root_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, diff --git a/notebooks/tutorials/model-training/03-data-scientist-download-results.ipynb b/notebooks/tutorials/model-training/03-data-scientist-download-results.ipynb index de5c4270d2e..7305f29ce96 100644 --- a/notebooks/tutorials/model-training/03-data-scientist-download-results.ipynb +++ b/notebooks/tutorials/model-training/03-data-scientist-download-results.ipynb @@ -22,7 +22,7 @@ "metadata": {}, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"mnist-torch-domain\", dev_mode=True)\n", + "node = sy.orchestra.launch(name=\"mnist-torch-datasite\", dev_mode=True)\n", "ds_client = node.login(email=\"sheldon@caltech.edu\", password=\"changethis\")" ] }, diff --git a/notebooks/tutorials/pandas-cookbook/01-reading-from-a-csv.ipynb b/notebooks/tutorials/pandas-cookbook/01-reading-from-a-csv.ipynb index 368d7090d57..fd8e8d1aa49 100644 --- a/notebooks/tutorials/pandas-cookbook/01-reading-from-a-csv.ipynb +++ b/notebooks/tutorials/pandas-cookbook/01-reading-from-a-csv.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-1\", port=7081, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-1\", port=7081, reset=True)" ] }, { @@ -74,7 +74,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -256,7 +256,7 @@ " name=\"test\",\n", " asset_list=[sy.Asset(name=\"bikes\", data=fixed_df, mock=mock, mock_is_real=False)],\n", ")\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -276,7 +276,7 @@ }, "outputs": [], "source": [ - "user = root_domain_client.register(\n", + "user = root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -307,7 +307,7 @@ }, "outputs": [], "source": [ - "guest_domain_client = node.client" + "guest_datasite_client = node.client" ] }, { @@ -319,7 +319,7 @@ }, "outputs": [], "source": [ - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -355,8 +355,8 @@ }, "outputs": [], "source": [ - "guest_domain_client = node.client\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_datasite_client = node.client\n", + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -368,7 +368,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[0]" + "ds = guest_datasite_client.datasets[0]" ] }, { @@ -632,7 +632,7 @@ }, "outputs": [], "source": [ - "domain_client = node.client.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.client.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -644,7 +644,7 @@ }, "outputs": [], "source": [ - "notifications = domain_client.notifications.get_all_unread()" + "notifications = datasite_client.notifications.get_all_unread()" ] }, { @@ -750,7 +750,7 @@ }, "outputs": [], "source": [ - "real_data = domain_client.datasets[0].assets[0].data" + "real_data = datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/02-selecting-data-finding-common-complain.ipynb b/notebooks/tutorials/pandas-cookbook/02-selecting-data-finding-common-complain.ipynb index c7a8d83c7f6..ad7df40b144 100644 --- a/notebooks/tutorials/pandas-cookbook/02-selecting-data-finding-common-complain.ipynb +++ b/notebooks/tutorials/pandas-cookbook/02-selecting-data-finding-common-complain.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-2\", port=9082, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-2\", port=9082, reset=True)" ] }, { @@ -74,7 +74,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -340,7 +340,7 @@ " sy.Asset(name=\"complaints\", data=complaints, mock=mock, mock_is_real=False)\n", " ],\n", ")\n", - "domain_client.upload_dataset(dataset)" + "datasite_client.upload_dataset(dataset)" ] }, { @@ -360,7 +360,7 @@ }, "outputs": [], "source": [ - "user = domain_client.register(\n", + "user = datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -371,9 +371,9 @@ "\n", "# todo: give user data scientist role\n", "\n", - "guest_domain_client = node.client\n", + "guest_datasite_client = node.client\n", "\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -409,7 +409,7 @@ }, "outputs": [], "source": [ - "guest_domain_client = node.client" + "guest_datasite_client = node.client" ] }, { @@ -421,8 +421,8 @@ }, "outputs": [], "source": [ - "# guest_domain_client = worker.guest_client\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "# guest_datasite_client = worker.guest_client\n", + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -434,7 +434,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[0]" + "ds = guest_datasite_client.datasets[0]" ] }, { @@ -830,7 +830,7 @@ }, "outputs": [], "source": [ - "domain_client = node.client.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.client.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -850,7 +850,7 @@ }, "outputs": [], "source": [ - "notifications = domain_client.notifications.get_all_unread()" + "notifications = datasite_client.notifications.get_all_unread()" ] }, { @@ -925,7 +925,7 @@ }, "outputs": [], "source": [ - "real_data = domain_client.datasets[0].assets[0].data" + "real_data = datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/03-which-borough-has-the-most-noise-complaints.ipynb b/notebooks/tutorials/pandas-cookbook/03-which-borough-has-the-most-noise-complaints.ipynb index 407f6507c3a..191d23fbae3 100644 --- a/notebooks/tutorials/pandas-cookbook/03-which-borough-has-the-most-noise-complaints.ipynb +++ b/notebooks/tutorials/pandas-cookbook/03-which-borough-has-the-most-noise-complaints.ipynb @@ -57,7 +57,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-3\", port=7083, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-3\", port=7083, reset=True)" ] }, { @@ -77,7 +77,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -355,7 +355,7 @@ " sy.Asset(name=\"complaints\", data=complaints, mock=mock, mock_is_real=False)\n", " ],\n", ")\n", - "domain_client.upload_dataset(dataset)" + "datasite_client.upload_dataset(dataset)" ] }, { @@ -375,7 +375,7 @@ }, "outputs": [], "source": [ - "user = domain_client.register(\n", + "user = datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -386,9 +386,9 @@ "\n", "# todo: give user data scientist role\n", "\n", - "guest_domain_client = node.client\n", + "guest_datasite_client = node.client\n", "\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -424,8 +424,8 @@ }, "outputs": [], "source": [ - "guest_domain_client = node.client\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_datasite_client = node.client\n", + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -437,7 +437,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[0]" + "ds = guest_datasite_client.datasets[0]" ] }, { @@ -955,7 +955,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -967,7 +967,7 @@ }, "outputs": [], "source": [ - "notifications = domain_client.notifications.get_all_unread()" + "notifications = datasite_client.notifications.get_all_unread()" ] }, { @@ -1042,7 +1042,7 @@ }, "outputs": [], "source": [ - "real_data = domain_client.datasets[0].assets[0].data" + "real_data = datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/04-weekday-bike-most-groupby-aggregate.ipynb b/notebooks/tutorials/pandas-cookbook/04-weekday-bike-most-groupby-aggregate.ipynb index daed660972d..393606c49a2 100644 --- a/notebooks/tutorials/pandas-cookbook/04-weekday-bike-most-groupby-aggregate.ipynb +++ b/notebooks/tutorials/pandas-cookbook/04-weekday-bike-most-groupby-aggregate.ipynb @@ -46,7 +46,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-4\", port=9084, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-4\", port=9084, reset=True)" ] }, { @@ -66,7 +66,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -211,7 +211,7 @@ " name=\"bikes2\",\n", " asset_list=[sy.Asset(name=\"bikes\", data=df, mock=mock, mock_is_real=False)],\n", ")\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -231,7 +231,7 @@ }, "outputs": [], "source": [ - "user = root_domain_client.register(\n", + "user = root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -242,9 +242,9 @@ "\n", "# todo: give user data scientist role\n", "\n", - "guest_domain_client = node.client\n", + "guest_datasite_client = node.client\n", "\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -288,7 +288,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[0]" + "ds = guest_datasite_client.datasets[0]" ] }, { @@ -716,7 +716,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -736,7 +736,7 @@ }, "outputs": [], "source": [ - "notifications = root_domain_client.notifications.get_all_unread()" + "notifications = root_datasite_client.notifications.get_all_unread()" ] }, { @@ -811,7 +811,7 @@ }, "outputs": [], "source": [ - "real_data = root_domain_client.datasets[0].assets[0].data" + "real_data = root_datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/05-combining-dataframes-scraping-weather-data.ipynb b/notebooks/tutorials/pandas-cookbook/05-combining-dataframes-scraping-weather-data.ipynb index 0bff86b5c06..6a44c71caee 100644 --- a/notebooks/tutorials/pandas-cookbook/05-combining-dataframes-scraping-weather-data.ipynb +++ b/notebooks/tutorials/pandas-cookbook/05-combining-dataframes-scraping-weather-data.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-5\", port=9085, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-5\", port=9085, reset=True)" ] }, { @@ -74,7 +74,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -298,7 +298,7 @@ "outputs": [], "source": [ "dataset = sy.Dataset(name=\"test\", asset_list=assets)\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -330,7 +330,7 @@ }, "outputs": [], "source": [ - "user = root_domain_client.register(\n", + "user = root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -341,9 +341,9 @@ "\n", "# todo: give user data scientist role\n", "\n", - "guest_domain_client = node.client\n", + "guest_datasite_client = node.client\n", "\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -382,7 +382,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[-1]" + "ds = guest_datasite_client.datasets[-1]" ] }, { @@ -903,7 +903,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -923,7 +923,7 @@ }, "outputs": [], "source": [ - "notifications = root_domain_client.notifications.get_all_unread()" + "notifications = root_datasite_client.notifications.get_all_unread()" ] }, { @@ -999,8 +999,8 @@ "outputs": [], "source": [ "real_data1, real_data2 = (\n", - " root_domain_client.datasets[-1].assets[\"weather1\"].data,\n", - " root_domain_client.datasets[-1].assets[\"weather2\"].data,\n", + " root_datasite_client.datasets[-1].assets[\"weather1\"].data,\n", + " root_datasite_client.datasets[-1].assets[\"weather2\"].data,\n", ")" ] }, @@ -1070,7 +1070,7 @@ }, "outputs": [], "source": [ - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/06-string-operations-which-month-was-the-snowiest.ipynb b/notebooks/tutorials/pandas-cookbook/06-string-operations-which-month-was-the-snowiest.ipynb index 896f842e6cb..a2b1d086061 100644 --- a/notebooks/tutorials/pandas-cookbook/06-string-operations-which-month-was-the-snowiest.ipynb +++ b/notebooks/tutorials/pandas-cookbook/06-string-operations-which-month-was-the-snowiest.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-6\", port=9086, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-6\", port=9086, reset=True)" ] }, { @@ -74,7 +74,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -287,7 +287,7 @@ " sy.Asset(name=\"weather\", data=weather_2012_final, mock=mock, mock_is_real=False)\n", " ],\n", ")\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -319,7 +319,7 @@ }, "outputs": [], "source": [ - "user = root_domain_client.register(\n", + "user = root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -328,8 +328,8 @@ " website=\"https://www.caltech.edu/\",\n", ")\n", "# todo: give user data scientist role\n", - "guest_domain_client = node.client\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_datasite_client = node.client\n", + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -368,7 +368,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[0]" + "ds = guest_datasite_client.datasets[0]" ] }, { @@ -805,7 +805,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -826,7 +826,7 @@ }, "outputs": [], "source": [ - "notifications = domain_client.notifications.get_all_unread()" + "notifications = datasite_client.notifications.get_all_unread()" ] }, { @@ -901,7 +901,7 @@ }, "outputs": [], "source": [ - "real_data = domain_client.datasets[0].assets[0].data" + "real_data = datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/07-cleaning-up-messy-data.ipynb b/notebooks/tutorials/pandas-cookbook/07-cleaning-up-messy-data.ipynb index dab79f0c217..60f480b722a 100644 --- a/notebooks/tutorials/pandas-cookbook/07-cleaning-up-messy-data.ipynb +++ b/notebooks/tutorials/pandas-cookbook/07-cleaning-up-messy-data.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-7\", port=9087, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-7\", port=9087, reset=True)" ] }, { @@ -74,7 +74,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -307,7 +307,7 @@ " )\n", " ],\n", ")\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -327,7 +327,7 @@ }, "outputs": [], "source": [ - "user = root_domain_client.register(\n", + "user = root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -336,8 +336,8 @@ " website=\"https://www.caltech.edu/\",\n", ")\n", "# todo: give user data scientist role\n", - "guest_domain_client = node.client\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_datasite_client = node.client\n", + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -860,7 +860,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -881,7 +881,7 @@ }, "outputs": [], "source": [ - "notifications = domain_client.notifications.get_all_unread()" + "notifications = datasite_client.notifications.get_all_unread()" ] }, { @@ -956,7 +956,7 @@ }, "outputs": [], "source": [ - "real_data = domain_client.datasets[0].assets[0].data" + "real_data = datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/notebooks/tutorials/pandas-cookbook/08-how-to-deal-with-timestamps.ipynb b/notebooks/tutorials/pandas-cookbook/08-how-to-deal-with-timestamps.ipynb index 32beba9af48..3266e8a954d 100644 --- a/notebooks/tutorials/pandas-cookbook/08-how-to-deal-with-timestamps.ipynb +++ b/notebooks/tutorials/pandas-cookbook/08-how-to-deal-with-timestamps.ipynb @@ -56,7 +56,7 @@ }, "outputs": [], "source": [ - "node = sy.orchestra.launch(name=\"pandas-test-domain-8\", port=9088, reset=True)" + "node = sy.orchestra.launch(name=\"pandas-test-datasite-8\", port=9088, reset=True)" ] }, { @@ -77,7 +77,7 @@ }, "outputs": [], "source": [ - "root_domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "root_datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -328,7 +328,7 @@ " )\n", " ],\n", ")\n", - "root_domain_client.upload_dataset(dataset)" + "root_datasite_client.upload_dataset(dataset)" ] }, { @@ -361,7 +361,7 @@ }, "outputs": [], "source": [ - "user = root_domain_client.register(\n", + "user = root_datasite_client.register(\n", " name=\"Jane Doe\",\n", " email=\"jane@caltech.edu\",\n", " password=\"abc123\",\n", @@ -370,8 +370,8 @@ " website=\"https://www.caltech.edu/\",\n", ")\n", "# todo: give user data scientist role\n", - "guest_domain_client = node.client\n", - "guest_client = guest_domain_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" + "guest_datasite_client = node.client\n", + "guest_client = guest_datasite_client.login(email=\"jane@caltech.edu\", password=\"abc123\")" ] }, { @@ -425,7 +425,7 @@ }, "outputs": [], "source": [ - "ds = guest_domain_client.datasets[0]" + "ds = guest_datasite_client.datasets[0]" ] }, { @@ -811,7 +811,7 @@ }, "outputs": [], "source": [ - "domain_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" + "datasite_client = node.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, { @@ -832,7 +832,7 @@ }, "outputs": [], "source": [ - "notifications = domain_client.notifications.get_all_unread()" + "notifications = datasite_client.notifications.get_all_unread()" ] }, { @@ -907,7 +907,7 @@ }, "outputs": [], "source": [ - "real_data = domain_client.datasets[0].assets[0].data" + "real_data = datasite_client.datasets[0].assets[0].data" ] }, { diff --git a/packages/grid/backend/backend.dockerfile b/packages/grid/backend/backend.dockerfile index 606569c49f4..8c284a7369c 100644 --- a/packages/grid/backend/backend.dockerfile +++ b/packages/grid/backend/backend.dockerfile @@ -74,7 +74,7 @@ ENV \ # Syft APPDIR="/root/app" \ NODE_NAME="default_node_name" \ - NODE_TYPE="domain" \ + NODE_TYPE="datasite" \ SERVICE_NAME="backend" \ RELEASE="production" \ DEV_MODE="False" \ diff --git a/packages/grid/backend/grid/core/config.py b/packages/grid/backend/grid/core/config.py index 619637c06c6..f31deefce2a 100644 --- a/packages/grid/backend/grid/core/config.py +++ b/packages/grid/backend/grid/core/config.py @@ -99,11 +99,11 @@ def get_emails_enabled(self) -> Self: NODE_NAME: str = "default_node_name" STREAM_QUEUE: bool = False - NODE_TYPE: str = "domain" + NODE_TYPE: str = "datasite" OPEN_REGISTRATION: bool = True - # DOMAIN_ASSOCIATION_REQUESTS_AUTOMATICALLY_ACCEPTED: bool = True + # DATASITE_ASSOCIATION_REQUESTS_AUTOMATICALLY_ACCEPTED: bool = True USE_BLOB_STORAGE: bool = ( True if os.getenv("USE_BLOB_STORAGE", "false").lower() == "true" else False ) @@ -124,7 +124,7 @@ def get_emails_enabled(self) -> Self: # STORE_DB_ID: int = int(os.getenv("STORE_DB_ID", 0)) # LEDGER_DB_ID: int = int(os.getenv("LEDGER_DB_ID", 1)) # NETWORK_CHECK_INTERVAL: int = int(os.getenv("NETWORK_CHECK_INTERVAL", 60)) - # DOMAIN_CHECK_INTERVAL: int = int(os.getenv("DOMAIN_CHECK_INTERVAL", 60)) + # DATASITE_CHECK_INTERVAL: int = int(os.getenv("DATASITE_CHECK_INTERVAL", 60)) CONTAINER_HOST: str = str(os.getenv("CONTAINER_HOST", "docker")) MONGO_HOST: str = str(os.getenv("MONGO_HOST", "")) MONGO_PORT: int = int(os.getenv("MONGO_PORT", 27017)) diff --git a/packages/grid/backend/grid/core/node.py b/packages/grid/backend/grid/core/node.py index 926cbbc5556..2f9143a9469 100644 --- a/packages/grid/backend/grid/core/node.py +++ b/packages/grid/backend/grid/core/node.py @@ -1,7 +1,7 @@ # syft absolute from syft.abstract_node import NodeType -from syft.node.domain import Domain -from syft.node.domain import Node +from syft.node.datasite import Datasite +from syft.node.datasite import Node from syft.node.enclave import Enclave from syft.node.gateway import Gateway from syft.node.node import get_default_bucket_name @@ -79,7 +79,7 @@ def seaweedfs_config() -> SeaweedFSConfig: enable_warnings = get_enable_warnings() worker_classes = { - NodeType.DOMAIN: Domain, + NodeType.DATASITE: Datasite, NodeType.GATEWAY: Gateway, NodeType.ENCLAVE: Enclave, } diff --git a/packages/grid/backend/grid/start.sh b/packages/grid/backend/grid/start.sh index 4b3d5de4cf2..f77b419c82a 100755 --- a/packages/grid/backend/grid/start.sh +++ b/packages/grid/backend/grid/start.sh @@ -7,7 +7,7 @@ APP_MODULE=grid.main:app LOG_LEVEL=${LOG_LEVEL:-info} HOST=${HOST:-0.0.0.0} PORT=${PORT:-80} -NODE_TYPE=${NODE_TYPE:-domain} +NODE_TYPE=${NODE_TYPE:-datasite} APPDIR=${APPDIR:-$HOME/app} RELOAD="" DEBUG_CMD="" diff --git a/packages/grid/default.env b/packages/grid/default.env index 0aae09f1026..d601c369c13 100644 --- a/packages/grid/default.env +++ b/packages/grid/default.env @@ -1,7 +1,7 @@ #!/bin/bash -DOMAIN=localhost +DATASITE=localhost NODE_NAME=default_node_name -NODE_TYPE=domain +NODE_TYPE=datasite FRONTEND_TARGET=grid-ui-development PORT=80 HTTP_PORT=80 @@ -44,9 +44,9 @@ SMTP_HOST= SMTP_USERNAME= SMTP_PASSWORD= EMAIL_SENDER= -SERVER_HOST="https://${DOMAIN}" +SERVER_HOST="https://${DATASITE}" NETWORK_CHECK_INTERVAL=60 -DOMAIN_CHECK_INTERVAL=60 +DATASITE_CHECK_INTERVAL=60 ASSOCIATION_TIMEOUT=10 USERS_OPEN_REGISTRATION=False DEV_MODE=False diff --git a/packages/grid/devspace.yaml b/packages/grid/devspace.yaml index 8bbf3487daf..9b084663c29 100644 --- a/packages/grid/devspace.yaml +++ b/packages/grid/devspace.yaml @@ -117,16 +117,16 @@ dev: localPort: 3480 profiles: - - name: domain-low - description: "Deploy a low-side domain" + - name: datasite-low + description: "Deploy a low-side datasite" patches: - op: add path: deployments.syft.helm.values.node value: side: low - - name: domain-tunnel - description: "Deploy a domain with tunneling enabled" + - name: datasite-tunnel + description: "Deploy a datasite with tunneling enabled" patches: # enable rathole image - op: add @@ -143,7 +143,7 @@ profiles: # use rathole client-specific chart values - op: add path: deployments.syft.helm.valuesFiles - value: ./helm/examples/dev/domain.tunnel.yaml + value: ./helm/examples/dev/datasite.tunnel.yaml - name: gateway description: "Deploy a Gateway Node with tunnel enabled" @@ -194,7 +194,7 @@ profiles: value: 2334:2333 - name: gcp - description: "Deploy a high-side domain on GCP" + description: "Deploy a high-side datasite on GCP" patches: - op: replace path: deployments.syft.helm.valuesFiles @@ -202,7 +202,7 @@ profiles: - ./helm/examples/gcp/gcp.high.yaml - name: gcp-low - description: "Deploy a low-side domain on GCP" + description: "Deploy a low-side datasite on GCP" patches: - op: replace path: deployments.syft.helm.valuesFiles @@ -210,7 +210,7 @@ profiles: - ./helm/examples/gcp/gcp.low.yaml - name: azure - description: "Deploy a high-side domain on AKS" + description: "Deploy a high-side datasite on AKS" patches: - op: replace path: deployments.syft.helm.valuesFiles diff --git a/packages/grid/frontend/README.md b/packages/grid/frontend/README.md index e912f1dd172..d9c01c76c19 100644 --- a/packages/grid/frontend/README.md +++ b/packages/grid/frontend/README.md @@ -1,7 +1,7 @@ # PyGrid UI The PyGrid UI is the user interface that allows data owners to manage their -**deployed** PyGrid domains and networks. +**deployed** PyGrid datasites and networks. ## Installation diff --git a/packages/grid/frontend/src/_routes/(app)/account/+page.svelte b/packages/grid/frontend/src/_routes/(app)/account/+page.svelte index 10780f2c120..74960f8f47f 100644 --- a/packages/grid/frontend/src/_routes/(app)/account/+page.svelte +++ b/packages/grid/frontend/src/_routes/(app)/account/+page.svelte @@ -85,8 +85,8 @@

When you delete your user account all information relating to you will be deleted as well - as any permissions and requests. If you are the domain owner the domain node will be - deleted as well and will be closed to all users. To transfer ownership of a domain node + as any permissions and requests. If you are the datasite owner the datasite node will be + deleted as well and will be closed to all users. To transfer ownership of a datasite node before deleting your account you can follow the instructions here

@@ -134,10 +134,10 @@

Are you sure you want to delete your node?

- Because you are the domain owner, the domain node along with all uploaded datasets, user + Because you are the datasite owner, the datasite node along with all uploaded datasets, user accounts, and requests will be deleted. All network memberships will also be removed. If you - would like to keep this domain node but no longer want to be an owner press “cancel” and - follow the instructions here to transfer ownership of your domain node. + would like to keep this datasite node but no longer want to be an owner press “cancel” and + follow the instructions here to transfer ownership of your datasite node.

- {#if openModal === "domain_name"} - + {#if openModal === "datasite_name"} + {:else if openModal === "organization"} { diff --git a/packages/grid/frontend/src/_routes/(app)/users/[slug]/+page.svelte b/packages/grid/frontend/src/_routes/(app)/users/[slug]/+page.svelte index f893eece0e9..a261ab5ebae 100644 --- a/packages/grid/frontend/src/_routes/(app)/users/[slug]/+page.svelte +++ b/packages/grid/frontend/src/_routes/(app)/users/[slug]/+page.svelte @@ -3,7 +3,7 @@ import Badge from "$lib/components/Badge.svelte" import CaretLeft from "$lib/components/icons/CaretLeft.svelte" import { getInitials, getUserRole } from "$lib/utils" - import type { UserView } from "../../../../types/domain/users" + import type { UserView } from "../../../../types/datasite/users" import type { PageData } from "./$types" export let data: PageData diff --git a/packages/grid/frontend/src/_routes/(auth)/login/+page.svelte b/packages/grid/frontend/src/_routes/(auth)/login/+page.svelte index 1b6ddaf5b69..14cc012889d 100644 --- a/packages/grid/frontend/src/_routes/(auth)/login/+page.svelte +++ b/packages/grid/frontend/src/_routes/(auth)/login/+page.svelte @@ -2,10 +2,10 @@ import { enhance } from "$app/forms" import Button from "$lib/components/Button.svelte" import Modal from "$lib/components/Modal.svelte" - import DomainMetadataPanel from "$lib/components/authentication/DomainMetadataPanel.svelte" + import DatasiteMetadataPanel from "$lib/components/authentication/DatasiteMetadataPanel.svelte" import Input from "$lib/components/Input.svelte" - import DomainOnlineIndicator from "$lib/components/DomainOnlineIndicator.svelte" - import type { DomainOnlineStatus } from "../../../types/domain/onlineIndicator" + import DatasiteOnlineIndicator from "$lib/components/DatasiteOnlineIndicator.svelte" + import type { DatasiteOnlineStatus } from "../../../types/datasite/onlineIndicator" import type { PageData, ActionData } from "./$types" export let data: PageData @@ -13,13 +13,13 @@ const { metadata } = data - let status: DomainOnlineStatus = "online" + let status: DatasiteOnlineStatus = "online"
- +
@@ -38,12 +38,12 @@

{/if}
- +

{#if status === "pending"} Checking connection {:else} - Domain {status} + Datasite {status} {/if}

diff --git a/packages/grid/frontend/src/_routes/(auth)/signup/+page.svelte b/packages/grid/frontend/src/_routes/(auth)/signup/+page.svelte index 500249f1b8f..24a963b0fab 100644 --- a/packages/grid/frontend/src/_routes/(auth)/signup/+page.svelte +++ b/packages/grid/frontend/src/_routes/(auth)/signup/+page.svelte @@ -1,7 +1,7 @@ diff --git a/packages/grid/frontend/src/lib/components/Datasets/DatasetNoneFound.svelte b/packages/grid/frontend/src/lib/components/Datasets/DatasetNoneFound.svelte index fe02b87ef03..2e651c2ab3d 100644 --- a/packages/grid/frontend/src/lib/components/Datasets/DatasetNoneFound.svelte +++ b/packages/grid/frontend/src/lib/components/Datasets/DatasetNoneFound.svelte @@ -1,7 +1,7 @@

No Datasets Uploaded

- To begin adding datasets to this domain node please click the "+" button and follow + To begin adding datasets to this datasite node please click the "+" button and follow instructions.

diff --git a/packages/grid/frontend/src/lib/components/DomainOnlineIndicator.svelte b/packages/grid/frontend/src/lib/components/DatasiteOnlineIndicator.svelte similarity index 68% rename from packages/grid/frontend/src/lib/components/DomainOnlineIndicator.svelte rename to packages/grid/frontend/src/lib/components/DatasiteOnlineIndicator.svelte index 1232283ac6c..b534a481813 100644 --- a/packages/grid/frontend/src/lib/components/DomainOnlineIndicator.svelte +++ b/packages/grid/frontend/src/lib/components/DatasiteOnlineIndicator.svelte @@ -1,6 +1,6 @@
diff --git a/packages/grid/frontend/src/lib/components/Navigation/SideNavDOHandbook.svelte b/packages/grid/frontend/src/lib/components/Navigation/SideNavDOHandbook.svelte index f72f9dddd45..cef3c840ab9 100644 --- a/packages/grid/frontend/src/lib/components/Navigation/SideNavDOHandbook.svelte +++ b/packages/grid/frontend/src/lib/components/Navigation/SideNavDOHandbook.svelte @@ -27,7 +27,7 @@

Data Owner handbook

Check out the data owner handbook to learn more tips & tricks about how to manage your - domain node. + datasite node.

diff --git a/packages/grid/frontend/src/lib/components/Navigation/TopNav.svelte b/packages/grid/frontend/src/lib/components/Navigation/TopNav.svelte index 9e127766cf3..1a3dc762b4e 100644 --- a/packages/grid/frontend/src/lib/components/Navigation/TopNav.svelte +++ b/packages/grid/frontend/src/lib/components/Navigation/TopNav.svelte @@ -13,7 +13,7 @@ { href: "", icon: BellIcon, disabled: true }, ] - $: domainInitials = getInitials(metadata.name) + $: datasiteInitials = getInitials(metadata.name) $: userInitials = getInitials(user.name) @@ -21,7 +21,7 @@ class="w-full py-2 px-6 flex items-center justify-between tablet:justify-end shadow-topbar-1 tablet:shadow-none" >
- +
    {#each links as link} diff --git a/packages/grid/frontend/src/lib/components/OnboardingModal.svelte b/packages/grid/frontend/src/lib/components/OnboardingModal.svelte index 8e381c18630..40a885c8634 100644 --- a/packages/grid/frontend/src/lib/components/OnboardingModal.svelte +++ b/packages/grid/frontend/src/lib/components/OnboardingModal.svelte @@ -23,15 +23,15 @@ website: "", } - let domainSettings = { + let datasiteSettings = { name: "", description: "", organization: "", on_board: false, } - let checkRequiredDomainFields = () => { - return domainSettings.name !== "" ? true : false + let checkRequiredDatasiteFields = () => { + return datasiteSettings.name !== "" ? true : false } let checkRequiredUserFields = () => { @@ -49,7 +49,7 @@ headers: { "Content-Type": "application/json", }, - body: JSON.stringify(domainSettings), + body: JSON.stringify(datasiteSettings), }) await fetch(`/_syft_api/users/${userSettings.id}`, { @@ -86,7 +86,7 @@ website: "", } - domainSettings = { + datasiteSettings = { name: "", description: "", organization: "", @@ -151,7 +151,7 @@
-

Domain Profile

+

Datasite Profile

Step 2 of 4

@@ -182,28 +182,28 @@

- Let's begin by describing some basic information about this domain + Let's begin by describing some basic information about this datasite node. This information will be shown to outside users to help them - find and understand what your domain offers. + find and understand what your datasite offers.

@@ -232,7 +232,7 @@

- Now that we have described our domain, let's update our password and + Now that we have described our datasite, let's update our password and describe some basic information about ourselves for our "User Profile". User profile information will be shown to teammates and collaborators when working on studies together. diff --git a/packages/grid/frontend/src/lib/components/Users/UserCreateModal.svelte b/packages/grid/frontend/src/lib/components/Users/UserCreateModal.svelte index 77ebcc3fb70..60274ed4cbd 100644 --- a/packages/grid/frontend/src/lib/components/Users/UserCreateModal.svelte +++ b/packages/grid/frontend/src/lib/components/Users/UserCreateModal.svelte @@ -16,13 +16,13 @@ roleId: 2, title: "Data Scientist", description: - "This role is for users who will be performing computations on your datasets. They may be users you know directly or those who found your domain through search and discovery. By default this user can see a list of your datasets and can request to get results.", + "This role is for users who will be performing computations on your datasets. They may be users you know directly or those who found your datasite through search and discovery. By default this user can see a list of your datasets and can request to get results.", }, { roleId: 32, title: "Data Owner", description: - "This role is for users on your team who will be responsible for uploading data to the domain.", + "This role is for users on your team who will be responsible for uploading data to the datasite.", }, { roleId: 128, @@ -105,7 +105,7 @@

To begin let's select the role this user is going to have on your - domain node. + datasite node.

{#each cardsContent as { title, description, roleId }} @@ -221,8 +221,8 @@ Welcome to {metadata?.name} {name},
- You are formally invited you to join {metadata?.name} Domain. Below is - your login credentials and the URL to the domain. After logging in you + You are formally invited you to join {metadata?.name} Datasite. Below is + your login credentials and the URL to the datasite. After logging in you will be prompted to customize your account.

{href} diff --git a/packages/grid/frontend/src/lib/components/Users/UserListItem.svelte b/packages/grid/frontend/src/lib/components/Users/UserListItem.svelte index b5ca6737d6f..ad34edaf1cb 100644 --- a/packages/grid/frontend/src/lib/components/Users/UserListItem.svelte +++ b/packages/grid/frontend/src/lib/components/Users/UserListItem.svelte @@ -1,6 +1,6 @@
{#if metadata}
- +
- +

{metadata.name}

{#if metadata.organization} diff --git a/packages/grid/frontend/src/lib/utils.ts b/packages/grid/frontend/src/lib/utils.ts index 2ffed30dec5..38ffbc1c284 100644 --- a/packages/grid/frontend/src/lib/utils.ts +++ b/packages/grid/frontend/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { ServiceRoles } from "../types/domain/users" +import { ServiceRoles } from "../types/datasite/users" import { COOKIES } from "./constants" import type { CookieSerializeOptions } from "cookie" import type { Cookies } from "@sveltejs/kit" diff --git a/packages/grid/frontend/src/routes/[...all]/+page.svelte b/packages/grid/frontend/src/routes/[...all]/+page.svelte index b1bb3b1cb01..58d3d7ba901 100644 --- a/packages/grid/frontend/src/routes/[...all]/+page.svelte +++ b/packages/grid/frontend/src/routes/[...all]/+page.svelte @@ -1,6 +1,6 @@
- PyGrid + Syft UI
diff --git a/packages/grid/frontend/static/assets/2023_welcome_to_pygrid.png b/packages/grid/frontend/static/assets/2023_welcome_to_syft_ui.png similarity index 100% rename from packages/grid/frontend/static/assets/2023_welcome_to_pygrid.png rename to packages/grid/frontend/static/assets/2023_welcome_to_syft_ui.png diff --git a/packages/grid/frontend/static/images/pygrid-logo.png b/packages/grid/frontend/static/images/pygrid-logo.png deleted file mode 100644 index d783862d8739822401f7da1ed9376ff478902f6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4507 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 diff --git a/packages/grid/frontend/tests/e2e/page.spec.ts b/packages/grid/frontend/tests/e2e/page.spec.ts index 9df184cda24..993ad4fa818 100644 --- a/packages/grid/frontend/tests/e2e/page.spec.ts +++ b/packages/grid/frontend/tests/e2e/page.spec.ts @@ -3,11 +3,11 @@ import { test, expect } from "@playwright/test" test("should display the page correctly", async ({ page }) => { await page.goto("/") const title = await page.title() - expect(title).toBe("PyGrid") + expect(title).toBe("Syft UI") }) test("should navigate to login page", async ({ page }) => { await page.goto("/login") const title = await page.title() - expect(title).toBe("PyGrid") + expect(title).toBe("Syft UI") }) diff --git a/packages/syft/PYPI.md b/packages/syft/PYPI.md index a28654af8e2..48e37c0e8d8 100644 --- a/packages/syft/PYPI.md +++ b/packages/syft/PYPI.md @@ -138,7 +138,7 @@ helm install ... --set ingress.class="gce" # Install Notes - PySyft 0.8.6 Requires: 🐍 `python 3.10 - 3.12` - Run: `pip install -U syft` -- PyGrid Requires: 🐳 `docker` or ☸️ `kubernetes` +- Syft Server Requires: 🐳 `docker` or ☸️ `kubernetes` # Versions @@ -159,7 +159,7 @@ Deprecated: - `0.5.1` - Course 2 + M1 Hotfix - `0.2.0` - `0.5.0` -PySyft and PyGrid use the same `version` and its best to match them up where possible. We release weekly betas which can be used in each context: +PySyft and Syft Server use the same `version` and its best to match them up where possible. We release weekly betas which can be used in each context: PySyft (Stable): `pip install -U syft` @@ -230,7 +230,7 @@ No more cold calls to get `access` to a dataset. No more weeks of `wait times` t - Deploy to GCP - Deploy to Kubernetes - Customize Networking -- Modify PyGrid UI +- Modify Syft UI diff --git a/packages/syft/src/syft/client/client.py b/packages/syft/src/syft/client/client.py index 0a6f92241b4..0406189d119 100644 --- a/packages/syft/src/syft/client/client.py +++ b/packages/syft/src/syft/client/client.py @@ -50,7 +50,7 @@ from ..service.user.user import UserView from ..service.user.user_roles import ServiceRole from ..service.user.user_service import UserService -from ..types.grid_url import GridURL +from ..types.server_url import ServerURL from ..types.syft_object import SYFT_OBJECT_VERSION_3 from ..types.uid import UID from ..util.telemetry import instrument @@ -73,12 +73,12 @@ from ..service.network.server_peer import ServerPeer -def upgrade_tls(url: GridURL, response: Response) -> GridURL: +def upgrade_tls(url: ServerURL, response: Response) -> ServerURL: try: if response.url.startswith("https://") and url.protocol == "http": # we got redirected to https - https_url = GridURL.from_url(response.url).with_path("") - logger.debug(f"GridURL Upgraded to HTTPS. {https_url}") + https_url = ServerURL.from_url(response.url).with_path("") + logger.debug(f"ServerURL Upgraded to HTTPS. {https_url}") return https_url except Exception as e: print(f"Failed to upgrade to HTTPS. {e}") @@ -115,8 +115,8 @@ def forward_message_to_proxy( API_PATH = "/api/v2" -DEFAULT_PYGRID_PORT = 80 -DEFAULT_PYGRID_ADDRESS = f"http://localhost:{DEFAULT_PYGRID_PORT}" +DEFAULT_SYFT_UI_PORT = 80 +DEFAULT_SYFT_UI_ADDRESS = f"http://localhost:{DEFAULT_SYFT_UI_PORT}" INTERNAL_PROXY_TO_RATHOLE = "http://proxy:80/rtunnel/" @@ -135,7 +135,7 @@ class HTTPConnection(ServerConnection): __canonical_name__ = "HTTPConnection" __version__ = SYFT_OBJECT_VERSION_3 - url: GridURL + url: ServerURL proxy_target_uid: UID | None = None routes: type[Routes] = Routes session_cache: Session | None = None @@ -146,8 +146,8 @@ class HTTPConnection(ServerConnection): @classmethod def make_url(cls, v: Any) -> Any: return ( - GridURL.from_url(v).as_container_host() - if isinstance(v, str | GridURL) + ServerURL.from_url(v).as_container_host() + if isinstance(v, str | ServerURL) else v ) @@ -161,7 +161,7 @@ def with_proxy(self, proxy_target_uid: UID) -> Self: rtunnel_token=self.rtunnel_token, ) - def stream_via(self, proxy_uid: UID, url_path: str) -> GridURL: + def stream_via(self, proxy_uid: UID, url_path: str) -> ServerURL: # Update the presigned url path to # // # url_path_bytes = _serialize(url_path, to_bytes=True) @@ -174,10 +174,10 @@ def get_cache_key(self) -> str: return str(self.url) @property - def api_url(self) -> GridURL: + def api_url(self) -> ServerURL: return self.url.with_path(self.routes.ROUTE_API_CALL.value) - def to_blob_route(self, path: str, **kwargs: Any) -> GridURL: + def to_blob_route(self, path: str, **kwargs: Any) -> ServerURL: _path = self.routes.ROUTE_BLOB_STORE.value + path return self.url.with_path(_path) @@ -202,7 +202,7 @@ def _make_get( if self.rtunnel_token: self.headers = {} if self.headers is None else self.headers - url = GridURL.from_url(INTERNAL_PROXY_TO_RATHOLE) + url = ServerURL.from_url(INTERNAL_PROXY_TO_RATHOLE) self.headers["Host"] = self.url.host_or_ip url = url.with_path(path) @@ -231,7 +231,7 @@ def _make_get_no_params(self, path: str, stream: bool = False) -> bytes | Iterab if self.rtunnel_token: self.headers = {} if self.headers is None else self.headers - url = GridURL.from_url(INTERNAL_PROXY_TO_RATHOLE) + url = ServerURL.from_url(INTERNAL_PROXY_TO_RATHOLE) self.headers["Host"] = self.url.host_or_ip url = url.with_path(path) @@ -262,7 +262,7 @@ def _make_put( url = self.url if self.rtunnel_token: - url = GridURL.from_url(INTERNAL_PROXY_TO_RATHOLE) + url = ServerURL.from_url(INTERNAL_PROXY_TO_RATHOLE) self.headers = {} if self.headers is None else self.headers self.headers["Host"] = self.url.host_or_ip @@ -294,7 +294,7 @@ def _make_post( url = self.url if self.rtunnel_token: - url = GridURL.from_url(INTERNAL_PROXY_TO_RATHOLE) + url = ServerURL.from_url(INTERNAL_PROXY_TO_RATHOLE) self.headers = {} if self.headers is None else self.headers self.headers["Host"] = self.url.host_or_ip @@ -409,7 +409,7 @@ def make_call(self, signed_call: SignedSyftAPICall) -> Any | SyftError: msg_bytes: bytes = _serialize(obj=signed_call, to_bytes=True) if self.rtunnel_token: - api_url = GridURL.from_url(INTERNAL_PROXY_TO_RATHOLE) + api_url = ServerURL.from_url(INTERNAL_PROXY_TO_RATHOLE) api_url = api_url.with_path(self.routes.ROUTE_API_CALL.value) self.headers = {} if self.headers is None else self.headers self.headers["Host"] = self.url.host_or_ip @@ -484,12 +484,12 @@ def get_server_metadata( else: return self.server.metadata.to(ServerMetadataJSON) - def to_blob_route(self, path: str, host: str | None = None) -> GridURL: + def to_blob_route(self, path: str, host: str | None = None) -> ServerURL: # TODO: FIX! if host is not None: - return GridURL(host_or_ip=host, port=8333).with_path(path) + return ServerURL(host_or_ip=host, port=8333).with_path(path) else: - return GridURL(port=8333).with_path(path) + return ServerURL(port=8333).with_path(path) def get_api( self, @@ -711,8 +711,8 @@ def verify_key(self) -> SyftVerifyKey: return self.credentials.verify_key @classmethod - def from_url(cls, url: str | GridURL) -> Self: - return cls(connection=HTTPConnection(url=GridURL.from_url(url))) + def from_url(cls, url: str | ServerURL) -> Self: + return cls(connection=HTTPConnection(url=ServerURL.from_url(url))) @classmethod def from_server(cls, server: AbstractServer) -> Self: @@ -1060,14 +1060,14 @@ def refresh_callback() -> SyftAPI: @instrument def connect( - url: str | GridURL = DEFAULT_PYGRID_ADDRESS, + url: str | ServerURL = DEFAULT_SYFT_UI_ADDRESS, server: AbstractServer | None = None, port: int | None = None, ) -> SyftClient: if server: connection = PythonConnection(server=server) else: - url = GridURL.from_url(url) + url = ServerURL.from_url(url) if isinstance(port, int | str): url.set_port(int(port)) connection = HTTPConnection(url=url) @@ -1082,7 +1082,7 @@ def connect( @instrument def register( - url: str | GridURL, + url: str | ServerURL, port: int, name: str, email: str, @@ -1103,7 +1103,7 @@ def register( @instrument def login_as_guest( # HTTPConnection - url: str | GridURL = DEFAULT_PYGRID_ADDRESS, + url: str | ServerURL = DEFAULT_SYFT_UI_ADDRESS, port: int | None = None, # PythonConnection server: AbstractServer | None = None, @@ -1131,7 +1131,7 @@ def login_as_guest( def login( email: str, # HTTPConnection - url: str | GridURL = DEFAULT_PYGRID_ADDRESS, + url: str | ServerURL = DEFAULT_SYFT_UI_ADDRESS, port: int | None = None, # PythonConnection server: AbstractServer | None = None, diff --git a/packages/syft/src/syft/client/registry.py b/packages/syft/src/syft/client/registry.py index 20707f74389..48627172eeb 100644 --- a/packages/syft/src/syft/client/registry.py +++ b/packages/syft/src/syft/client/registry.py @@ -17,7 +17,7 @@ from ..service.network.server_peer import ServerPeer from ..service.network.server_peer import ServerPeerConnectionStatus from ..service.response import SyftException -from ..types.grid_url import GridURL +from ..types.server_url import ServerURL from ..util.constants import DEFAULT_TIMEOUT from .client import SyftClient as Client @@ -77,7 +77,7 @@ def check_network(network: dict) -> dict[Any, Any] | None: url = "http://" + network["host_or_ip"] + ":" + str(network["port"]) + "/" try: res = requests.get(url, timeout=DEFAULT_TIMEOUT) # nosec - online = "This is a PyGrid Network server." in res.text + online = "This is a Syft Gateway server." in res.text except Exception: online = False @@ -165,8 +165,8 @@ def create_client(network: dict[str, Any]) -> Client: port = int(network["port"]) protocol = network["protocol"] host_or_ip = network["host_or_ip"] - grid_url = GridURL(port=port, protocol=protocol, host_or_ip=host_or_ip) - client = connect(url=str(grid_url)) + server_url = ServerURL(port=port, protocol=protocol, host_or_ip=host_or_ip) + client = connect(url=str(server_url)) return client.guest() except Exception as e: raise SyftException(f"Failed to login with: {network}. {e}") @@ -212,7 +212,7 @@ def check_network(network: dict) -> dict[Any, Any] | None: url = "http://" + network["host_or_ip"] + ":" + str(network["port"]) + "/" try: res = requests.get(url, timeout=DEFAULT_TIMEOUT) - online = "This is a PyGrid Network server." in res.text + online = "This is a Syft Gateway server." in res.text except Exception: online = False @@ -431,8 +431,8 @@ def create_client(enclave: dict[str, Any]) -> Client: port = int(enclave["port"]) protocol = enclave["protocol"] host_or_ip = enclave["host_or_ip"] - grid_url = GridURL(port=port, protocol=protocol, host_or_ip=host_or_ip) - client = connect(url=str(grid_url)) + server_url = ServerURL(port=port, protocol=protocol, host_or_ip=host_or_ip) + client = connect(url=str(server_url)) return client.guest() except Exception as e: raise SyftException(f"Failed to login with: {enclave}. {e}") diff --git a/packages/syft/src/syft/protocol/data_protocol.py b/packages/syft/src/syft/protocol/data_protocol.py index 4cf71ab8ac7..0590db599bc 100644 --- a/packages/syft/src/syft/protocol/data_protocol.py +++ b/packages/syft/src/syft/protocol/data_protocol.py @@ -152,12 +152,13 @@ def read_history(self) -> dict: return protocol_history def save_history(self, history: dict) -> None: - for file_path in protocol_release_dir().iterdir(): - for version in self.read_json(file_path): - # Skip adding file if the version is not part of the history - if version not in history.keys(): - continue - history[version] = {"release_name": file_path.name} + if os.path.exists(protocol_release_dir()): + for file_path in protocol_release_dir().iterdir(): + for version in self.read_json(file_path): + # Skip adding file if the version is not part of the history + if version not in history.keys(): + continue + history[version] = {"release_name": file_path.name} self.file_path.write_text(json.dumps(history, indent=2) + "\n") @property diff --git a/packages/syft/src/syft/protocol/releases/.gitignore b/packages/syft/src/syft/protocol/releases/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/syft/src/syft/service/network/network_service.py b/packages/syft/src/syft/service/network/network_service.py index c712e728985..1d313bbe362 100644 --- a/packages/syft/src/syft/service/network/network_service.py +++ b/packages/syft/src/syft/service/network/network_service.py @@ -23,7 +23,7 @@ from ...store.document_store import PartitionKey from ...store.document_store import PartitionSettings from ...store.document_store import QueryKeys -from ...types.grid_url import GridURL +from ...types.server_url import ServerURL from ...types.transforms import TransformContext from ...types.transforms import keep from ...types.transforms import make_set_default @@ -948,7 +948,7 @@ def _get_association_requests_by_peer_id( SERVICE_TO_TYPES[NetworkService].update({ServerPeer}) -def from_grid_url(context: TransformContext) -> TransformContext: +def from_server_url(context: TransformContext) -> TransformContext: if context.obj is not None and context.output is not None: url = context.obj.url.as_container_host() context.output["host_or_ip"] = url.host_or_ip @@ -964,7 +964,7 @@ def from_grid_url(context: TransformContext) -> TransformContext: @transform(HTTPConnection, HTTPServerRoute) def http_connection_to_server_route() -> list[Callable]: - return [from_grid_url] + return [from_server_url] def get_python_server_route(context: TransformContext) -> TransformContext: @@ -993,7 +993,7 @@ def server_route_to_python_connection( def server_route_to_http_connection( obj: Any, context: TransformContext | None = None ) -> list[Callable]: - url = GridURL( + url = ServerURL( protocol=obj.protocol, host_or_ip=obj.host_or_ip, port=obj.port ).as_container_host() return HTTPConnection( diff --git a/packages/syft/src/syft/service/network/reverse_tunnel_service.py b/packages/syft/src/syft/service/network/reverse_tunnel_service.py index 839f29933c5..e155dd3f5b5 100644 --- a/packages/syft/src/syft/service/network/reverse_tunnel_service.py +++ b/packages/syft/src/syft/service/network/reverse_tunnel_service.py @@ -1,5 +1,5 @@ # relative -from ...types.grid_url import GridURL +from ...types.server_url import ServerURL from .rathole_config_builder import RatholeConfigBuilder from .routes import ServerRoute from .server_peer import ServerPeer @@ -21,7 +21,7 @@ def set_client_config( + f"Peer: {self_server_peer} has no rathole route: {rathole_route}" ) - remote_url = GridURL( + remote_url = ServerURL( host_or_ip=remote_server_route.host_or_ip, port=remote_server_route.port ) rathole_remote_addr = remote_url.as_container_host() diff --git a/packages/syft/src/syft/store/blob_storage/__init__.py b/packages/syft/src/syft/store/blob_storage/__init__.py index 2affff7a36a..891aee269d0 100644 --- a/packages/syft/src/syft/store/blob_storage/__init__.py +++ b/packages/syft/src/syft/store/blob_storage/__init__.py @@ -64,7 +64,7 @@ from ...types.blob_storage import CreateBlobStorageEntry from ...types.blob_storage import DEFAULT_CHUNK_SIZE from ...types.blob_storage import SecureFilePathLocation -from ...types.grid_url import GridURL +from ...types.server_url import ServerURL from ...types.syft_migration import migrate from ...types.syft_object import SYFT_OBJECT_VERSION_2 from ...types.syft_object import SYFT_OBJECT_VERSION_3 @@ -119,7 +119,7 @@ def read(self, _deserialize: bool = True) -> SyftObject | SyftError: def syft_iter_content( - blob_url: str | GridURL, + blob_url: str | ServerURL, chunk_size: int, max_retries: int = MAX_RETRIES, timeout: int = DEFAULT_TIMEOUT, @@ -154,7 +154,7 @@ class BlobRetrievalByURLV4(BlobRetrieval): __canonical_name__ = "BlobRetrievalByURL" __version__ = SYFT_OBJECT_VERSION_4 - url: GridURL | str + url: ServerURL | str @serializable() @@ -162,7 +162,7 @@ class BlobRetrievalByURL(BlobRetrieval): __canonical_name__ = "BlobRetrievalByURL" __version__ = SYFT_OBJECT_VERSION_5 - url: GridURL | str + url: ServerURL | str proxy_server_uid: UID | None = None def read(self) -> SyftObject | SyftError: @@ -192,7 +192,7 @@ def _read_data( user_verify_key=self.syft_client_verify_key, ) - if api and api.connection and isinstance(self.url, GridURL): + if api and api.connection and isinstance(self.url, ServerURL): if self.proxy_server_uid is None: blob_url = api.connection.to_blob_route( self.url.url_path, host=self.url.host_or_ip diff --git a/packages/syft/src/syft/store/blob_storage/seaweedfs.py b/packages/syft/src/syft/store/blob_storage/seaweedfs.py index 53730b1780e..35b330f6bb7 100644 --- a/packages/syft/src/syft/store/blob_storage/seaweedfs.py +++ b/packages/syft/src/syft/store/blob_storage/seaweedfs.py @@ -37,7 +37,7 @@ from ...types.blob_storage import CreateBlobStorageEntry from ...types.blob_storage import SeaweedSecureFilePathLocation from ...types.blob_storage import SecureFilePathLocation -from ...types.grid_url import GridURL +from ...types.server_url import ServerURL from ...types.syft_object import SYFT_OBJECT_VERSION_4 from ...types.uid import UID from ...util.constants import DEFAULT_TIMEOUT @@ -55,7 +55,7 @@ class SeaweedFSBlobDeposit(BlobDeposit): __canonical_name__ = "SeaweedFSBlobDeposit" __version__ = SYFT_OBJECT_VERSION_4 - urls: list[GridURL] + urls: list[ServerURL] size: int proxy_server_uid: UID | None = None @@ -187,8 +187,8 @@ class SeaweedFSClientConfig(BlobStorageClientConfig): @property def endpoint_url(self) -> str: - grid_url = GridURL(host_or_ip=self.host, port=self.port) - return grid_url.url + server_url = ServerURL(host_or_ip=self.host, port=self.port) + return server_url.url @property def mount_url(self) -> str: @@ -280,7 +280,7 @@ def write(self, obj: BlobStorageEntry) -> BlobDeposit: total_parts = math.ceil(obj.file_size / DEFAULT_FILE_PART_SIZE) urls = [ - GridURL.from_url( + ServerURL.from_url( self.client.generate_presigned_url( ClientMethod="upload_part", Params={ diff --git a/packages/syft/src/syft/types/blob_storage.py b/packages/syft/src/syft/types/blob_storage.py index c05a6a0efe4..364a3428b0b 100644 --- a/packages/syft/src/syft/types/blob_storage.py +++ b/packages/syft/src/syft/types/blob_storage.py @@ -33,7 +33,7 @@ from ..service.response import SyftError from ..service.response import SyftException from ..service.service import from_api_or_context -from ..types.grid_url import GridURL +from ..types.server_url import ServerURL from ..types.transforms import drop from ..types.transforms import keep from ..types.transforms import make_set_default @@ -261,7 +261,7 @@ def generate_url( from ..store.blob_storage import BlobRetrievalByURL return BlobRetrievalByURL( - url=GridURL.from_url(url), file_name=Path(self.path).name, type_=type_ + url=ServerURL.from_url(url), file_name=Path(self.path).name, type_=type_ ) except BotoClientError as e: raise SyftException(e) diff --git a/packages/syft/src/syft/types/grid_url.py b/packages/syft/src/syft/types/server_url.py similarity index 88% rename from packages/syft/src/syft/types/grid_url.py rename to packages/syft/src/syft/types/server_url.py index 510a95d36c4..0ccaeb10c23 100644 --- a/packages/syft/src/syft/types/grid_url.py +++ b/packages/syft/src/syft/types/server_url.py @@ -20,10 +20,10 @@ @serializable(attrs=["protocol", "host_or_ip", "port", "path", "query"]) -class GridURL: +class ServerURL: @classmethod - def from_url(cls, url: str | GridURL) -> GridURL: - if isinstance(url, GridURL): + def from_url(cls, url: str | ServerURL) -> ServerURL: + if isinstance(url, ServerURL): return url try: # urlparse doesnt handle no protocol properly @@ -38,7 +38,7 @@ def from_url(cls, url: str | GridURL) -> GridURL: host_or_ip = host_or_ip_parts[0] if parts.scheme == "https": port = 443 - return GridURL( + return ServerURL( host_or_ip=host_or_ip, path=parts.path, port=port, @@ -46,7 +46,7 @@ def from_url(cls, url: str | GridURL) -> GridURL: query=getattr(parts, "query", ""), ) except Exception as e: - logger.error(f"Failed to convert url: {url} to GridURL. {e}") + logger.error(f"Failed to convert url: {url} to ServerURL. {e}") raise e def __init__( @@ -61,11 +61,11 @@ def __init__( # port was included in the supplied host_or_ip:port combo passed in earlier match_port = re.search(":[0-9]{1,5}", host_or_ip) if match_port: - sub_grid_url: GridURL = GridURL.from_url(host_or_ip) - host_or_ip = str(sub_grid_url.host_or_ip) # type: ignore - port = int(sub_grid_url.port) # type: ignore - protocol = str(sub_grid_url.protocol) # type: ignore - path = str(sub_grid_url.path) # type: ignore + sub_server_url: ServerURL = ServerURL.from_url(host_or_ip) + host_or_ip = str(sub_server_url.host_or_ip) # type: ignore + port = int(sub_server_url.port) # type: ignore + protocol = str(sub_server_url.protocol) # type: ignore + path = str(sub_server_url.path) # type: ignore prtcl_pattrn = "://" if prtcl_pattrn in host_or_ip: @@ -146,7 +146,7 @@ def url_no_protocol(self) -> str: def url_path(self) -> str: return f"{self.path}{self.query_string}" - def to_tls(self) -> GridURL: + def to_tls(self) -> ServerURL: if self.protocol == "https": return self @@ -170,7 +170,7 @@ def __str__(self) -> str: def __hash__(self) -> int: return hash(self.__str__()) - def __copy__(self) -> GridURL: + def __copy__(self) -> ServerURL: return self.__class__.from_url(self.url) def set_port(self, port: int) -> Self: diff --git a/packages/syft/src/syft/types/transforms.py b/packages/syft/src/syft/types/transforms.py index fa5d8063f4a..60e9722a029 100644 --- a/packages/syft/src/syft/types/transforms.py +++ b/packages/syft/src/syft/types/transforms.py @@ -11,7 +11,7 @@ from ..server.credentials import SyftVerifyKey from ..service.context import AuthedServiceContext from ..service.context import ServerServiceContext -from .grid_url import GridURL +from .server_url import ServerURL from .syft_object import Context from .syft_object import SyftBaseObject from .syft_object_registry import SyftObjectRegistry @@ -157,7 +157,7 @@ def generate_action_object_id(context: TransformContext) -> TransformContext: def validate_url(context: TransformContext) -> TransformContext: if context.output and context.output["url"] is not None: - context.output["url"] = GridURL.from_url(context.output["url"]).url_no_port + context.output["url"] = ServerURL.from_url(context.output["url"]).url_no_port return context @@ -167,11 +167,11 @@ def validate_email(context: TransformContext) -> TransformContext: return context -def str_url_to_grid_url(context: TransformContext) -> TransformContext: +def str_url_to_server_url(context: TransformContext) -> TransformContext: if context.output: url = context.output.get("url", None) if url is not None and isinstance(url, str): - context.output["url"] = GridURL.from_url(str) + context.output["url"] = ServerURL.from_url(str) return context diff --git a/packages/syft/tests/conftest.py b/packages/syft/tests/conftest.py index 396c0c6f8e4..dcca9d8f113 100644 --- a/packages/syft/tests/conftest.py +++ b/packages/syft/tests/conftest.py @@ -113,10 +113,11 @@ def stage_protocol(protocol_file: Path): dp.save_history(dp.protocol_history) # Cleanup release dir, remove unused released files - for _file_path in protocol_release_dir().iterdir(): - for version in dp.read_json(_file_path): - if version not in dp.protocol_history.keys(): - _file_path.unlink() + if os.path.exists(protocol_release_dir()): + for _file_path in protocol_release_dir().iterdir(): + for version in dp.read_json(_file_path): + if version not in dp.protocol_history.keys(): + _file_path.unlink() @pytest.fixture diff --git a/packages/syft/tests/syft/migrations/protocol_communication_test.py b/packages/syft/tests/syft/migrations/protocol_communication_test.py index 81bd074f5b2..d7b9bd3ad79 100644 --- a/packages/syft/tests/syft/migrations/protocol_communication_test.py +++ b/packages/syft/tests/syft/migrations/protocol_communication_test.py @@ -1,5 +1,6 @@ # stdlib from copy import deepcopy +import os from pathlib import Path from unittest import mock @@ -174,10 +175,11 @@ def my_stage_protocol(protocol_file: Path): dp.save_history(dp.protocol_history) # Cleanup release dir, remove unused released files - for _file_path in protocol_release_dir().iterdir(): - for version in dp.read_json(_file_path): - if version not in dp.protocol_history.keys(): - _file_path.unlink() + if os.path.exists(protocol_release_dir()): + for _file_path in protocol_release_dir().iterdir(): + for version in dp.read_json(_file_path): + if version not in dp.protocol_history.keys(): + _file_path.unlink() @pytest.mark.skip( diff --git a/packages/syft/tests/syft/grid_url_test.py b/packages/syft/tests/syft/server_url_test.py similarity index 61% rename from packages/syft/tests/syft/grid_url_test.py rename to packages/syft/tests/syft/server_url_test.py index bb87c2d0d07..c23670f9f12 100644 --- a/packages/syft/tests/syft/grid_url_test.py +++ b/packages/syft/tests/syft/server_url_test.py @@ -2,7 +2,7 @@ import pytest # syft absolute -from syft.types.grid_url import GridURL +from syft.types.server_url import ServerURL test_suite = [ ("http://0.0.0.0", 8081, "http://0.0.0.0:8081"), @@ -16,12 +16,12 @@ @pytest.mark.parametrize("url, port, ground_truth", test_suite) -def test_grid_url(url, port, ground_truth) -> None: +def test_server_url(url, port, ground_truth) -> None: if not url and not port: - assert GridURL().base_url == ground_truth + assert ServerURL().base_url == ground_truth elif not url: - assert GridURL(port=port).base_url == ground_truth + assert ServerURL(port=port).base_url == ground_truth elif not port: - assert GridURL(host_or_ip=url).base_url == ground_truth + assert ServerURL(host_or_ip=url).base_url == ground_truth else: - assert GridURL(host_or_ip=url, port=port).base_url == ground_truth + assert ServerURL(host_or_ip=url, port=port).base_url == ground_truth diff --git a/tests/integration/frontend/frontend_start_test.py b/tests/integration/frontend/frontend_start_test.py index 198a45a9d5f..54490db450f 100644 --- a/tests/integration/frontend/frontend_start_test.py +++ b/tests/integration/frontend/frontend_start_test.py @@ -14,7 +14,7 @@ @pytest.mark.frontend def test_serves_datasite_frontend() -> None: - title_str = "PyGrid" + title_str = "Syft UI" url = f"http://{HOST_IP}:{DATASITE_PORT}" result = requests.get(url) assert result.status_code == 200 @@ -23,7 +23,7 @@ def test_serves_datasite_frontend() -> None: @pytest.mark.frontend def test_serves_network_frontend() -> None: - title_str = "PyGrid" + title_str = "Syft UI" url = f"http://localhost:{NETWORK_PORT}" result = requests.get(url) assert result.status_code == 200 From 09ab586eebeb5cf479f108ef76a8628cc8f2a1b0 Mon Sep 17 00:00:00 2001 From: Madhava Jay Date: Fri, 12 Jul 2024 16:50:56 +1000 Subject: [PATCH 4/5] Renamed grid-* container images to syft-* --- .github/workflows/cd-syft-dev.yml | 42 +-- .github/workflows/cd-syft.yml | 128 ++++----- .isort.cfg | 6 +- .../api_reference/syft.types.server_url.rst | 29 ++ notebooks/api/0.8/00-load-data.ipynb | 9 + notebooks/api/0.8/10-container-images.ipynb | 6 +- .../api/0.8/11-container-images-k8s.ipynb | 2 +- .../deployments/02-deploy-container.ipynb | 2 +- packages/grid/backend/backend.dockerfile | 2 +- packages/grid/backend/grid/api/new/new.py | 2 +- packages/grid/backend/grid/api/router.py | 2 +- .../grid/backend/grid/core/celery_config.py | 13 - .../grid/backend/grid/core/celery_serde.py | 46 ---- packages/grid/backend/grid/core/server.py | 2 +- .../backend/grid/images/worker_cpu.dockerfile | 4 +- packages/grid/backend/grid/main.py | 2 +- packages/grid/default.env | 20 +- packages/grid/devspace.yaml | 12 +- packages/grid/frontend/frontend.dockerfile | 6 +- .../static/assets/small-grid-symbol-logo.png | Bin 41764 -> 0 bytes .../frontend/static/assets/small-logo.png | Bin 26289 -> 24772 bytes .../static/assets/small-syft-symbol-logo.png | Bin 0 -> 33965 bytes .../backend/backend-statefulset.yaml | 6 +- .../frontend/frontend-deployment.yaml | 2 +- .../rathole/rathole-statefulset.yaml | 2 +- .../seaweedfs/seaweedfs-statefulset.yaml | 2 +- packages/grid/quickstart/template.json | 260 ------------------ .../grid/scripts/backup_server_credentials.sh | 2 +- .../assets/img/small-grid-symbol-logo.png | Bin 41764 -> 0 bytes .../assets/img/small-syft-symbol-logo.png | Bin 0 -> 33965 bytes packages/syft/src/syft/client/client.py | 2 +- .../syft/src/syft/client/enclave_client.py | 4 +- .../syft/src/syft/client/gateway_client.py | 4 +- .../src/syft/protocol/protocol_version.json | 42 ++- packages/syft/src/syft/server/uvicorn.py | 2 +- .../syft/service/settings/settings_service.py | 2 +- .../syft/src/syft/service/worker/utils.py | 2 +- packages/syft/src/syft/util/schema.py | 2 +- packages/syft/tests/syft/assets_test.py | 2 +- .../tests/syft/custom_worker/config_test.py | 2 +- .../worker_pool/worker_pool_service_test.py | 2 +- .../tests/syft/worker_pool/worker_test.py | 2 +- packages/syftcli/manifest.yml | 4 +- scripts/build_images.sh | 6 +- scripts/flush_queue.sh | 2 - .../container_workload/pool_image_test.py | 2 +- tox.ini | 6 +- 47 files changed, 224 insertions(+), 473 deletions(-) create mode 100644 docs/source/api_reference/syft.types.server_url.rst delete mode 100644 packages/grid/backend/grid/core/celery_config.py delete mode 100644 packages/grid/backend/grid/core/celery_serde.py delete mode 100644 packages/grid/frontend/static/assets/small-grid-symbol-logo.png create mode 100644 packages/grid/frontend/static/assets/small-syft-symbol-logo.png delete mode 100644 packages/grid/quickstart/template.json delete mode 100644 packages/syft/src/syft/assets/img/small-grid-symbol-logo.png create mode 100644 packages/syft/src/syft/assets/img/small-syft-symbol-logo.png delete mode 100755 scripts/flush_queue.sh diff --git a/.github/workflows/cd-syft-dev.yml b/.github/workflows/cd-syft-dev.yml index b28510a2906..0b9ce3fe27d 100644 --- a/.github/workflows/cd-syft-dev.yml +++ b/.github/workflows/cd-syft-dev.yml @@ -77,11 +77,11 @@ jobs: username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} - - name: Set Grid package version - id: grid + - name: Set Server package version + id: server shell: bash run: | - echo "GRID_VERSION=$(python packages/grid/VERSION)" >> $GITHUB_OUTPUT + echo "SERVER_VERSION=$(python packages/grid/VERSION)" >> $GITHUB_OUTPUT - name: Build and push `syft` image to registry uses: docker/build-push-action@v6 @@ -92,9 +92,9 @@ jobs: tags: | ${{ secrets.ACR_SERVER }}/openmined/syft-client:dev ${{ secrets.ACR_SERVER }}/openmined/syft-client:dev-${{ github.sha }} - ${{ secrets.ACR_SERVER }}/openmined/syft-client:${{ steps.grid.outputs.GRID_VERSION }} + ${{ secrets.ACR_SERVER }}/openmined/syft-client:${{ steps.server.outputs.SERVER_VERSION }} - - name: Build and push `grid-backend` image to registry + - name: Build and push `syft-backend` image to registry uses: docker/build-push-action@v6 with: context: ./packages @@ -102,43 +102,43 @@ jobs: push: true target: backend tags: | - ${{ secrets.ACR_SERVER }}/openmined/grid-backend:dev - ${{ secrets.ACR_SERVER }}/openmined/grid-backend:dev-${{ github.sha }} - ${{ secrets.ACR_SERVER }}/openmined/grid-backend:${{ steps.grid.outputs.GRID_VERSION }} + ${{ secrets.ACR_SERVER }}/openmined/syft-backend:dev + ${{ secrets.ACR_SERVER }}/openmined/syft-backend:dev-${{ github.sha }} + ${{ secrets.ACR_SERVER }}/openmined/syft-backend:${{ steps.syft.outputs.SERVER_VERSION }} - - name: Build and push `grid-frontend` image to registry + - name: Build and push `syft-frontend` image to registry uses: docker/build-push-action@v6 with: context: ./packages/grid/frontend file: ./packages/grid/frontend/frontend.dockerfile push: true tags: | - ${{ secrets.ACR_SERVER }}/openmined/grid-frontend:dev - ${{ secrets.ACR_SERVER }}/openmined/grid-frontend:dev-${{ github.sha }} - ${{ secrets.ACR_SERVER }}/openmined/grid-frontend:${{ steps.grid.outputs.GRID_VERSION }} - target: grid-ui-development + ${{ secrets.ACR_SERVER }}/openmined/syft-frontend:dev + ${{ secrets.ACR_SERVER }}/openmined/syft-frontend:dev-${{ github.sha }} + ${{ secrets.ACR_SERVER }}/openmined/syft-frontend:${{ steps.syft.outputs.SERVER_VERSION }} + target: syft-ui-development - - name: Build and push `grid-seaweedfs` image to registry + - name: Build and push `syft-seaweedfs` image to registry uses: docker/build-push-action@v6 with: context: ./packages/grid/seaweedfs file: ./packages/grid/seaweedfs/seaweedfs.dockerfile push: true tags: | - ${{ secrets.ACR_SERVER }}/openmined/grid-seaweedfs:dev - ${{ secrets.ACR_SERVER }}/openmined/grid-seaweedfs:dev-${{ github.sha }} - ${{ secrets.ACR_SERVER }}/openmined/grid-seaweedfs:${{ steps.grid.outputs.GRID_VERSION }} + ${{ secrets.ACR_SERVER }}/openmined/syft-seaweedfs:dev + ${{ secrets.ACR_SERVER }}/openmined/syft-seaweedfs:dev-${{ github.sha }} + ${{ secrets.ACR_SERVER }}/openmined/syft-seaweedfs:${{ steps.syft.outputs.SERVER_VERSION }} - - name: Build and push `grid-enclave-attestation` image to registry + - name: Build and push `syft-enclave-attestation` image to registry uses: docker/build-push-action@v6 with: context: ./packages/grid/enclave/attestation file: ./packages/grid/enclave/attestation/attestation.dockerfile push: true tags: | - ${{ secrets.ACR_SERVER }}/openmined/grid-enclave-attestation:dev - ${{ secrets.ACR_SERVER }}/openmined/grid-enclave-attestation:dev-${{ github.sha }} - ${{ secrets.ACR_SERVER }}/openmined/grid-enclave-attestation:${{ steps.grid.outputs.GRID_VERSION }} + ${{ secrets.ACR_SERVER }}/openmined/syft-enclave-attestation:dev + ${{ secrets.ACR_SERVER }}/openmined/syft-enclave-attestation:dev-${{ github.sha }} + ${{ secrets.ACR_SERVER }}/openmined/syft-enclave-attestation:${{ steps.syft.outputs.SERVER_VERSION }} - name: Build Helm Chart & Copy to infra if: github.ref == 'refs/heads/dev' || github.event.inputs.deploy-helm == 'true' diff --git a/.github/workflows/cd-syft.yml b/.github/workflows/cd-syft.yml index d0610ac7956..f9b0cd12295 100644 --- a/.github/workflows/cd-syft.yml +++ b/.github/workflows/cd-syft.yml @@ -90,7 +90,7 @@ jobs: outputs: release_tag: ${{ steps.get_release_tag.outputs.release_tag }} - grid_version: ${{ steps.release_metadata.outputs.grid_version }} + server_version: ${{ steps.release_metadata.outputs.server_version }} steps: - uses: actions/checkout@v4 @@ -166,7 +166,7 @@ jobs: echo "release_platform=linux/arm64" >> $GITHUB_OUTPUT echo "short_release_platform=arm64" >> $GITHUB_OUTPUT fi - echo "grid_version=$(python packages/grid/VERSION)" >> $GITHUB_OUTPUT + echo "server_version=$(python packages/grid/VERSION)" >> $GITHUB_OUTPUT # TODO: Optimize redundant bump protocol version checks - name: Check and Bump Protocol Version @@ -185,79 +185,79 @@ jobs: username: ${{ secrets.DOCKER_LOGIN }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and push `grid-backend` image to DockerHub - id: grid-backend-build + - name: Build and push `syft-backend` image to DockerHub + id: syft-backend-build uses: docker/build-push-action@v6 with: context: ./packages file: ./packages/grid/backend/backend.dockerfile platforms: ${{ steps.release_metadata.outputs.release_platform }} target: backend - outputs: type=image,name=openmined/grid-backend,push-by-digest=true,name-canonical=true,push=true - cache-from: type=registry,ref=openmined/grid-backend:cache-${{ steps.release_metadata.outputs.short_release_platform }} - cache-to: type=registry,ref=openmined/grid-backend:cache-${{ steps.release_metadata.outputs.short_release_platform }},mode=max + outputs: type=image,name=openmined/syft-backend,push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=openmined/syft-backend:cache-${{ steps.release_metadata.outputs.short_release_platform }} + cache-to: type=registry,ref=openmined/syft-backend:cache-${{ steps.release_metadata.outputs.short_release_platform }},mode=max - - name: Export digest for grid-backend + - name: Export digest for syft-backend run: | - mkdir -p /tmp/digests/grid-backend - digest="${{ steps.grid-backend-build.outputs.digest }}" - touch "/tmp/digests/grid-backend/${digest#sha256:}" + mkdir -p /tmp/digests/syft-backend + digest="${{ steps.syft-backend-build.outputs.digest }}" + touch "/tmp/digests/syft-backend/${digest#sha256:}" - - name: Build and push `grid-frontend` image to DockerHub - id: grid-frontend-build + - name: Build and push `syft-frontend` image to DockerHub + id: syft-frontend-build uses: docker/build-push-action@v6 with: context: ./packages/grid/frontend file: ./packages/grid/frontend/frontend.dockerfile platforms: ${{ steps.release_metadata.outputs.release_platform }} - outputs: type=image,name=openmined/grid-frontend,push-by-digest=true,name-canonical=true,push=true - target: grid-ui-development - cache-from: type=registry,ref=openmined/grid-frontend:cache-${{ steps.release_metadata.outputs.short_release_platform }} - cache-to: type=registry,ref=openmined/grid-frontend:cache-${{ steps.release_metadata.outputs.short_release_platform}},mode=max + outputs: type=image,name=openmined/syft-frontend,push-by-digest=true,name-canonical=true,push=true + target: syft-ui-development + cache-from: type=registry,ref=openmined/syft-frontend:cache-${{ steps.release_metadata.outputs.short_release_platform }} + cache-to: type=registry,ref=openmined/syft-frontend:cache-${{ steps.release_metadata.outputs.short_release_platform}},mode=max - - name: Export digest for grid-frontend + - name: Export digest for syft-frontend run: | - mkdir -p /tmp/digests/grid-frontend - digest="${{ steps.grid-frontend-build.outputs.digest }}" - touch "/tmp/digests/grid-frontend/${digest#sha256:}" + mkdir -p /tmp/digests/syft-frontend + digest="${{ steps.syft-frontend-build.outputs.digest }}" + touch "/tmp/digests/syft-frontend/${digest#sha256:}" - - name: Build and push `grid-seaweedfs` image to DockerHub - id: grid-seaweedfs-build + - name: Build and push `syft-seaweedfs` image to DockerHub + id: syft-seaweedfs-build uses: docker/build-push-action@v6 with: context: ./packages/grid/seaweedfs file: ./packages/grid/seaweedfs/seaweedfs.dockerfile platforms: ${{ steps.release_metadata.outputs.release_platform }} - outputs: type=image,name=openmined/grid-seaweedfs,push-by-digest=true,name-canonical=true,push=true - cache-from: type=registry,ref=openmined/grid-seaweedfs:cache-${{ steps.release_metadata.outputs.short_release_platform }} - cache-to: type=registry,ref=openmined/grid-seaweedfs:cache-${{ steps.release_metadata.outputs.short_release_platform}},mode=max + outputs: type=image,name=openmined/syft-seaweedfs,push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=openmined/syft-seaweedfs:cache-${{ steps.release_metadata.outputs.short_release_platform }} + cache-to: type=registry,ref=openmined/syft-seaweedfs:cache-${{ steps.release_metadata.outputs.short_release_platform}},mode=max - - name: Export digest for grid-seaweedfs + - name: Export digest for syft-seaweedfs run: | - mkdir -p /tmp/digests/grid-seaweedfs - digest="${{ steps.grid-seaweedfs-build.outputs.digest }}" - touch "/tmp/digests/grid-seaweedfs/${digest#sha256:}" + mkdir -p /tmp/digests/syft-seaweedfs + digest="${{ steps.syft-seaweedfs-build.outputs.digest }}" + touch "/tmp/digests/syft-seaweedfs/${digest#sha256:}" - # Some of the dependencies of grid-enclave-attestation are not available for arm64 - # Hence, we are building grid-enclave-attestation only for x64 (see the `if` conditional) - - name: Build and push `grid-enclave-attestation` image to DockerHub + # Some of the dependencies of syft-enclave-attestation are not available for arm64 + # Hence, we are building syft-enclave-attestation only for x64 (see the `if` conditional) + - name: Build and push `syft-enclave-attestation` image to DockerHub if: ${{ endsWith(matrix.runner, '-x64') }} - id: grid-enclave-attestation-build + id: syft-enclave-attestation-build uses: docker/build-push-action@v6 with: context: ./packages/grid/enclave/attestation file: ./packages/grid/enclave/attestation/attestation.dockerfile platforms: ${{ steps.release_metadata.outputs.release_platform }} - outputs: type=image,name=openmined/grid-enclave-attestation,push-by-digest=true,name-canonical=true,push=true - cache-from: type=registry,ref=openmined/grid-enclave-attestation:cache-${{ steps.release_metadata.outputs.short_release_platform }} - cache-to: type=registry,ref=openmined/grid-enclave-attestation:cache-${{ steps.release_metadata.outputs.short_release_platform}},mode=max + outputs: type=image,name=openmined/syft-enclave-attestation,push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=openmined/syft-enclave-attestation:cache-${{ steps.release_metadata.outputs.short_release_platform }} + cache-to: type=registry,ref=openmined/syft-enclave-attestation:cache-${{ steps.release_metadata.outputs.short_release_platform}},mode=max - - name: Export digest for grid-enclave-attestation + - name: Export digest for syft-enclave-attestation if: ${{ endsWith(matrix.runner, '-x64') }} run: | - mkdir -p /tmp/digests/grid-enclave-attestation - digest="${{ steps.grid-enclave-attestation-build.outputs.digest }}" - touch "/tmp/digests/grid-enclave-attestation/${digest#sha256:}" + mkdir -p /tmp/digests/syft-enclave-attestation + digest="${{ steps.syft-enclave-attestation-build.outputs.digest }}" + touch "/tmp/digests/syft-enclave-attestation/${digest#sha256:}" - name: Build and push `syft` image to registry id: syft-build @@ -279,7 +279,7 @@ jobs: - name: Upload digests uses: actions/upload-artifact@v4 with: - name: digests-${{ steps.release_metadata.outputs.grid_version }}-${{ steps.release_metadata.outputs.short_release_platform }} + name: digests-${{ steps.release_metadata.outputs.server_version }}-${{ steps.release_metadata.outputs.short_release_platform }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 @@ -299,7 +299,7 @@ jobs: uses: actions/download-artifact@v4 with: path: /tmp/digests - pattern: digests-${{ needs.build-and-push-docker-images.outputs.grid_version }}-* + pattern: digests-${{ needs.build-and-push-docker-images.outputs.server_version }}-* merge-multiple: true - name: Set up Docker Buildx @@ -311,43 +311,43 @@ jobs: username: ${{ secrets.DOCKER_LOGIN }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Create manifest list and push for grid-backend - working-directory: /tmp/digests/grid-backend + - name: Create manifest list and push for syft-backend + working-directory: /tmp/digests/syft-backend run: | docker buildx imagetools create \ - -t openmined/grid-backend:${{ needs.build-and-push-docker-images.outputs.grid_version }} \ - -t openmined/grid-backend:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ - $(printf 'openmined/grid-backend@sha256:%s ' *) + -t openmined/syft-backend:${{ needs.build-and-push-docker-images.outputs.server_version }} \ + -t openmined/syft-backend:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ + $(printf 'openmined/syft-backend@sha256:%s ' *) - - name: Create manifest list and push for grid-frontend - working-directory: /tmp/digests/grid-frontend + - name: Create manifest list and push for syft-frontend + working-directory: /tmp/digests/syft-frontend run: | docker buildx imagetools create \ - -t openmined/grid-frontend:${{ needs.build-and-push-docker-images.outputs.grid_version }} \ - -t openmined/grid-frontend:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ - $(printf 'openmined/grid-frontend@sha256:%s ' *) + -t openmined/syft-frontend:${{ needs.build-and-push-docker-images.outputs.server_version }} \ + -t openmined/syft-frontend:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ + $(printf 'openmined/syft-frontend@sha256:%s ' *) - - name: Create manifest list and push for grid-seaweedfs - working-directory: /tmp/digests/grid-seaweedfs + - name: Create manifest list and push for syft-seaweedfs + working-directory: /tmp/digests/syft-seaweedfs run: | docker buildx imagetools create \ - -t openmined/grid-seaweedfs:${{ needs.build-and-push-docker-images.outputs.grid_version }} \ - -t openmined/grid-seaweedfs:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ - $(printf 'openmined/grid-seaweedfs@sha256:%s ' *) + -t openmined/syft-seaweedfs:${{ needs.build-and-push-docker-images.outputs.server_version }} \ + -t openmined/syft-seaweedfs:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ + $(printf 'openmined/syft-seaweedfs@sha256:%s ' *) - - name: Create manifest list and push for grid-enclave-attestation - working-directory: /tmp/digests/grid-enclave-attestation + - name: Create manifest list and push for syft-enclave-attestation + working-directory: /tmp/digests/syft-enclave-attestation run: | docker buildx imagetools create \ - -t openmined/grid-enclave-attestation:${{ needs.build-and-push-docker-images.outputs.grid_version }} \ - -t openmined/grid-enclave-attestation:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ - $(printf 'openmined/grid-enclave-attestation@sha256:%s ' *) + -t openmined/syft-enclave-attestation:${{ needs.build-and-push-docker-images.outputs.server_version }} \ + -t openmined/syft-enclave-attestation:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ + $(printf 'openmined/syft-enclave-attestation@sha256:%s ' *) - name: Create manifest list and push for syft working-directory: /tmp/digests/syft run: | docker buildx imagetools create \ - -t openmined/syft-client:${{ needs.build-and-push-docker-images.outputs.grid_version }} \ + -t openmined/syft-client:${{ needs.build-and-push-docker-images.outputs.server_version }} \ -t openmined/syft-client:${{ needs.build-and-push-docker-images.outputs.release_tag }} \ $(printf 'openmined/syft-client@sha256:%s ' *) diff --git a/.isort.cfg b/.isort.cfg index 5c9360d3549..aeb09bb8f36 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,18 +2,18 @@ profile=black force_single_line=True known_syft=syft -known_grid=grid +known_server=grid known_syftcli=syftcli known_first_party=src remove_redundant_aliases=True -sections=FUTURE,STDLIB,THIRDPARTY,SYFT,GRID,SYFTCLI,FIRSTPARTY,LOCALFOLDER +sections=FUTURE,STDLIB,THIRDPARTY,SYFT,SERVER,SYFTCLI,FIRSTPARTY,LOCALFOLDER lines_between_types=0 force_sort_within_sections=True import_heading_future=future import_heading_stdlib=stdlib import_heading_thirdparty=third party import_heading_syft=syft absolute -import_heading_grid=grid absolute +import_heading_server=server absolute import_heading_syftcli=syftcli absolute import_heading_firstparty=first party import_heading_localfolder=relative diff --git a/docs/source/api_reference/syft.types.server_url.rst b/docs/source/api_reference/syft.types.server_url.rst new file mode 100644 index 00000000000..c9ad2ab0625 --- /dev/null +++ b/docs/source/api_reference/syft.types.server_url.rst @@ -0,0 +1,29 @@ +syft.types.server\_url +==================== + +.. automodule:: syft.types.server_url + + + + + + + + + + + + .. rubric:: Classes + + .. autosummary:: + + ServerURL + + + + + + + + + diff --git a/notebooks/api/0.8/00-load-data.ipynb b/notebooks/api/0.8/00-load-data.ipynb index 57f38ceaafb..5370bd2e595 100644 --- a/notebooks/api/0.8/00-load-data.ipynb +++ b/notebooks/api/0.8/00-load-data.ipynb @@ -84,6 +84,15 @@ "datasite_client = server.login(email=\"info@openmined.org\", password=\"changethis\")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "datasite_client" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/notebooks/api/0.8/10-container-images.ipynb b/notebooks/api/0.8/10-container-images.ipynb index f3412e4def9..22e4706bf7f 100644 --- a/notebooks/api/0.8/10-container-images.ipynb +++ b/notebooks/api/0.8/10-container-images.ipynb @@ -169,7 +169,7 @@ "outputs": [], "source": [ "opendp_dockerfile_str = f\"\"\"\n", - "FROM openmined/grid-backend:{syft_base_worker_tag}\n", + "FROM openmined/syft-backend:{syft_base_worker_tag}\n", "\n", "RUN uv pip install opendp\n", "\n", @@ -1080,7 +1080,7 @@ "outputs": [], "source": [ "custom_dockerfile_str_2 = f\"\"\"\n", - "FROM openmined/grid-backend:{syft_base_worker_tag}\n", + "FROM openmined/syft-backend:{syft_base_worker_tag}\n", "\n", "RUN uv pip install opendp\n", "\"\"\".strip()\n", @@ -1296,7 +1296,7 @@ "outputs": [], "source": [ "custom_dockerfile_str_3 = f\"\"\"\n", - "FROM openmined/grid-backend:{syft_base_worker_tag}\n", + "FROM openmined/syft-backend:{syft_base_worker_tag}\n", "\n", "RUN uv pip install recordlinkage\n", "\"\"\".strip()\n", diff --git a/notebooks/api/0.8/11-container-images-k8s.ipynb b/notebooks/api/0.8/11-container-images-k8s.ipynb index a2ec5be3920..68620d9c114 100644 --- a/notebooks/api/0.8/11-container-images-k8s.ipynb +++ b/notebooks/api/0.8/11-container-images-k8s.ipynb @@ -250,7 +250,7 @@ "from syft.util.util import get_latest_tag\n", "\n", "registry = os.getenv(\"SYFT_BASE_IMAGE_REGISTRY\", \"docker.io\")\n", - "repo = \"openmined/grid-backend\"\n", + "repo = \"openmined/syft-backend\"\n", "\n", "if \"k3d\" in registry:\n", " tag = get_latest_tag(registry, repo)\n", diff --git a/notebooks/tutorials/deployments/02-deploy-container.ipynb b/notebooks/tutorials/deployments/02-deploy-container.ipynb index c677b1b6fc2..6c8b5bbfdb8 100644 --- a/notebooks/tutorials/deployments/02-deploy-container.ipynb +++ b/notebooks/tutorials/deployments/02-deploy-container.ipynb @@ -65,7 +65,7 @@ " -e CREATE_PRODUCER=true \\\n", " -e INMEMORY_WORKERS=true \\\n", " -p 8080:80 --add-host=host.docker.internal:host-gateway \\\n", - " --name syft-example-datasite-1 openmined/grid-backend:$SYFT_VERSION\n", + " --name syft-example-datasite-1 openmined/syft-backend:$SYFT_VERSION\n", "```" ] }, diff --git a/packages/grid/backend/backend.dockerfile b/packages/grid/backend/backend.dockerfile index 28af9aeb24c..256fdfd447b 100644 --- a/packages/grid/backend/backend.dockerfile +++ b/packages/grid/backend/backend.dockerfile @@ -60,7 +60,7 @@ WORKDIR /root/app/ # Copy pre-built syft dependencies COPY --from=syft_deps /root/app/.venv .venv -# copy grid +# copy server COPY grid/backend/grid ./grid/ # copy syft diff --git a/packages/grid/backend/grid/api/new/new.py b/packages/grid/backend/grid/api/new/new.py index 3bb7b35abf1..3ff61bb041b 100644 --- a/packages/grid/backend/grid/api/new/new.py +++ b/packages/grid/backend/grid/api/new/new.py @@ -1,7 +1,7 @@ # syft absolute from syft.server.routes import make_routes -# grid absolute +# server absolute from grid.core.server import worker router = make_routes(worker=worker) diff --git a/packages/grid/backend/grid/api/router.py b/packages/grid/backend/grid/api/router.py index 020491323a6..8412869db53 100644 --- a/packages/grid/backend/grid/api/router.py +++ b/packages/grid/backend/grid/api/router.py @@ -7,7 +7,7 @@ # third party from fastapi import APIRouter -# grid absolute +# server absolute from grid.api.new.new import router as new_router api_router = APIRouter() diff --git a/packages/grid/backend/grid/core/celery_config.py b/packages/grid/backend/grid/core/celery_config.py deleted file mode 100644 index 20fa3f55544..00000000000 --- a/packages/grid/backend/grid/core/celery_config.py +++ /dev/null @@ -1,13 +0,0 @@ -worker_send_task_event = False -task_ignore_result = True -task_time_limit = 1500 # Rasswanth: should modify after optimizing PC -task_acks_late = True -broker_pool_limit = 500 -worker_prefetch_multiplier = 1 -task_routes = { - "grid.worker.msg_without_reply": "main-queue", - "delivery_mode": "transient", -} -accept_content = ["application/syft"] -task_serializer = "syft" -result_serializer = "syft" diff --git a/packages/grid/backend/grid/core/celery_serde.py b/packages/grid/backend/grid/core/celery_serde.py deleted file mode 100644 index 005e4a4f514..00000000000 --- a/packages/grid/backend/grid/core/celery_serde.py +++ /dev/null @@ -1,46 +0,0 @@ -# stdlib -from typing import Any - -# third party -from kombu import serialization - -# syft absolute -import syft as sy -from syft.logger import error - - -def loads(data: bytes) -> Any: - # original payload might have nested bytes in the args - org_payload = sy.deserialize(data, from_bytes=True) - # original payload is found at org_payload[0][0] - if ( - len(org_payload) > 0 - and len(org_payload[0]) > 0 - and isinstance(org_payload[0][0], bytes) - ): - try: - nested_data = org_payload[0][0] - org_obj = sy.deserialize(nested_data, from_bytes=True) - org_payload[0][0] = org_obj - except Exception as e: - error(f"Unable to deserialize nested payload. {e}") - raise e - - return org_payload - - -def dumps(obj: Any) -> bytes: - # this is usually a Tuple of args where the first one is what we send to the task - # but it can also get other arbitrary data which we need to serde - # since we might get bytes directly from the web endpoint we can avoid double - # unserializing it by keeping it inside the nested args list org_payload[0][0] - return sy.serialize(obj, to_bytes=True) - - -serialization.register( - "syft", - dumps, - loads, - content_type="application/syft", - content_encoding="binary", -) diff --git a/packages/grid/backend/grid/core/server.py b/packages/grid/backend/grid/core/server.py index 1e692de29a7..9802eece8e8 100644 --- a/packages/grid/backend/grid/core/server.py +++ b/packages/grid/backend/grid/core/server.py @@ -20,7 +20,7 @@ from syft.store.sqlite_document_store import SQLiteStoreConfig from syft.types.uid import UID -# grid absolute +# server absolute from grid.core.config import settings diff --git a/packages/grid/backend/grid/images/worker_cpu.dockerfile b/packages/grid/backend/grid/images/worker_cpu.dockerfile index 94297fee0a9..3095c703e22 100644 --- a/packages/grid/backend/grid/images/worker_cpu.dockerfile +++ b/packages/grid/backend/grid/images/worker_cpu.dockerfile @@ -2,11 +2,11 @@ # Build as-is to create a base worker image # Build with args to create a custom worker image -# NOTE: This dockerfile will be built inside a grid-backend container in PROD +# NOTE: This dockerfile will be built inside a syft-backend container in PROD # Hence COPY will not work the same way in DEV vs. PROD ARG SYFT_VERSION_TAG="0.8.7-beta.13" -FROM openmined/grid-backend:${SYFT_VERSION_TAG} +FROM openmined/syft-backend:${SYFT_VERSION_TAG} # should match base image python version ARG PYTHON_VERSION="3.12" diff --git a/packages/grid/backend/grid/main.py b/packages/grid/backend/grid/main.py index 461e76de6b0..497a2dd7a90 100644 --- a/packages/grid/backend/grid/main.py +++ b/packages/grid/backend/grid/main.py @@ -11,7 +11,7 @@ # syft absolute from syft.protocol.data_protocol import stage_protocol_changes -# grid absolute +# server absolute from grid.api.router import api_router from grid.core.config import settings from grid.core.server import worker diff --git a/packages/grid/default.env b/packages/grid/default.env index 29f73b65900..e3edfdc38df 100644 --- a/packages/grid/default.env +++ b/packages/grid/default.env @@ -2,7 +2,7 @@ DATASITE=localhost SERVER_NAME=default_server_name SERVER_TYPE=datasite -FRONTEND_TARGET=grid-ui-development +FRONTEND_TARGET=syft-ui-development PORT=80 HTTP_PORT=80 HTTPS_PORT=443 @@ -15,26 +15,26 @@ IGNORE_TLS_ERRORS=False TRAEFIK_TLS_CONF=./traefik/dynamic-configurations TRAEFIK_TLS_CERTS=./traefik/certs TRAEFIK_PUBLIC_NETWORK=traefik-public -TRAEFIK_TAG=grid.openmined.org +TRAEFIK_TAG=syft.openmined.org TRAEFIK_PUBLIC_TAG=traefik-public -STACK_NAME=grid-openmined-org -DOCKER_IMAGE_BACKEND=openmined/grid-backend -DOCKER_IMAGE_FRONTEND=openmined/grid-frontend -DOCKER_IMAGE_RATHOLE=openmined/grid-rathole +STACK_NAME=syft-openmined-org +DOCKER_IMAGE_BACKEND=openmined/syft-backend +DOCKER_IMAGE_FRONTEND=openmined/syft-frontend +DOCKER_IMAGE_RATHOLE=openmined/syft-rathole DOCKER_IMAGE_TRAEFIK=traefik TRAEFIK_VERSION=v2.11.0 REDIS_VERSION=6.2 RABBITMQ_VERSION=3 -DOCKER_IMAGE_SEAWEEDFS=openmined/grid-seaweedfs +DOCKER_IMAGE_SEAWEEDFS=openmined/syft-seaweedfs VERSION=latest VERSION_HASH=unknown STACK_API_KEY="" # Backend -BACKEND_CORS_ORIGINS='["http://localhost","http://localhost:4200","http://localhost:3000","http://localhost:8080","https://localhost","https://localhost:4200","https://localhost:3000","https://localhost:8080","http://dev.grid.openmined.org","https://stag.grid.openmined.org","https://grid.openmined.org"]' +BACKEND_CORS_ORIGINS='["http://localhost","http://localhost:4200","http://localhost:3000","http://localhost:8080","https://localhost","https://localhost:4200","https://localhost:3000","https://localhost:8080","http://dev.syft.openmined.org","https://stag.syft.openmined.org","https://syft.openmined.org"]' SEAWEED_MOUNT_PORT=4001 -PROJECT_NAME=grid +PROJECT_NAME=syft SECRET_KEY=changethis DEFAULT_ROOT_EMAIL=info@openmined.org DEFAULT_ROOT_PASSWORD=changethis @@ -110,7 +110,7 @@ USE_BLOB_STORAGE=False ENABLE_SIGNUP=False # Enclave Attestation -DOCKER_IMAGE_ENCLAVE_ATTESTATION=openmined/grid-enclave-attestation +DOCKER_IMAGE_ENCLAVE_ATTESTATION=openmined/syft-enclave-attestation # Rathole Config RATHOLE_PORT=2333 \ No newline at end of file diff --git a/packages/grid/devspace.yaml b/packages/grid/devspace.yaml index 63623d712ff..0a00e7767f7 100644 --- a/packages/grid/devspace.yaml +++ b/packages/grid/devspace.yaml @@ -22,11 +22,11 @@ pipelines: create_deployments --all vars: - DOCKER_IMAGE_BACKEND: openmined/grid-backend - DOCKER_IMAGE_FRONTEND: openmined/grid-frontend - DOCKER_IMAGE_SEAWEEDFS: openmined/grid-seaweedfs - DOCKER_IMAGE_RATHOLE: openmined/grid-rathole - DOCKER_IMAGE_ENCLAVE_ATTESTATION: openmined/grid-enclave-attestation + DOCKER_IMAGE_BACKEND: openmined/syft-backend + DOCKER_IMAGE_FRONTEND: openmined/syft-frontend + DOCKER_IMAGE_SEAWEEDFS: openmined/syft-seaweedfs + DOCKER_IMAGE_RATHOLE: openmined/syft-rathole + DOCKER_IMAGE_ENCLAVE_ATTESTATION: openmined/syft-enclave-attestation CONTAINER_REGISTRY: "docker.io" VERSION: "0.8.7-beta.13" PLATFORM: $(uname -m | grep -q 'arm64' && echo "arm64" || echo "amd64") @@ -48,7 +48,7 @@ images: buildKit: args: ["--platform", "linux/${PLATFORM}"] dockerfile: ./frontend/frontend.dockerfile - target: "grid-ui-production" + target: "syft-ui-production" context: ./frontend tags: - dev-${DEVSPACE_TIMESTAMP} diff --git a/packages/grid/frontend/frontend.dockerfile b/packages/grid/frontend/frontend.dockerfile index 4dd570dd206..7dd46d4a888 100644 --- a/packages/grid/frontend/frontend.dockerfile +++ b/packages/grid/frontend/frontend.dockerfile @@ -22,14 +22,14 @@ FROM base AS dependencies RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile -FROM dependencies as grid-ui-tests +FROM dependencies as syft-ui-tests COPY vite.config.ts ./ COPY ./tests ./tests COPY ./src/ ./src CMD pnpm test:unit -FROM dependencies as grid-ui-development +FROM dependencies as syft-ui-development ENV SERVER_ENV=development @@ -41,7 +41,7 @@ FROM dependencies AS builder COPY . . RUN pnpm build -FROM base AS grid-ui-production +FROM base AS syft-ui-production ENV SERVER_ENV=production diff --git a/packages/grid/frontend/static/assets/small-grid-symbol-logo.png b/packages/grid/frontend/static/assets/small-grid-symbol-logo.png deleted file mode 100644 index b8956c2adec8f91eae3cc718be1626a5e39f2808..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41764 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 diff --git a/packages/grid/frontend/static/assets/small-logo.png b/packages/grid/frontend/static/assets/small-logo.png index 6732c49351eff345865006cee5a9bba48857c4ce..c0bb753561d038bf271fb01ac0b4c3642ef0d6a3 100644 GIT binary patch literal 24772 zcmZ^J1AHdWwqR`A&cwFuOl;e>Cbl`TZQIFYV%xUuWRw5BZ{OXwyZ!yX?&_}NI_K2s z>I##S5rc)of&u^lfRzvzRsaA1jQwioL4bXoZ9URAzAk`{3Sxo)RTDVJUpGa@Y7!UuHQ|9@6&%><;>lTt*rH(Y#j*X9Bc`gXjy34zWRZ40RNk34$yzm0F2E6{;xJb z-WMEzXy&Wz*9Fo}T-^}>fLiMN4_L_ayY!2Oow<^lliE*dPD5L3T74s117lh@YrAhO z0B$$Vuco!JlRkl)wUvz{ryCE^KM?eE8QL@z-OT@+la1rQ z)%sG9?z@MMftH@`|AqFY?EjYU|G;w7xBLGhIhmXMe|UZO{0r?LWBf}-?r#Y={~1tq zVRLI^8^FNIy^FMn21M^EUP9X>5ZwFGcwYB2? zfB6$P-T#F89|V4W4Y-lMlfJONlQAzNJtH#>JsS-JqY?uvClfm-Jqr~BJ?FPD-(>y| z*#DsOZ4yp31!G5BE9dWltJsD<&ghK%kXb@|M~3S@Z5CYKKUO$`mX@^=jm4j@IrkV=D(te z7wQ))sx$xqKY)a=fRY>Fxi+{L%D`jxa%VhLF7ZtIOTWK=$(xltHGu)Bj}HonJOTnV zkqb`}oWFl=sS|Z?Gzw}VgufFs<%}mZ>HW#YTQ_pNUF+J#`G)q(SjfK1VW;`#`dGUA z?Ex$)kuWS^lZm+X{HTt8B+1A_imKV`c1Ygu)**2M0t5tzLTAzBp( z>=2UXRB@HWq<#{YL;W9W290k7W3BLdE-a0_c4(2Z)h)P?3e( zo1eC_aFVETK!AR}{5g@t1R&y}=zqMu4!o3qX9d9=#|+%0Ae{XemhSZ!vI?8gzYDha zl>UKs_AhuPLX9^Pn^y3;4nY=yUv&J<6Tg-Q{z0@$AFDn50@edPx zkw(a9O-hnv1Q*n1fibFw-WPCSM%gb4>>@Qbmd+77D`r5U~JPgxv8c z@vR`(z^nT1>6a9Ay#OMZxvM1AUf7+^W7|m7YAB>%Lk$5UO{~X|ndkpQuT|5>>2qh1|o#*RgDw1JF95j`<^K<%ZAdKAATx`w0(3gCx z#)^W)=waqKt(t!gIHMnbp*HX~*U0e5*eZ&s!u5}E_goxa4~l!FzY8Nu-bowm4~LA` z4rdu3iGTx$`-HH*vr))ft* zgc&H%+Ud_TBU83;KsMNwa~=#Yv;H3Z8TqFmsZ-FvI4iVin7pW(5!rX(aDY&D01Rh3 zS9lYKQFI+s8a|3$!@4nyvInG&Nv2NQ8!7jJo!FJgkLeoy%laeCK&U#tPu^H-ptwAI zG*^9Q9gqOO!9GJU)+OIG^l22R@SGh}4>r*dqZqMZD=3FY&M zLF4<+YJB%TC68X}f1Hkz5NO{Utj{&kQ`Mv?4l@;!aB5_UE6^G(Uu?_LwOxAX)ktKg zxX*tC!@*zbAIbKJ<*Gm-9N9@W@0aQ1%HTu;V9F|v6d+>YjkuO2S9DvGXKcJG^ZSBu zpJ z=|O2{niZ+$6roaOY`s0R@qE+^Xa)gQ(KQ_nl#@xL5m+{e)D2TlqWZZh@eckthe}S0 zbz(Dwu@X>n;ahURUTLAL_P+Aqr9X{NJe>$}HeWO=rr;(IG*h2^arA*eaiZTZJn=Hj zsdE@wx9jA^qbwIKah+myn^7jc&5^w}DibSE8?`>rD9lQdyhl|w z&qG>}GbrF?oF7U#L?k5@2M%bGhlPl3fKZOIqyX`0CC-QFnE_n8;+e=F z_D7&)vtS^Bfkv^q@8;IMsLpGYN!3aFvtv1mmHUbPG$C99{U?h70_}_X*6dHtD?_}( z;M#gRHG$r%{E9Tjy?J(srmUNUo!&A0$h`U}c%n>J@zG~~49C8YZ9Nv25}!?2Vr znM*0FAly?!KEpNdA&w@#11iOmGeiRIj`CN(d>_FaM_B+az9Hs29*w^Zw1mt!ktFw z>u>ac^(KGV%6u@#H)Uq9l|VVy4yxx>qD3__len}-Q^wuqiuS<(0p)gpy1{JVD=PC~ zB?I)c#7Eu7<}ygwGsUOhxg<3gg}!_TNMQtjDjI=RD{a`mR!na*+#&FUjj*M^@c`CZ z*;hE}uK+JHec*BO=Dysfp*hJ)-FHRxJ_=egooe?KZ88#aW^S z)_*F8#b}@64NeGer7r-zCnc!G3oY%UHepvr63$fWOz*ewgNcFz{iw)E#q*nFL(0`590qgsvm2xm`n%uK*WC~SZWKd zm7GCl%pH!}kX$y2?50QG%y4UasJ%E+R^%^LNU`{Ud~UIynlmjZ`h``FrV0;zp4>~) z%z?In4g6L#G`$5Terp^e*-JVYw?sMO*FqVz5Sz330v8M=q<``1S;7sX{gtOmIv^5T zF(Gc_J^Rt|+~Snp%eK=mgU&&&mChSpr)npJWe}m^OKLvijGjl<B9!Q8@a4-#l93B`5(SWL)jEhX+14~fAaaxm>_WMv3aQehCR1zX<7 zS0>=^yedl8$>dRgS0HZ$az&gW{n1_av;%eM9=RI&0^-9u1D58C273iH8H8C`oL3$q z%6wqth?&W70zIytvZOlC?^v=jBlIh152H^@%%&?MXh~X;n37a`(=e2E2S%@{l$7dLww2>vD6WjSHAv;=z&|Wzu8|)H0jP9m!z}&Rll`=f=HwQtUI0WN*4c9 zhnBzTRzHSuxv;s&qsn5j!lzKqusw1!*TcpJ&iG-CBr%Z!=b$ zHIwXeoSTTObizft(J4@530ID;u`jaF&J;goX|GE%Pb%uwJxASg3F)2X9#z9+Lc zlt|{Ab;duecauJ}^^or{b%H+noNHVMhAtV<_76OwkIj3BJnS(OUUj#K_n4fr&`+<; zXE7qfj}=l!kmD_IUQ=B(jRf$gA`!If;?jehl-F+e$QrNIh8f-{ z)SJ&1aXa$LYy0F(R+z9_jCT3Wxlm?B z=p`-qGwchtXtp9)QljTQ{A^e$-?L$?$Su$`WHx2&vBX}|>8iZ%v8|C&EE7~L$0gGF zGB~+6$Vei<@~y?E&lI+{UeWT_AQ8*?)LG>)_E-TI!%Lhn1)&M5mt`Ys0$fKdrjtnI zz>8j=;pOkq%vH=$(K$IY7cymi?V1e`OL_%VCcYs|3=ClDWRe7*vITt`$cY_iVBAb& zz7LINm1xH1Ns;CifAX)=K^W8z0asXQgg$XtW^lK$HaT7M1!W}xfs)~+Bb6-1so+7h(i9O=) zat|IP(lu*fEo;4+X9M_9dyGL9hG)7UtrHQKyVslMG(4Gl1G^?<-yEXEvff~q*F`zX^H}=rBj0)z3q5kW5|c2Zxq}V% za*tuJ{54Y#%~AA6Q`N($W`%gsaE%zkcga=)17UX3D%EyfBN-G{M&ZmkcW=~!x!t?! z^s;x&mQ&TbDgHWTX<&$-2?DP=TgIJMH!6-r%ndU*Zo;|WG$v*3IT@jLbyrqMBE}A?ri;%i1PVTVsx7S$=23AvL*+R$W|E=@hEdMgPzvUa~_fGyOBI z)=P6_YflBZ{_Y9%=eIx@5J%C}Qe|OWV6b(g5?$-$lVf0Az_qo%Sr0pntY-D9OxHEU zLiFAZ3jAE2Yw&6abs8TdG1&=sI^owi_RMFP{SY^wRf-<|q!Pjdx@kjG&`Q=D7$#Yz z<6;p#6jMtGB`&4t(o%0bjwjnln!~Xyn20wC1GflV#O22at=qpE%UZGip-cJa1@dd^ zMIZpzajy$X_V57>7yW5IT^#S34wHV9B)=ysgL#GF^oBgG_s+yk{t7%w-yf=~VPS&q zUgJ?yty`9G?63i+KLxn26O4)WV0i^x6bX99Z%Ui4JscWV%YXH(cYTL_^8|F8cZ|Mv zOy9XVpjIlFBu4U_L$+uMFQEN6#B>EjYqY#OZfOW5p z-pvReyq#6Q6NL zzTh09^qZ2*ikAVjkT3iJd`Qv-WE1JnY)7prC?!KCh1#3XsPQN2bw3if}>q#C<4) z%iHbN0Z&&e7yGJ61W~7>)T;OKk69>}Zwp2eAvh`F zl~rMXUM{DR$Um#e4?CItI0@W=7ZEe{aVF2Ick);|Ou~mIOqZ2y8a;7?N>Iknu8AuiqbZ0_!`sV}xZY#>c+O8XC7==w20geSQ;S0@iQju+8 zBzcC=bnz@cjMX3R6GjrV3t16ESt0NSx#&hd<;q7c(>HU!N2`h0r$}~L9*HE{exn1Q znv9dsJ9|H`!s`lyesNL3j+44AEtm5eSnLs8wA6}V$SZkk*4xAZO@&}RkpRv5~VH#)LpmCtMmT%2dYRH|-QKl-pJE7W0RM}d{K z+4>F060dp~K{;rooK$RxGC%8zd46S$oRrh0kzDeF@0cUNE zzzn}D^}%W`M!GvjPRx@sY@qU}iO+;nUM$lUKm%{ycHPTQUU|n#&zt=4x4he1{oGEm zgl2`OF)=11Y0svjJWjqon+r{LWWIB3@Qc#H$CVVS{<^JL+ex8yG?eOc=kXC#U_Z~z zb$yO|#Fb(6QX$A~#VaS4<>Gi*I?2rk*Mcgwu2*ZBjy$6((N>dRVb8?1m}Na5n91|& zf6!0fA$R_n%3r?(<*8Uty~nT;P6kKDi(|FL_EHdVdt+1LP5Je)tIM#d;Jj-qFVgHUCYn8tT0SdhPzljmf(9K&lnAG9CK)7BLG{C zd(ICokIkZbI7DQA@`KAqYtc@lW$@a~FmI-jJikiEBTT}T8Si(UTEwB-h%d;|%b=k6 zog#OlQAQ0t@2T9c=Q4)h0y_mSixU`%`ja(JkFRVx%E}K=ftAw(j=VdcXmGB@YSd?{ z6$wXGkmmZ`X19BT*Y?sqUXxRJc$HpPLBA+UjgHQ(|CENLVLH!BIs@=a7||7cbHH(OUb?=z$Z-I&{zOgYeN}En#)|>r>Xn zqp$tjL`;mpGm|lK?aGoW)XU+pd2YuqWB!!i)dwgD6lCmt49rih+o{;zGD)|PbtKgx zKBJN*!5cFcCHeUu7_BF2tkZ@r_~*+_x^^)QT5cIC6#?3*GsGcHtGRV`!+2!O??)o< z{^)2*_3v3!do&N;ZnQ1u8g8S5$+AqaGc$y zmlGU?U8LXJq}G}aOPj&)pqf-yT)GvolFO_uD6#>>mm+1na{2ll`|?8PAwHLfH&cV# zL9i^|j!I|Wd)653r>|`cheKUwMAs{Ep@4i{DB^b_gaVq`i4HjH=4Ri+!@dZ zv1U-@aARSnwgS2xz)-a%d}OYdPz0LYTuJ}6Ha+wKgDIqltQuAr(9SZf7Gjbi%08rzkTad&+K?y<-OY%r*TLKDvpT||T{48JSeL}qNT4_fJpdQu*GT1Y& zB)2ck?+=0pu?r!XZo|NI?N4!<3m#Q2J8Ys(ckm$$DAU)_mn4@TsOb*RJkd#u82OF!a z&74zB?yGMl+k!3dJc+Z6@%J-r-cjH_J>2L4E~q1$qnN-V73z}uyt#yBrXyC+6yh!- zv`@0xWVrb33=?1G(*7D>7_1`a6`iBit&;UCt*RYxr>f^YFC4K(*w%-o4WEmfdAzh* zr7ITi2rZd{Xt3SvY8u_gDkUH3G&6$%0mhf!n#3yNQUjTX%3*MOZUwn}ZEzFzazd!l zQ)&ZL&+Jp?;ZzTvFgDb+@sO&9eSjpmcx}kfinZylWvRiV4!Hw*WPeJ<6B5a*)Oj(X zidfI`cD}h4L7QA{IzQy;n7)=iDAfX_hfu5sQf1Di_ca#8S)127N+7klsv$?=EAHRD z_$bsYCg~S;BgC`BmdNi<0B&#N;Jh;+b3R{D-stpnWkFFwC0@lSpSKVqGr4x-<~S-r zXS|eWww}L}jFusmpeG_Hwl29j;8|3=duKu*T(h%J*S7*wO!hmBaD{Cnk@B;}3ftY-HSa@ITKi*M;pt|6riNB(Ae2Es07hpH9?zoY( z0bGDhao(smoh)oQw4_JDg~orsfeYW_b%hCxj{ba!J%In?6LN|Sk>G(|``5MT=rnFBNh0jP>hr5rv!-CVZ`g{bg=;CU`{ITJ(aOB%h4r zqHD*AQf#yg`Kw)&kf{SPEpJQR?oPgfw|zOi_dcJa2KQ4n z*yU-B6GW2da|xO~Q6T?TycF$rz%-qG;~(QTUC6j!;tp0;R_GnwR6RqR zG|i&p-A}iTDdxc}_>Y(P*97B&_S&ys?+!i!V^=v~`$lA|BgumYN0(6C zxWNeSJC<}b3FWLInP-XX<^ppbp-=Vfk+UGtzKwx)F!2b;Uem&=6Q@x%dO03K#g_A7 zwF8eYp_nL!kC6!O3n>@!xIumJ*Z%H>!>P=}z8Rw^&EcLH#|9MtbDGluJj{Yqv}Og7&le)YW2rx(fK7Y|8jV`k1cMw>0Ohcu~(ck zG6GQ$wnC!Z!FxVlCK(Zd#!_JAbP`LLV|PaZ>viuZ=AQy z(*P_wiDAvOkqX_hHbS}7sQ!DuOY|1WYnynh_+*HOgfb)J?S}q04&|PpiY|u?wj(DE z<3|}S+ixM-adeevLro`7a&6#{B26X9gGh%yxJ?He)R{Kimxu7(bgvv#8)QnS#uZ)_cIC zxtD*DCzO+ib6u&g@n8(lh7hzuKNfx3gtz+_Ch@T;^mw{K?T2{4%UpRuY}*+hPGs)Z zPAL7{wW#-gf610C=SeDE^N+zu@Kt^SX5*6t(eU)(_3#c@)WVt`Qx3Py>e$TZ&o`c>Drhn^p6q8`=?ov^L6YlS5#>xQ|CpWY~UoyDQ*m5jH?|ctMLR?!s%QqWlW6!b44iK z+2PiXy$HQfhEC0vCVpCC;@v?M+lHCjPYJsI8|hn*PFT6m15Rspz!hQyy!7jG4#fot zI`M%(em?;7ZoNM9786~0>3tL4`i&47Ss0DL4cHOWVW_+ zHoSum>Z@r*fjM5tIE0lT1<@tG<)7NS7Lv+zAsqkIRCVrh=&{LozUDR&%O()g)Wf~W zP8IH!kcrNMXDX?zF!O=pv?i|K&o3D?f{1YSdB0axw9rmeMBaxHGJzIN_DLIG98BaCojD^C zv)rksSmYc!O-f?^To%&-!^m@CjYn_$d*bJHbcM1^tGsd_!T22kd+krd=&$LK` zzfAi{IA?3pcw3|o#6z>y?)=9^A zLZMu7sD|Q(sPb<6Prs{ztPkoYM2^PrGjLrB{m&DZkIVU&|OHk-{^$j@M}q8sj?N$w7x@Ei1;cqDpkT0jqKb2t0>$lufGZuz$?1C#oHm* zGIrGn z-{T=MIX9Q!;)qDh9*T zwExCUY9A=><_ZBt7Z<7M2b57C^`VMs{;q`IwP?0&r@@r|lm02-hhruP@#}h-Y5O#H zSDxHeY=xeAJH{;I;-^yP>oi3GT_HS)q1y2_lAsF=N@XRiqV!r z9QN1Lr6TOxkj?Y}^5U`{!?Px8;NrNR8mVUM620zo-DT#2)LCIx;0?2212>EDtoy5K zMCe{&(Shr6ou{*Z1voP~0I~FzyF_-NW2Ft`uc8 zDoOrcHf6G|SH~6y6x&gUEkxY-rP||_E)!I*O>~D|FA(S^Z}z%ff^KCj1ffGcn*1MV zkP#vvkuMi^9zn{C9KrhimLf9;HQ$080Jf|w(nfjiM6H!_QZBD|eVZ*;hBjt&_F-(W zabsO&DE>Jl=`~4{LO+U)4*@tQpNy{>Ev?<*W7nGL);>h9Di?TpV6DpLXi&P%FNu;xsw-KCNVL{`{n%>U_3%{3b} zFcN_PF_#)CnfY79>K=i`jrk`?>vEr}rrJZBfX!ivE*DcqLj!IdDREqG64g;u+m76F z_+Wri?Gh68mzt@1Hv(otG!_0cB?1(=8`o-+(Jn4}oy%1D(Tgo7EZbt{9Z<{?mqX0(;pD`%St&Z;A4#bk+h72W<+h%t#{7r=MK5(n|LSAI@~uk85$;ZN|h; z&tO48poTSn4T!hx?2X&^a&tKw*A5gTUg&0PNaIVK=sNCXT!m|Ffyv;CXf_zCcROkD z7#lD9w%Q?$f0?*7S=V$BS!qRqK9a(9M8NZN!>exbpTL-WI!2HP|4~-eo(C)H`emni z1P}2BzIE&hWg;8mUl`$nvlb!_jZS+O>h=5xl0PD-Z|LFia+k^Fil#7Y@A{CU?hI(E z_qB;`!QCvhlcnDIC`i1A1ou?^vNxVqF7u~+vZe25ev3bxOk5MDS#W;cx0 zsoN8%I#V$raBhdtf{w~EaBl){fxpqw2uyR$c5Xd*2ke`1^_$X>p)$K^xm@=0q8gux z9HkL;MyfiQb3cMY**Zgp_^M?Fh`HSm$?UFWG^qlWdM!wqk>Tq%NRR$h1#p(LZb;?A zp=SR48w`v60W}tr9S6AGyO?bOHWLfpzb#dXP~D!uMX%j58G%OAhH9>856YDLiLX`v z2Dk#H)e4ABg5Cdf7>64L$;;g+pKrcrX_lOcz)w4P2H`EJ%iZa%7QyI}@WY|9!%d?T z;04yh$p>o^+PWKf|At3ofVcmiuAStPh07_zY8i$dHVW*zIENzi276b22n9xv`ESj$ z)^oQP7gQ_Tr^IKnNw4~u#;>dqsUYQbI*rOf^3u*jB@J7eHlGe)uC?t!$|G@fA1W98 z>u5Gdl~A7zQoHK*2LK!mgKBa4UNsohfY_zZuN{ugU%sk->O+`?&5d&0s|Pm5Ww{Xs z2CW6L4m;Ur9e|jRIf%=b2kZ^x4u*`^LC!l$RWK5~)edGK2LYdD%bqKtM+9NEAr^#l z1XdJr)yioiM5=8PsLYzIrRb~0VtYr3z=kRFcj??L|$x-^scE~SUiCq8@}}koJo_t|0*iv$6ZS;xB6+JHtu|* zn|&9^XHKhu^RY926ldjt>hf+Y3tB=0F69mlS?}j^F?MncRUQ$AKkht=a7L0w${T+i zvoWy&X_>r{)1_*o28MP_$3dDUR8-FUQK9=Ny_+{E>d->zALT3LWu*{2*qMMK;ahA! z1f3vcP-jz*>5{;qd@I5n3Vt^h9&qHY{>}9dGv(*#2C$^?Kkwk3~|Q?DTT7y8ub2ZqPA7m@?e^h%i>vfLy*fQ;krsb|F)q3hFjh zKKC0EJ{wippPJ}p@z{b5bXE?h3zZ46PaK^va@~&s?A+8e>)nd^RlHG8JK-t>YKd$&(%|kOX=Vx~Iubi{YkrDPXZguR4fVk@Bph zTjMO3!|FbiTS4UQpOM7In4CXJplri_ ziH+DSE2#3dyyOKCvdzp#I|}YQhsG)Hdfv3G77QKpf00o2?WKvkfC2G8l@bbq`a!_2 zy44NGDfdV3M||B!yFOcE`#EY93-Ue43{?wrH>hMHLn(Ig_$8W%X(*>NtME=bi2nN> z-kee!5K`)OKo;JhEqcmYS2&y2OHwP#RT;Oup*!7Y4^&uX2PyCsY9aNWw)03Je19!Wi z4pc=JBk=96$y|zd^dGAWp2{}8=}(_0Q3asc1HOD*I>8k0%+HF)Q`H!vmZL%{+lE*m z;U6bpL2_gZ^iHA#X`7;Q-1PC<{u7_BYL^XgBeCd^$&~13)48%B)R7OY5dh);MT{rVdcg zZR>00@-BHC#qzy9tlDPtr==eC{O-;Ayqu}{Ok=TRluvG!3qi!X&pFxx%Sq5e`kAd` z)BH-5=$a~;`i3&yEq9?p^U`F!`t;aGH}30VJ`>yR56B-kUXupt1mRBEG^;^f0-jxH z1_qH_VKy&PAL}3r!2nFKm*&3fn%n>Rvs(LTto&#i*xc3oOkBwHK~T?Yi-{HDgN*)S zjp=92a;lteCb011M4RcQ$59INr`yC26BJ%oPfsk525Gw4UT|l%p(z#;F$#LUyP@t@ zpGdT*iBpHGu877s*p#@6Kfzr_`Q{M;@b}q%UNIK?A3A1bdoR zD-n|5_$Kedbqc}+1pwO%iWn^?v`U8Ts`FOZoIZCy(2kCaJ&x&xO500bYeak9b#&P)gmiPM?FXKM6Ox;U= z9=YIb+kjDbeyeYTTwLrxlDvT|b|d^Kw@(^U$D2AgMBqONEV+v%w+=6mTfQ1_46?0x>+iFz z?UWRmnFyjCrv#kNd{JIvJwxiB5r-1zY&2Z1m6_?xz$`xkU>J!6zGZG@Mb|22QHei7 zF~1-L_rR#9qI(OOe5Wg6KUZsoQnn(ooAe$p){6`^ z!lSPmidEzRn(5Ww$S$|iYRD7l9tm?eIFxM)PmNZ^;qeSm%xy_9btnPq(gppB9o zD)l|2TsXX~52am4a}Cb7k&T+~Lr22(?$9535LZ8g5FRWe6ej-S)RMR@MW?GLh3q(G#n(f@xj-(13NDF% z4=>JRAz)vkp~K~eAN*gZ*l1>vEFYo9RoZYmhy!VT5KTyUPUg!8xt^Qt?FI5UN*Lq~ zd6Bm)X%+R5JONHBRcSl#X56?988LKxk^eFm?A7B`_KbQ7Mwk&~EjFj3;e(vhrieJ| zQwfra_0#}tvv043O!r>-Ls4B6&|Ehvh4iCE;!$2~v_|jNNrMaUp*hXMijE8V1_DhJ zbSjtI;6>6ZzAf`y)n(JHx`iD4K$_J??%BZUDqphmRlkbRwMiYmKi((W>WILBNFZE- zSO8#OkCKe(H~woORh`LN(!9~v6C|>*=J!vF~ z4mylYMST{pcTGgg4YEVC;2IaNdAAqLOrdhG=jIDe_U3*Bg#dl~wPkOp!EZ3#QCmlT z|KKo&?}HsSqSBU7I;(#k%e071WRL?5oeejH!QbW(w=8q~`jHXGleHNyY3|zgK}#7v+j)mmzS^?pt1RKT2DiKy5-<9ayGa zuL?$$MT>w>=+VM}=Hi0tT4AFRe$wn1TJM7H2y@fQS9W-5a~(!+9fK5 z(JTs6J?c?B>{Nt^@g28JzwI33-s%nxi9!+OsO4XJu;n;Ce=Yt88Iei%Ie+F^Go7YMEB;t+wS)F=lan*LxY)L!u+nNk=ck z9TEaCh{^PKfPUGSjI{leZA-F(7FrV!(^qy)^As(6mT2V9jK2C8`=j3<#-yS#ZzCl!D?w)E zzvs82cH0a|?Q++2LdG*ZsI|a1wuTZLnq~7Zy~9SPtc}mz*<)GB_j?OsZGf4-)dBk! zYi%@Z>?NhvBS#tHVn&{88xY9xa}v&dqr#JOIX1VUC}MBZ)**CkyL&!hnS#k-m?DS8 zs~;z9`pyo@IvAOKYI1A=1$8T zc(tFU{i4Es^n4^t$0RR=p}kp`!pn1h@4N*An-CTgPALkKxM4W0ci(4ZWGrl%7bi^e zVjx{jih}s@0|K?-D+8+b{FK(}gMp!FC6z3Z16JX=HF?DGq`-hJr@P45U4oCOFwFra zpK9!5kcGLYz>mh~Wiie$)EJiog>K^os>0DkSJP2`JnWOSrexj#RUv4m;HDWG_-A`8mW*EJw5Sa}R0Z0X2!KC5b;o54^84tRy(rzEIn;RNW zVlm|-H)Mr@lsQQx6i-6%s{s*TJ&wk~0jZ(Ir=!KR>k|7g+_JT1hx{~H=+Af3st5l; zSfD7y2>vRi@}hWV6O@g$n5{8KdKsC5=Adzrt+JLar1!PcRHdW)Gc$t2GX%5mqNhb~ z(hXc|yyUqrpm5v%*rxrMS>r3PpE^ViljTflg$0Akg&06)whw->aXxJV?Y?KF3-GV) zJZc@eELLf&620FC5kd3e44fG%Y?EJ;AV4rw&V!2QQ8^t4g^kw~G1!V;He;hiFL>v% zk4yh1Nx9F0^%D7W@?h^bxUa04T~?Js@8z(0y{l+=tM(^G07R>5>9)tikf2?7&j)zd zpI2&zq61FR#(J%+;$ler?T49n5&@DWu~)#3<59`t5exbqS_)0dOHnSZqhN{b;;?ET z&~v(m*hb!1mH6mql|SeiZXtdjYFCxBPE8lo5f`ZIK$D}sAXm*baw zbsFL_^mbwq@iBM&rq1EFgDsPgu^o3;-Mtqz8eV)+_zdsC=Ce6zhyv(-iMj`@ArGZT z8X&~SvQBPGm%l7b4I4F|UcY94IHLgy@HzuWBhe=FLk(^e8_=Q2!K?1?Z@yyYPZg59 zdh+jdiC7lQF|J0mc0M{o>E%PrpKyd7A2HRgPlu76_?_2lb9@t@c=a*XOWdvVd@^g$ zAUB8*n!Y5H4lPse-3i%oVVkcV9Czw7O(m+0B^=>%m^6*VeWAVAX)=voIO&P z@Zx{8E6sw^?QJA_H*p^_Nd!weDhI}P<&#x!CRi2?ksBn#`HeCwrCT>x9%FiqL?8Ez z20OD>oI41$>GE%ks9#J3RZVVj=Lp}&-nQTSy_U?&KLaJH_mIa^k5H6Te7_;W8^z<*yg(jK1Z z@}i{*qGe^v9ROtb^hrcflC~#~$W;^r3cS)*t2u9JtOeX19itgUzY5l)`_1i)s}=Xr zW5w6GrsIVoTlWnL1PGCS`4U~`!_*2)H1F>04h>u)1%g}^Hv{()m!Zkg=Jc!{Rl>MeA*bKJ&YZnTMeN|ZWnJym0AP$`c|r}cK5sHiDK)>r@> zZ;gR85(f^$dN za>GmV4CastKwFt=K_EJHG8Mt*JgA}ne#ZX;YR+9E1N^*OHYB;ck9RH?I-Z~3$5Ec# z>qRDe0v&j9@8RgkPw*Oiigao3p}tM#UJ7<<6zpoB7f+I%8C>KoEoHxUM^#Z{8$NI` zAI_yu_Gj#AWIFRpzen#)xwt)Xcc!lO+>X2L)>`7ElIQ_^=X3LX`td z85oF}C-AHpuaIk)>toChT##gFUTnH)}Z9pZPcgH%B6^hh0BJn5# z;-*Fcu2rVoI+L!uVx6W-xfyYi$)s6`5xnLSB{#(K^U2qKK6}GqsgavRKno}1t)N6* zsWR)2r&HnapZXN0od!0s6z?-HP}>ibrqFNuML4QG)SS&}4Iz2ikh4;wz8? z`&r0kWqn)pY_Q|HDLOt;I$iSN9u0JdFt+kJUIp>8^^y0o;{OyijQaQN{U!9+2I4U= zE)XH%cL1y(KArk{A*Y^31>oG$Q0~)7U$xXS#m&+};?br-4XWt(Mqo159`m|sv+pN^ z$Ku;qN|P$_NCf4~mXB0pE(SNwLPgk3&X-9)vxf}8U2k*upNFr~Zv%~(jJQQA3RaRp zm6UD#Df(zaQsdOj;#0J%L^4_=XfAaGh--uu4mA(y5`uNAe0T?gpz-mZkq+0OMYUx^K)b*f=@ ztCxG^VZ$l#*KVZ$6*wKo;zxZRK5SL8(EiUP-vdEjG)!DP(T;V9po`~u+-|JwrWZ}A z9U~JkK~zPNObHN|)mvoKnwLrHnjhvM(zArcOEKh7=VbED7<4d{hoPA~lBuYX(Kw3i!>^T(d}C;qbYf z%F|S)9kIr-(p>(zbMlK+rhakg-&3Q?lMrz%Uv3%o?VQ)}=`P~C`p>nSep&56j;27J z{PT>a=)E63#~TDjobO2X*6%so8xa!K|?;f5VZ2bEczD8DR0j|~gH!{mW z1!|(>oWEz!o_Ec<64A^1^2;x8gqg<*bWBfyFVs5W7MS8L)635&4U6eVP^RX0p_LEP z@L5}BUOfLn3!A+7zSm!W{rMqyDh)X*0;Ekn;&MyU*Fa5)965@GJvu|r<;ma)gPnt4 ze0=HPm;2wyB9F$p9Yp?(1!Ci$hTosSEa-tq*FXF-#iDR3o^$-;Lfu1nyukf-h2pEIbiDs1z(7x% zQUg|O8}WYo@%IcI^i+Wr6{doAMXmAN6gMaCiq@1$pW`JoDkve`8!Q6aZ1NS|rw=hC z6jHmU%BNeMwr5${{a6xs8K%B14HlhlrP6~>zT9t-LH-hD8@1%4YnkHv425#DOaG7t zTiXA=bjQP*)=->z;;Nay;(fI9PZ;F>pQmnk#I$br(+!jPXsv?}EA-0`)uAxm_0JlD zgY}-*_3kK39G5V_bPVf5Pt{SX|x-fpikCL$dfz_Vo1hx|m^b z6&U~CIL^tqF2KcS8-)PDvt~fvFJrbeLX#m3!ljp98jt#JMbhR5lkbG^;O!_;l_LbL zUU|QvPOS~rKVmk_7;8cB{)~+HJJbt&V8jP*+;S_}?48rMAg^a(&2|*`6*7bx6&Y@H zH*8|+HT%U)E}IjM;OI+8xZiN%V2q44@VLDnrgnAMf{_E`y5XR!evW=Q&%XTloGjn* zi!8FGEw+oi1VOSCe-3&hs;XY5e*5?j|}e7+x1hd|+h zNf&W=HtgA{SHFor&*%Vkml4N&Mb@kkTZ@cZw-XQKlfYcNyZiGPAiuQNB8RrzIu0m2E?exiXE@V=A3=kst~ z5ai+mvhMh9hG}_02EE*esLenEk(kqCY&`VpGXdcJpoLuy1i+fu9Oq5l7C3^Mrv{cA z>={lL4u~)IM=p$Y3MtVUNPaz_qFRE!>-PNT2!25T8zDSGbsA?g zk+e4j>XmZJ6?1sQ@tW}LAB0Pi-Ut#~>QU$*ze30GUwo4*OSv*fFgqA{=FFL=ic@&x zwQ)LrgT18!2!O{boSgVuK}03L!P?c6fM8oChaqJ1!=4$0%-N5D`=n~TC*~m-bEaUi zJ3iE|bHAhox9AMt+1J?hY%~{TVa|IVBkJ=oiG2(O1*#ps&Ggl{&*2EZF|VLvQMz#~ zoEB%NaUbCC{Qq>a^x!ncD_*$0SI7_h-BmNEVDHD{I(Ze4uy^D7!8i8idSBxKE@K}0 z>}xF2Qa-~B3~BSa-c>6wvaCI0l=(D-$y=ybJgyS?TR=z6{Z(y(@VFmpwF4!A3D$qE z&s+cD`#*vO1~uFTTeoiA5sX;eabu{;)jP8d@Oa5FxJDku_bG(J70AeHI5UdFa;Wss-bOi}Msp?-SpK_XKe!m@ zX1t7BAL%<7-{>0)F!Fiz!_NQHGuh3n z3&rC3SRC*5tf9IwlKs?;kC;-|_5AGX%b~Vff&8lkD+BumpO}09-6_FnWjPKb&dwN# z^R*zpD?J7m|2D(@8{z6vzHY|{OCRIh@^Ls8_89IhLlOmjf!mH#I(rx_ufDU*eD4oI58c=K%=g0jN_;RKN=1Amd)|`+(y< z>A5@ZxMON@_E#kihF^htjrU`V{_Vs;6YYta*)W ziO;tg-Chj?`Et$pb_kFQ4e^4>!I-q`^MwoXhto+bjm2uYbk^N>{GmlJ>+#&aAEvon zZP5(Zh3F`n;Fr6Mse}7?{PJD>T)1#kO+G^A`v%T{b{M}0@cS=#cH({)ez#-YMb8Ql zY{eI#9^z`uDLN(aK~Xc*@rVvjv6A@i|NZ;-w+7~g1|;ZwPshi{{|S@nfVIkEtxl-S zDKM>pcU%|stA+swx75=LIhbv3smKR%m6`zYM|^g#(XV2T_cV0&Iu=s?DM%KhmYaIb zdUhi$l1glPIBw+hE4{?G^Tii)cVQQ*kkJPRb4d2Z&2B}<@-@KMpLLwg^dvkU-|}Lo zhqo9hnyA|IW7lc6rI>O;l<-HP=C?W*{ArLUw$%M!yLUPYpLx;hi(+yC4Oh%__WV8Y2 zh`z69_8xsrghiW5KIFx|8zK=NBfgkj!@j85yAYJ08WbLkO5{a%$32E;JvbAjCmLn= zy<+3%0iz)7y14a~5FSJI__2=jrXF3;N)CazxVWn!JnHF`cnm(eM(fZ8Ng$w$fZp^= zH(@l1xwD853Ez7$aCV>3vzd>zLxb&KN&LutK~zCD_o+rq|4w zF|gL+Gmj*l{L}Beugi?NIt+qTatJ_p{2xk-)hThwnl)=Ob?Aa55YR=SOb8JF8b18t z^XupLAKAg{mO2rIy#bo$^c%XX#){Ybqj&4RZO^|qCzDC?0m{@P5J*Qcaz7~hY=^7Y zAx8&y>lW3;Z~yulxsPevtRG{_>)@?pgg=kxAJs*M8Rh5aogX=wWoXy2+aoEbGHp(u zDi2mvIxkcieBEQ`f^UXHpe1AWq{seCH<+pFF$h}8Ake;j`$pI$|A1a;P+z6$(FIK) z5DI}ZCqQ_GADr^yY5dIpsPDM%+#Y2dj!*4^=W3i57;rX7(^`iz8ysnzWWk|GxeFpN z#m!ybR3tY!dV;X$Q|6?>m}WQnz60n4_aVPsQ1a|onePy#7N_L)xurOZ+nMclSB~DrhB!0QGo0Lr@yu-=Y=v zHfhr2d#rzG(M7yKAS41II)sqvON3!&uW?sOOc@Hna4!lBs^3qq6|Wv{oP;F+Q)ZWN z=R~s5F=pdH#cbrAh1|0sg|Z=nPJ(zqQrTf@AqR;{_p7RWDDGt4;|@n?b=l~J^KMqD z`VR7Jh06@iUXsE0P|V=KO`2SkX69JcQ<+vd$HQeT*$-FvJSUf*BL+~jQ%W=!fiF7-4 zd8B--_DHh)zo`%2vz|N{x-xyIO|#}m_P5c_kirA-9Ck5}7|w=i|f8-E{XQhI@esQaxC7%Eu(T>hxin#re)ZaOsCc2@fHXU z)RU}IEVSqJ2k+UZl}}*2yJp>Mu+zbi`VCOLyP#_~$afJ+8vzIq?#GG$4QPe0uGmya6JTmi@1B(Yu>~SL9eFe!~YdCvo5)Lh<=j}ZW*H%P1Q|5#S zO~+JVJ@#I#f_I!XOr}SCUiTS$Qv9jcyM^bp@z8-PJrqXjIK2{86xf~x1PFjxK5ynT zGx~JP3?OMfV%@{$rmhK?#Dx|fJQvC~{9P~JaQ>PF3qnK)2_kj3H83us1gtYaovjKWTC$D2eB0H;q$6Fiq%`M%7%LDHSl24E`mTH zas>QU9g*i&iaGF(bzZ-B>N2lqprW!NXqa8GNNnE)HGHmkn{kJYVYPv|v3|V~jvjx) z!pADb>)FR_a(Y6HlHusxoBK(MN_c$E7quZYu-?JCBE$1>X$nV7MFyXYQ710wW^P^L z(XYR*phEu&o*h1~G5Gi?9*E?4fiWCnejsSc#ans!~xPty&W6f@f)9JjsBKu}4 zanLJyf#rGz;{WdO$H7NceBZ>Ya~>JBR8`1JvQNFK{;t#311)N(?pR9^^^SjfgZEga zr-x9`5$q?~MO9@h>>)SA#dv;?rl9bz3%7)#v2`zF#Mf0NYG2Uwc7pSyCom%zGPTFO zbNur$a^Gv_@)(yV6RIm+3!cENjwi;Koug|5D_2>bAw2jXiO;};3XeP-J{Q8HvV0LG z)6NPbK>XlhJ!ibII<@Qg`?BNRH`rauc$#8rtRlr94NXoSW(RXB}v*FV*v3z9& zO0r!~fB9vSD@(Z_iiqE$i>an^e(mQRE zeDRF?I`1}-Mz9tG0ydS^87ki-I}oP3vx}i|&9V)x?a$sNg_z$aZG8-#We9od-}6GZTp1dJkH> z*nTF*_bG6?%S=PC>1I7P58jA%7E_D#X37w}#u2~{^fp)|?~35dp$+95ZP0aD2!wK^=4uk1Mfcu|lErH@oEibMS=dp7f{p-rjus!^_l! z*h7wf-P82=N`waKHDK_f;NehQ!Qz{q=ZyLNf9Gz6~kN(xSafIy(Q5GXMLGVa-F7bi*fn z7BW-+Kt1ecfx$$s%iR0#Txd3(;1>u81S$psB_=>PT%(>yS%(6z(u9uE?NE!X0DT=g zr;XXwYy9hQgtx&?vy4(C^qsaaj@Vt-5{W*59rWFydonlT&#OCBRz9qeOmdQ(SLgQZ z$@SY|g|m(l?WqtRox{lBq?@@LLw!}EYeZqxQzQ`x2n31=0n?-2Na{g}JV!S@V;7)f z#Mv3-LzS^jv1up%9y92@SH?V-VrF4iw}{dr_Mf}pIuh$%OJ!*h>hMUo!UJi&@bEaO z-tIq#L?S#m4l#1f{SY2^@O#sNk=a6cm_|(`5C~K#1WI%UGUn-&*33s%0KuxJP{dId zP;>pkRJH~?yw}=o()wp63@G!KKLci_)>UNbZqymRZz=bX8mYie$9lt1mXkI!(jDzR zK3jAZ{7%9=nZ=uW7iXD0@TYXutb6X*8b(@?L?9p#s2~WG=splD@$B^w8(%~>CqNjr zp%ZR{1^6~tG=9>96Ej8+O4$Z=*Z3Hs&VTZ(OOrD5O8AC6yXEv#KBm5oO2tBWM5AsQ z7RowJ?s5Mwk!IF$;$p0Kc!wdMaD)f9`ufLfr)6G%M~ah3FAxw2=pzu7o=v}q!c=tp zldx#~MB&(oo^p_SBLvHOsAJa9*w}SL$M!xNQI26I?mz#_Hj3hd`*Z1T$c#o<=ZF@* zf5}7|Fs0|ct57vGGqTY0vxU#8OdW|_G+m+Sl>iI6SuWM{s985DdU*<(KtLc+{t$>( z2WPnDaAISOK6KkZ;Rue&#O7SqXkQ~&w*}_>K^ZINEEnIuQA4pd zP<`rq5E$XDC+HSP_exeNdFub&vsahRAn~CE-(Ckt#as}D9b`(o&%VFQ$6;q65()$a z0u=&*5*HxY3qr9cMYLw2p2<c&P+_g6GRelMJ*-9C{CrIRo^|oi17~^mh$HfWGdADv zm<_FiHf?!M%^Dl@_MG&Y|9K->CB4WQ_uUF5txcD0Fah7=0oIA``ZZpjo*e!L!NB5n zx4Pms2#=Qn<4r#MFq@lgGL2vt2nYl!C<3J-K;q=s?M~*xLN{wxTDW2c)Bt5SfJzPP z7T5}&cH-#RL6hgAhx{1Z^!8R)K-Yz0=Tf{~T80 zVoC&oQWu*SpOcufUXt0mber$f<~y8oEMAp+IDMhmg$JQ->`;Fq+QP#DPmvHFDLC4; zUBu8a_FJ=R)lNm6kH|nEAP}e!2$YHdfyphi%481zEqf?ZJ}Ya9irtfRB&&PFt;g%1 z_wN~3{Arih;!zoelqrTfpTdme&gnhw`yiS#4_?ompMM2MjpVUtm=t3(=MqP9GFP#Q zAP^7;2n0x}sIWNh4UW`WwWn^{PK@kLd!Q}J_eQat9e2hZgNxvZT-6t2VeWVf&8ccv zbE;Y?M)nb^g;<(Q$-Ps0+#B^78Q;+8V;OHo2``T&!SYR%@)Fqy1Ox&V0D)4OfpFaN z_GWxho-o7_xspB0BUd?=Bh~%)w57?WG^y5}3?(kz(k0u>p7Qk#KrP~nSdIn*Lg;})(p%3q+W zN~@f3%KY-#FgvkXsI!XIol`Bw;)>5bgI&)|`WK?gJ*W1%=Xi0n+$P)Zf$y;HQOX-x z(HT+7OJpMu5C~Kd1WHwa*sPX6y>e1n05yz4!SuzRx?vs1J1OcE3^_?e6~Vx7Daqc>{Szs9rl;JD3(9w0f0Fb5=`n zyc3b9K%gQbP^tnXqw29Eai?m{!|?X9iUzLi@sXq>xQ;rBCq`nP7z@d&6HjsyZNIry zX;g6u500bcRQsUQ--}y*MI5lu^GBBK4*SHzh$|p65C{ka$|M5+4`3&oPKrzaga7~l M07*qoM6N<$g5k{71poj5 literal 26289 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 diff --git a/packages/grid/frontend/static/assets/small-syft-symbol-logo.png b/packages/grid/frontend/static/assets/small-syft-symbol-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..08086eac8e0d614aa0e098c9b317551584d780c3 GIT binary patch literal 33965 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

8cJLDH)k-ztTuExUk}G!t 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 #eEAVkE#75OY6MfXw)J;9y`PNty@DU*Jwq<_T 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 diff --git a/packages/syft/src/syft/assets/img/small-syft-symbol-logo.png b/packages/syft/src/syft/assets/img/small-syft-symbol-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..08086eac8e0d614aa0e098c9b317551584d780c3 GIT binary patch literal 33965 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

8cJLDH)k-ztTuExUk}G!t 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 #eEAVkE#75OY6MfXw)J;9y`PNty@DU*Jwq<_T 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 int: def get_client_type(self) -> type[SyftClient] | SyftError: # TODO: Rasswanth, should remove passing in credentials - # when metadata are proxy forwarded in the grid routes + # when metadata are proxy forwarded in the server routes # in the gateway fixes PR # relative from .datasite_client import DatasiteClient diff --git a/packages/syft/src/syft/client/enclave_client.py b/packages/syft/src/syft/client/enclave_client.py index 92ccd076804..fc0544e03ad 100644 --- a/packages/syft/src/syft/client/enclave_client.py +++ b/packages/syft/src/syft/client/enclave_client.py @@ -117,7 +117,7 @@ def _repr_html_(self) -> str: """ - small_grid_symbol_logo = load_png_base64("small-grid-symbol-logo.png") + small_server_symbol_logo = load_png_base64("small-syft-symbol-logo.png") url = getattr(self.connection, "url", None) server_details = f"URL: {url}
" if url else "" @@ -160,7 +160,7 @@ def _repr_html_(self) -> str: }}

- Logo

Welcome to {self.name}

diff --git a/packages/syft/src/syft/client/gateway_client.py b/packages/syft/src/syft/client/gateway_client.py index 003c7aa958b..c8e4929f99e 100644 --- a/packages/syft/src/syft/client/gateway_client.py +++ b/packages/syft/src/syft/client/gateway_client.py @@ -93,7 +93,7 @@ def _repr_html_(self) -> str: """ - small_grid_symbol_logo = load_png_base64("small-grid-symbol-logo.png") + small_server_symbol_logo = load_png_base64("small-syft-symbol-logo.png") url = getattr(self.connection, "url", None) server_details = f"URL: {url}
" if url else "" @@ -136,7 +136,7 @@ def _repr_html_(self) -> str: }}
- Logo

Welcome to {self.name}

diff --git a/packages/syft/src/syft/protocol/protocol_version.json b/packages/syft/src/syft/protocol/protocol_version.json index a79ae863d68..6602155d200 100644 --- a/packages/syft/src/syft/protocol/protocol_version.json +++ b/packages/syft/src/syft/protocol/protocol_version.json @@ -267,6 +267,11 @@ } }, "ServerSettings": { + "3": { + "version": 3, + "hash": "6b2240b92099684b0e431d27c0be2a22daad234b890c95eb607f0cc9e73c9f0d", + "action": "add" + }, "5": { "version": 5, "hash": "522690efe97c50dff81936640e95207861406e480d7e1d87587e558dbd7a506c", @@ -281,7 +286,7 @@ "HTTPConnection": { "3": { "version": 3, - "hash": "b58f9e16879e87169ccfac55504c8ae65dbc7a8345f6e361af4133e5572e2b13", + "hash": "2e567111a679e9f01b84eafc96d5e14e52b12fea991957e547f2edcbc0ede34c", "action": "add" } }, @@ -422,12 +427,12 @@ "BlobRetrievalByURL": { "4": { "version": 4, - "hash": "4ed706c90c5835959993bf9ebd61577829cdf8d2e51d4fd9606c68928f7be658", + "hash": "6a77259a8c83f85e83e89cee4c7ea7b1d57ccac4f70ecfb86b6c5db21c62ee79", "action": "add" }, "5": { "version": 5, - "hash": "2c6528e3efa7473a73951c4dec5e58547afdec8d0ba58807a0556733fd6217a5", + "hash": "e9c5e16f14e5bd45209f44ebf6c758a8c4a315b2d335a33bc529606506037890", "action": "add" } }, @@ -554,6 +559,11 @@ } }, "SyftLog": { + "3": { + "version": 3, + "hash": "c2ee3f4c34dc7af3af533865dc07384403fb757e7959272c9d6d08110623a216", + "action": "add" + }, "4": { "version": 4, "hash": "3b90dcb66359a1d03345291b47dcb8b5c166b1e0344dbe84d9c1f3a8e57f99eb", @@ -561,6 +571,11 @@ } }, "JobItem": { + "4": { + "version": 4, + "hash": "3e2eaae1b2c367b5bc52cbc4eb3390b1f5a6a6f8cae69523bbfc8e071c314bf8", + "action": "add" + }, "6": { "version": 6, "hash": "b633f770c9c47a849b75fe6b93c573c94eadab23c689aa812a8671cec9543650", @@ -568,6 +583,11 @@ } }, "ExecutionOutput": { + "1": { + "version": 1, + "hash": "c6cb71d6ae689e71c31685c29a7ecd44d1c0c5b31fd19991b535cb597db7b44a", + "action": "add" + }, "2": { "version": 2, "hash": "1b045334d8049f3970e655da11f3f62bc8a9f78e562fd75ed517c97fc95bb1c3", @@ -781,7 +801,7 @@ "SeaweedFSBlobDeposit": { "4": { "version": 4, - "hash": "84cdecd1a4ae0713a1e2992ba00ef74363996855c0b864b6fcd0e2fef2e3b44c", + "hash": "d5c268e0ccbb00149319679b95eb43134ae80ae43814d7f991732530c79399a1", "action": "add" } }, @@ -1156,6 +1176,20 @@ "hash": "eec308b8fd9437fc74a65f0cf70dff586130b35866d1e1512de49d45e7896981", "action": "add" } + }, + "StoreMetadata": { + "1": { + "version": 1, + "hash": "8de9a22a2765ef976bc161cb0704347d30350c085da8c8ffa876065cfca3e5fd", + "action": "add" + } + }, + "MigrationData": { + "1": { + "version": 1, + "hash": "ae07a6345762b8ebe9d2a100776e2405fd17516c9d224913a3358c96480ba889", + "action": "add" + } } } } diff --git a/packages/syft/src/syft/server/uvicorn.py b/packages/syft/src/syft/server/uvicorn.py index ad1a5eee238..3626db6aa13 100644 --- a/packages/syft/src/syft/server/uvicorn.py +++ b/packages/syft/src/syft/server/uvicorn.py @@ -162,7 +162,7 @@ def run_uvicorn( # Finally, run the uvicorn server. uvicorn.run( - "syft.server.server:app_factory", + "syft.server.uvicorn:app_factory", host=host, port=port, factory=True, diff --git a/packages/syft/src/syft/service/settings/settings_service.py b/packages/syft/src/syft/service/settings/settings_service.py index 5df9d9755e7..a59773a0280 100644 --- a/packages/syft/src/syft/service/settings/settings_service.py +++ b/packages/syft/src/syft/service/settings/settings_service.py @@ -351,7 +351,7 @@ def welcome_show( """ result = str_tmp.safe_substitute( FONT_CSS=FONT_CSS, - grid_symbol=load_png_base64("small-grid-symbol-logo.png"), + server_symbol=load_png_base64("small-syft-symbol-logo.png"), datasite_name=context.server.name, description=context.server.metadata.description, # server_url='http://testing:8080', diff --git a/packages/syft/src/syft/service/worker/utils.py b/packages/syft/src/syft/service/worker/utils.py index bc84a138b31..55d608c2964 100644 --- a/packages/syft/src/syft/service/worker/utils.py +++ b/packages/syft/src/syft/service/worker/utils.py @@ -567,7 +567,7 @@ def create_default_image( in_kubernetes: bool = False, ) -> SyftError | SyftWorkerImage: if not in_kubernetes: - tag = f"openmined/grid-backend:{tag}" + tag = f"openmined/syft-backend:{tag}" worker_config = PrebuiltWorkerConfig( tag=tag, diff --git a/packages/syft/src/syft/util/schema.py b/packages/syft/src/syft/util/schema.py index 56e443b1b7d..fd284fa6e87 100644 --- a/packages/syft/src/syft/util/schema.py +++ b/packages/syft/src/syft/util/schema.py @@ -57,7 +57,7 @@ }
- Logo

Welcome to $datasite_name

diff --git a/packages/syft/tests/syft/assets_test.py b/packages/syft/tests/syft/assets_test.py index 498094b9300..acc2297f1df 100644 --- a/packages/syft/tests/syft/assets_test.py +++ b/packages/syft/tests/syft/assets_test.py @@ -9,7 +9,7 @@ def test_load_assets(): - png = load_png_base64("logo.png") + png = load_png_base64("small-syft-symbol-logo.png") assert isinstance(png, str) with pytest.raises(FileNotFoundError): diff --git a/packages/syft/tests/syft/custom_worker/config_test.py b/packages/syft/tests/syft/custom_worker/config_test.py index 76a353e2d3b..3777be6e222 100644 --- a/packages/syft/tests/syft/custom_worker/config_test.py +++ b/packages/syft/tests/syft/custom_worker/config_test.py @@ -166,7 +166,7 @@ def test_load_custom_worker_config( DOCKER_METHODS = ["from_str", "from_path"] DOCKER_CONFIG_OPENDP = f""" - FROM openmined/grid-backend:{sy.__version__} + FROM openmined/syft-backend:{sy.__version__} RUN pip install opendp """ diff --git a/packages/syft/tests/syft/worker_pool/worker_pool_service_test.py b/packages/syft/tests/syft/worker_pool/worker_pool_service_test.py index 70219984d58..b5c5ea2f3a3 100644 --- a/packages/syft/tests/syft/worker_pool/worker_pool_service_test.py +++ b/packages/syft/tests/syft/worker_pool/worker_pool_service_test.py @@ -16,7 +16,7 @@ # relative from ..request.request_code_accept_deny_test import get_ds_client -PREBUILT_IMAGE_TAG = f"docker.io/openmined/grid-backend:{sy.__version__}" +PREBUILT_IMAGE_TAG = f"docker.io/openmined/syft-backend:{sy.__version__}" CUSTOM_DOCKERFILE = f""" FROM {PREBUILT_IMAGE_TAG} diff --git a/packages/syft/tests/syft/worker_pool/worker_test.py b/packages/syft/tests/syft/worker_pool/worker_test.py index e65a3334273..4da24cb315b 100644 --- a/packages/syft/tests/syft/worker_pool/worker_test.py +++ b/packages/syft/tests/syft/worker_pool/worker_test.py @@ -10,7 +10,7 @@ def get_docker_config(): # the DS makes a request to create an image and a pool based on the image custom_dockerfile = f""" - FROM openmined/grid-backend:{sy.__version__} + FROM openmined/syft-backend:{sy.__version__} RUN pip install recordlinkage """ return DockerWorkerConfig(dockerfile=custom_dockerfile) diff --git a/packages/syftcli/manifest.yml b/packages/syftcli/manifest.yml index 966706f9b11..809fb7cfb9d 100644 --- a/packages/syftcli/manifest.yml +++ b/packages/syftcli/manifest.yml @@ -4,8 +4,8 @@ syftVersion: 0.8.7-beta.13 dockerTag: 0.8.7-beta.13 images: - - docker.io/openmined/grid-frontend:0.8.7-beta.13 - - docker.io/openmined/grid-backend:0.8.7-beta.13 + - docker.io/openmined/syft-frontend:0.8.7-beta.13 + - docker.io/openmined/syft-backend:0.8.7-beta.13 - docker.io/library/mongo:7.0.4 - docker.io/traefik:v2.11.0 diff --git a/scripts/build_images.sh b/scripts/build_images.sh index 3426b5049de..bf7ad7e1823 100644 --- a/scripts/build_images.sh +++ b/scripts/build_images.sh @@ -3,6 +3,6 @@ REGISTRY=${1:-"k3d-registry.localhost:5800"} TAG=${2:-"latest"} -docker image build -f ./packages/grid/backend/backend.dockerfile --target backend -t $REGISTRY/openmined/grid-backend:$TAG ./packages -docker image build -f ./packages/grid/frontend/frontend.dockerfile --target grid-ui-development -t $REGISTRY/openmined/grid-frontend:$TAG ./packages/grid/frontend -docker image build -f ./packages/grid/seaweedfs/seaweedfs.dockerfile -t $REGISTRY/openmined/grid-seaweedfs:$TAG ./packages/grid/seaweedfs +docker image build -f ./packages/grid/backend/backend.dockerfile --target backend -t $REGISTRY/openmined/syft-backend:$TAG ./packages +docker image build -f ./packages/grid/frontend/frontend.dockerfile --target syft-ui-development -t $REGISTRY/openmined/syft-frontend:$TAG ./packages/grid/frontend +docker image build -f ./packages/grid/seaweedfs/seaweedfs.dockerfile -t $REGISTRY/openmined/syft-seaweedfs:$TAG ./packages/grid/seaweedfs diff --git a/scripts/flush_queue.sh b/scripts/flush_queue.sh deleted file mode 100755 index fbb2914bc33..00000000000 --- a/scripts/flush_queue.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -docker ps --format '{{.Names}}' | grep "celeryworker" | xargs -I '{}' docker exec -i {} python -c "from grid.core.celery_app import celery_app; celery_app.control.purge();print('Tasks Cleared')" diff --git a/tests/integration/container_workload/pool_image_test.py b/tests/integration/container_workload/pool_image_test.py index 6024a4b7b3a..10eab93ad31 100644 --- a/tests/integration/container_workload/pool_image_test.py +++ b/tests/integration/container_workload/pool_image_test.py @@ -21,7 +21,7 @@ from syft.util.util import get_latest_tag registry = os.getenv("SYFT_BASE_IMAGE_REGISTRY", "docker.io") -repo = "openmined/grid-backend" +repo = "openmined/syft-backend" if "k3d" in registry: tag = get_latest_tag(registry, repo) diff --git a/tox.ini b/tox.ini index ac6ba7f7b3f..9b67b7cfb06 100644 --- a/tox.ini +++ b/tox.ini @@ -178,7 +178,7 @@ commands = pnpm install; \ pnpm run test:unit; \ else \ - docker build --target grid-ui-tests -t ui-test -f frontend.dockerfile .; \ + docker build --target syft-ui-tests -t ui-test -f frontend.dockerfile .; \ docker run -t ui-test; \ fi' @@ -310,7 +310,7 @@ allowlist_externals = bash commands = bash -c 'tox -e single_container.destroy' - bash -c 'docker build -f grid/backend/backend.dockerfile . -t openmined/grid-backend:local-dev' + bash -c 'docker build -f grid/backend/backend.dockerfile . -t openmined/syft-backend:local-dev' bash -c 'docker run -d \ -e SERVER_NAME=${SERVER_NAME} \ -e SERVER_TYPE=${SERVER_TYPE} \ @@ -319,7 +319,7 @@ commands = -e CREATE_PRODUCER=true \ -e INMEMORY_WORKERS=true \ -p ${SERVER_PORT}:80 --add-host=host.docker.internal:host-gateway \ - --name ${SERVER_NAME} openmined/grid-backend:local-dev' + --name ${SERVER_NAME} openmined/syft-backend:local-dev' [testenv:single_container.destroy] description = Destroy the single backend container run using single_container.launch From 198cb53620cbfcc32dae7ac675976b091a45cf82 Mon Sep 17 00:00:00 2001 From: eelcovdw Date: Fri, 12 Jul 2024 09:47:30 +0200 Subject: [PATCH 5/5] disable migration tests --- .github/workflows/pr-tests-stack.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-tests-stack.yml b/.github/workflows/pr-tests-stack.yml index 26098bfa9fa..458092a16c5 100644 --- a/.github/workflows/pr-tests-stack.yml +++ b/.github/workflows/pr-tests-stack.yml @@ -417,6 +417,7 @@ jobs: k3d registry delete k3d-registry.localhost || true pr-tests-migrations: + if: false strategy: max-parallel: 99 matrix: