robovast-cluster 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. robovast_cluster-2.1.0/PKG-INFO +36 -0
  2. robovast_cluster-2.1.0/README.md +17 -0
  3. robovast_cluster-2.1.0/pyproject.toml +63 -0
  4. robovast_cluster-2.1.0/robovast/execution/cluster_config/__init__.py +0 -0
  5. robovast_cluster-2.1.0/robovast/execution/cluster_config/azure.py +227 -0
  6. robovast_cluster-2.1.0/robovast/execution/cluster_config/base_config.py +636 -0
  7. robovast_cluster-2.1.0/robovast/execution/cluster_config/gcp.py +765 -0
  8. robovast_cluster-2.1.0/robovast/execution/cluster_config/minikube.py +262 -0
  9. robovast_cluster-2.1.0/robovast/execution/cluster_config/minio_store.py +252 -0
  10. robovast_cluster-2.1.0/robovast/execution/cluster_config/rke2.py +314 -0
  11. robovast_cluster-2.1.0/robovast/execution/cluster_execution/__init__.py +0 -0
  12. robovast_cluster-2.1.0/robovast/execution/cluster_execution/bucket_ops.py +491 -0
  13. robovast_cluster-2.1.0/robovast/execution/cluster_execution/buildkitd_deploy.py +886 -0
  14. robovast_cluster-2.1.0/robovast/execution/cluster_execution/campaign_resume.py +350 -0
  15. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cli.py +1530 -0
  16. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cluster_capacity.py +334 -0
  17. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cluster_context.py +251 -0
  18. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cluster_execution.py +1371 -0
  19. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cluster_image_build.py +519 -0
  20. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cluster_service.py +5064 -0
  21. robovast_cluster-2.1.0/robovast/execution/cluster_execution/cluster_setup.py +870 -0
  22. robovast_cluster-2.1.0/robovast/execution/cluster_execution/container_runner.py +948 -0
  23. robovast_cluster-2.1.0/robovast/execution/cluster_execution/data_paths.py +204 -0
  24. robovast_cluster-2.1.0/robovast/execution/cluster_execution/data_purge.py +151 -0
  25. robovast_cluster-2.1.0/robovast/execution/cluster_execution/fetch_cache.py +54 -0
  26. robovast_cluster-2.1.0/robovast/execution/cluster_execution/helm.py +54 -0
  27. robovast_cluster-2.1.0/robovast/execution/cluster_execution/image_warm.py +449 -0
  28. robovast_cluster-2.1.0/robovast/execution/cluster_execution/in_pod_storage.py +1147 -0
  29. robovast_cluster-2.1.0/robovast/execution/cluster_execution/in_pod_upload.py +50 -0
  30. robovast_cluster-2.1.0/robovast/execution/cluster_execution/index_deploy.py +248 -0
  31. robovast_cluster-2.1.0/robovast/execution/cluster_execution/kube_client.py +756 -0
  32. robovast_cluster-2.1.0/robovast/execution/cluster_execution/kube_exec_lane.py +501 -0
  33. robovast_cluster-2.1.0/robovast/execution/cluster_execution/kubernetes.py +217 -0
  34. robovast_cluster-2.1.0/robovast/execution/cluster_execution/kubernetes_backend.py +4170 -0
  35. robovast_cluster-2.1.0/robovast/execution/cluster_execution/kubernetes_gpu.py +381 -0
  36. robovast_cluster-2.1.0/robovast/execution/cluster_execution/manifests.py +72 -0
  37. robovast_cluster-2.1.0/robovast/execution/cluster_execution/node_admission.py +971 -0
  38. robovast_cluster-2.1.0/robovast/execution/cluster_execution/node_calibration.py +1009 -0
  39. robovast_cluster-2.1.0/robovast/execution/cluster_execution/node_governor.py +402 -0
  40. robovast_cluster-2.1.0/robovast/execution/cluster_execution/node_placement.py +853 -0
  41. robovast_cluster-2.1.0/robovast/execution/cluster_execution/postprocess_host.py +291 -0
  42. robovast_cluster-2.1.0/robovast/execution/cluster_execution/postprocess_job.py +2284 -0
  43. robovast_cluster-2.1.0/robovast/execution/cluster_execution/postprocess_reattach.py +147 -0
  44. robovast_cluster-2.1.0/robovast/execution/cluster_execution/postprocess_stage.py +300 -0
  45. robovast_cluster-2.1.0/robovast/execution/cluster_execution/postprocess_usage.py +130 -0
  46. robovast_cluster-2.1.0/robovast/execution/cluster_execution/registry_client.py +834 -0
  47. robovast_cluster-2.1.0/robovast/execution/cluster_execution/registry_deploy.py +334 -0
  48. robovast_cluster-2.1.0/robovast/execution/cluster_execution/registry_image_store.py +328 -0
  49. robovast_cluster-2.1.0/robovast/execution/cluster_execution/serve_backend.py +64 -0
  50. robovast_cluster-2.1.0/robovast/execution/cluster_execution/service_deploy.py +2697 -0
  51. robovast_cluster-2.1.0/robovast/execution/cluster_execution/store_errors.py +78 -0
  52. robovast_cluster-2.1.0/robovast/execution/cluster_execution/store_pod.py +238 -0
  53. robovast_cluster-2.1.0/robovast/execution/cluster_execution/tailnet_deploy.py +356 -0
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: robovast-cluster
3
+ Version: 2.1.0
4
+ Summary: The Kubernetes execution lane for RoboVAST: cluster campaigns, deployment, and the operator commands.
5
+ License: Apache-2.0
6
+ Author: Frederik Pasch
7
+ Author-email: fred-labs@mailbox.org
8
+ Requires-Python: >=3.12,<3.14
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: bcrypt (>=4.0,<5.0)
13
+ Requires-Dist: boto3 (>=1.35,<2.0)
14
+ Requires-Dist: google-cloud-storage (>=2.0,<3.0)
15
+ Requires-Dist: kubernetes (>=34.1,<35.0)
16
+ Requires-Dist: robovast (>=2.0.0,<3.0.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # robovast-cluster
20
+
21
+ The Kubernetes execution lane for [RoboVAST](https://github.com/cps-test-lab/robovast):
22
+ running campaigns as Kubernetes Jobs, deploying the service, and the operator commands
23
+ that set a cluster up and take it down.
24
+
25
+ Install it alongside `robovast` when you have a cluster:
26
+
27
+ ```bash
28
+ pip install robovast robovast-cluster
29
+ ```
30
+
31
+ Without it, `robovast` runs the local Docker lane and says so — `vast serve --backend
32
+ cluster` reports which lanes are installed, and `vast doctor` reports the cluster lane as
33
+ absent rather than broken.
34
+
35
+ It ships into the `robovast` namespace, so nothing about the import paths changes.
36
+
@@ -0,0 +1,17 @@
1
+ # robovast-cluster
2
+
3
+ The Kubernetes execution lane for [RoboVAST](https://github.com/cps-test-lab/robovast):
4
+ running campaigns as Kubernetes Jobs, deploying the service, and the operator commands
5
+ that set a cluster up and take it down.
6
+
7
+ Install it alongside `robovast` when you have a cluster:
8
+
9
+ ```bash
10
+ pip install robovast robovast-cluster
11
+ ```
12
+
13
+ Without it, `robovast` runs the local Docker lane and says so — `vast serve --backend
14
+ cluster` reports which lanes are installed, and `vast doctor` reports the cluster lane as
15
+ absent rather than broken.
16
+
17
+ It ships into the `robovast` namespace, so nothing about the import paths changes.
@@ -0,0 +1,63 @@
1
+ [tool.poetry]
2
+ name = "robovast-cluster"
3
+ version = "2.1.0"
4
+ description = "The Kubernetes execution lane for RoboVAST: cluster campaigns, deployment, and the operator commands."
5
+ authors = ["Frederik Pasch <fred-labs@mailbox.org>"]
6
+ license = "Apache-2.0"
7
+ readme = "README.md"
8
+ # Ships into the `robovast` namespace rather than a name of its own, so the import paths
9
+ # do not change: `robovast/` and `robovast/execution/` carry no `__init__.py` in either
10
+ # distribution, which makes them PEP 420 namespace packages and lets the two trees merge.
11
+ packages = [{include = "robovast"}]
12
+
13
+ [tool.poetry.dependencies]
14
+ python = ">=3.12,<3.14"
15
+ # The lane is built on the core, and the edge is safe to declare: `robovast` names
16
+ # `robovast-nav` and `robovast-sim-roqsim` as path dependencies but never this package, so
17
+ # there is no cycle to avoid -- which is why `robovast_sim_roqsim` cannot state its
18
+ # equivalent and this can. It has to be stated rather than assumed: the controller image
19
+ # installs `robovast-cluster` into the pod, and every module here imports `robovast`.
20
+ robovast = "^2.0.0"
21
+ kubernetes = "^34.1"
22
+ # The built-in registry's password file. ``registry:2`` verifies with Go's bcrypt and
23
+ # rejects anything weaker, so this is the hash or no auth at all -- see registry_deploy.
24
+ bcrypt = "^4.0"
25
+ boto3 = "^1.35"
26
+ google-cloud-storage = "^2.0"
27
+
28
+ # `vast serve --backend cluster`. Resolved by name, so the core never imports this package
29
+ # to start a service -- see robovast/service/serve_backends.py.
30
+ [tool.poetry.plugins."robovast.execution_backends"]
31
+ cluster = "robovast.execution.cluster_execution.serve_backend:ClusterServeBackend"
32
+
33
+ # The operator half of `vast cluster` -- the verbs that need a kubeconfig, an API
34
+ # server or a cluster Secret. The `cluster` group itself belongs to robovast-client, whose
35
+ # service-driven verbs (run/stop/stop-job/log/download-cleanup) need none of this; these
36
+ # attach to it lazily, so `vast cluster --help` lists them without importing the
37
+ # Kubernetes client.
38
+ #
39
+ # `monitor` is here rather than in the client because its kubeconfig fallback view and its
40
+ # service view are one command chosen at runtime -- moving it would split one function's
41
+ # body across two distributions. A client-only user gets `vast campaign wait` and the
42
+ # web UI.
43
+ [tool.poetry.plugins."robovast.cluster_plugins"]
44
+ monitor = "robovast.execution.cluster_execution.cli:monitor"
45
+ setup = "robovast.execution.cluster_execution.cli:setup"
46
+ cleanup = "robovast.execution.cluster_execution.cli:cleanup"
47
+ "jobs-cleanup" = "robovast.execution.cluster_execution.cli:run_cleanup"
48
+
49
+ # `vast service upgrade` / `token` act on the deployed SERVICE, not on the cluster, so they
50
+ # attach to that group -- and need an API server, so they attach from here.
51
+ [tool.poetry.plugins."robovast.service_plugins"]
52
+ upgrade = "robovast.execution.cluster_execution.cli:upgrade"
53
+ token = "robovast.execution.cluster_execution.cli:cluster_token"
54
+
55
+ [tool.poetry.plugins."robovast.cluster_configs"]
56
+ minikube = "robovast.execution.cluster_config.minikube:MinikubeClusterConfig"
57
+ rke2 = "robovast.execution.cluster_config.rke2:Rke2ClusterConfig"
58
+ gcp = "robovast.execution.cluster_config.gcp:GcpClusterConfig"
59
+ azure = "robovast.execution.cluster_config.azure:AzureClusterConfig"
60
+
61
+ [build-system]
62
+ requires = ["poetry-core"]
63
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright (C) 2025 Frederik Pasch
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing,
11
+ # software distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions
14
+ # and limitations under the License.
15
+ #
16
+ # SPDX-License-Identifier: Apache-2.0
17
+ import io
18
+ import logging
19
+
20
+ import yaml
21
+ from kubernetes import client
22
+
23
+ from robovast.execution.cluster_execution.kube_client import load_kube_config
24
+
25
+ from ..cluster_execution import store_pod
26
+ from ..cluster_execution.kubernetes import apply_manifests, delete_manifests
27
+ from .base_config import BaseConfig
28
+
29
+ MINIO_MANIFEST_AZURE = """---
30
+ apiVersion: v1
31
+ kind: PersistentVolumeClaim
32
+ metadata:
33
+ name: robovast-pvc
34
+ spec:
35
+ accessModes:
36
+ - ReadWriteOnce
37
+ resources:
38
+ requests:
39
+ storage: {storage_size}
40
+ storageClassName: managed-csi
41
+ ---
42
+ apiVersion: v1
43
+ kind: Pod
44
+ metadata:
45
+ name: robovast
46
+ labels:
47
+ role: robovast
48
+ spec:
49
+ containers:
50
+ - name: minio
51
+ image: minio/minio:latest
52
+ args: ["server", "/data", "--console-address", ":9001"]
53
+ env:
54
+ - name: MINIO_ROOT_USER
55
+ value: "minioadmin"
56
+ - name: MINIO_ROOT_PASSWORD
57
+ value: "minioadmin"
58
+ ports:
59
+ - name: s3
60
+ containerPort: 9000
61
+ - name: console
62
+ containerPort: 9001
63
+ volumeMounts:
64
+ - mountPath: /data
65
+ name: minio-storage
66
+ resources:
67
+ # A floor, not an estimate: every milli-core requested here is capacity campaign jobs
68
+ # cannot be admitted against, so the request covers an idle server. The LIMIT is a
69
+ # different quantity and has to be sized against the peak: MinIO streams objects, but
70
+ # a batch mirrors dozens of multi-part transfers at once, and each carries buffers the
71
+ # request knows nothing about. Sized well above idle on purpose -- a store that is
72
+ # OOM-killed mid-batch takes that batch's bag conversion with it, and the campaign then
73
+ # fails in postprocessing with no conversion log to explain it. No CPU limit --
74
+ # throttling the store slows every upload and download with no visible cause.
75
+ requests:
76
+ cpu: "50m"
77
+ memory: "256Mi"
78
+ limits:
79
+ memory: "4Gi"
80
+ readinessProbe:
81
+ httpGet:
82
+ path: /minio/health/ready
83
+ port: 9000
84
+ initialDelaySeconds: 10
85
+ periodSeconds: 5
86
+ volumes:
87
+ - name: minio-storage
88
+ persistentVolumeClaim:
89
+ claimName: robovast-pvc
90
+ ---
91
+ apiVersion: v1
92
+ kind: Service
93
+ metadata:
94
+ name: robovast
95
+ spec:
96
+ ports:
97
+ - name: s3
98
+ port: 9000
99
+ targetPort: 9000
100
+ - name: console
101
+ port: 9001
102
+ targetPort: 9001
103
+ selector:
104
+ role: robovast
105
+ """
106
+
107
+
108
+ class AzureClusterConfig(BaseConfig):
109
+
110
+ #: AKS node pools are Azure VMs, whose guest kernels expose no cpufreq policy.
111
+ #: See :attr:`BaseConfig.governor_is_settable`.
112
+ governor_is_settable = False
113
+
114
+ def setup_cluster(self, storage_size="10Gi", **kwargs):
115
+ """Set up MinIO S3 server for Azure cluster.
116
+
117
+ Args:
118
+ storage_size (str): Size of the persistent volume (default: "10Gi")
119
+ **kwargs: Additional cluster-specific options (kube_context, namespace,
120
+ control_node_labels)
121
+ """
122
+ control_node_labels = kwargs.pop('control_node_labels', None)
123
+ logging.info("Setting up RoboVAST MinIO S3 server in Azure cluster...")
124
+ logging.info(f"Storage size: {storage_size}")
125
+
126
+ load_kube_config(context=kwargs.get('kube_context'))
127
+ k8s_client = client.ApiClient()
128
+
129
+ try:
130
+ yaml_objects = list(yaml.safe_load_all(io.StringIO(MINIO_MANIFEST_AZURE.format(storage_size=storage_size))))
131
+ except yaml.YAMLError as e:
132
+ raise RuntimeError(f"Failed to parse MinIO manifest YAML: {str(e)}") from e
133
+
134
+ namespace = kwargs.get('namespace', 'default')
135
+ # The registry and the campaign index ride in this pod: setup-lifetime
136
+ # infrastructure, created once here rather than in the service Deployment that
137
+ # every upgrade rolls. See `cluster_execution.store_pod`.
138
+ yaml_objects = store_pod.attach_infrastructure(
139
+ yaml_objects, namespace,
140
+ index_storage_path=kwargs.get('index_storage_path', ''),
141
+ index_storage_class=kwargs.get('index_storage_class', ''),
142
+ index_storage_size=kwargs.get('index_storage_size', ''),
143
+ registry_storage_path=kwargs.get('registry_storage_path', ''),
144
+ registry_storage_class=kwargs.get('registry_storage_class', ''),
145
+ registry_authenticated=kwargs.get('registry_authenticated', False),
146
+ ingress_class=kwargs.get('ingress_class', ''))
147
+ yaml_objects = self._apply_pod_node_selector(yaml_objects, control_node_labels)
148
+ try:
149
+ apply_manifests(k8s_client, iter(yaml_objects), namespace=namespace)
150
+ except Exception as e:
151
+ raise RuntimeError(f"Error applying MinIO manifest: {str(e)}") from e
152
+
153
+ logging.info(f"MinIO S3 server available at: {self.get_s3_endpoint()}")
154
+
155
+ def cleanup_cluster(self, storage_size="10Gi", **kwargs):
156
+ """Clean up MinIO S3 server for Azure cluster.
157
+
158
+ Args:
159
+ storage_size (str): Size of the persistent volume (default: "10Gi")
160
+ **kwargs: Additional cluster-specific options (ignored)
161
+ """
162
+ logging.debug("Cleaning up RoboVAST MinIO in Azure cluster...")
163
+ load_kube_config(context=kwargs.get('kube_context'))
164
+ core_v1 = client.CoreV1Api()
165
+
166
+ try:
167
+ yaml_objects = yaml.safe_load_all(io.StringIO(MINIO_MANIFEST_AZURE.format(storage_size=storage_size)))
168
+ except yaml.YAMLError as e:
169
+ raise RuntimeError(f"Failed to parse MinIO manifest YAML: {str(e)}") from e
170
+
171
+ namespace = kwargs.get('namespace', 'default')
172
+ yaml_objects = store_pod.attach_infrastructure(list(yaml_objects), namespace)
173
+ delete_manifests(core_v1, store_pod.infrastructure_claims(namespace) + yaml_objects,
174
+ namespace=namespace)
175
+ logging.debug("MinIO manifest deleted successfully!")
176
+
177
+ def prepare_setup_cluster(self, output_dir, storage_size="10Gi", **kwargs):
178
+ """Prepare any prerequisites before setting up the cluster.
179
+
180
+ Args:
181
+ output_dir (str): Directory where setup files will be written
182
+ storage_size (str): Size of the persistent volume (default: "10Gi")
183
+ **kwargs: Cluster-specific options (control_node_labels)
184
+ """
185
+ control_node_labels = kwargs.pop('control_node_labels', None)
186
+ docs = store_pod.attach_infrastructure(
187
+ list(yaml.safe_load_all(io.StringIO(
188
+ MINIO_MANIFEST_AZURE.format(storage_size=storage_size)))),
189
+ kwargs.get('namespace', 'default'))
190
+ docs = self._apply_pod_node_selector(docs, control_node_labels)
191
+ manifest_to_write = "---\n".join(
192
+ yaml.dump(d, default_flow_style=False) for d in docs if d is not None
193
+ )
194
+ with open(f"{output_dir}/robovast-manifest.yaml", "w") as f:
195
+ f.write(manifest_to_write)
196
+
197
+ readme_content = f"""# Azure Cluster Setup Instructions
198
+
199
+ Uses MinIO backed by an Azure managed-csi PVC ({storage_size}).
200
+
201
+ ## Setup Steps
202
+
203
+ ### 1. Apply the RoboVAST MinIO Manifest
204
+
205
+ ```bash
206
+ kubectl apply -f robovast-manifest.yaml
207
+ ```
208
+
209
+ ### 2. Wait for the pod to be ready
210
+
211
+ ```bash
212
+ kubectl wait --for=condition=ready pod/robovast --timeout=120s
213
+ ```
214
+
215
+ MinIO S3 API is available at `http://robovast:9000` (cluster-internal).
216
+ MinIO console is available at port 9001.
217
+ """
218
+ with open(f"{output_dir}/README_azure.md", "w") as f:
219
+ f.write(readme_content)
220
+
221
+ def get_instance_type_command(self):
222
+ """Get command to retrieve instance type of the current node."""
223
+ return (
224
+ 'INSTANCE_TYPE=$(curl -s -H "Metadata: true" '
225
+ '"http://169.254.169.254/metadata/instance/compute/vmSize'
226
+ '?api-version=2021-02-01&format=text")'
227
+ )