dory-sdk 2.1.0__py3-none-any.whl → 2.1.4__py3-none-any.whl
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.
- dory/__init__.py +32 -1
- dory/config/defaults.py +6 -0
- dory/config/schema.py +26 -0
- dory/edge/__init__.py +88 -0
- dory/edge/adaptive.py +648 -0
- dory/edge/detector.py +546 -0
- dory/edge/fencing.py +488 -0
- dory/edge/heartbeat.py +598 -0
- dory/edge/role.py +416 -0
- dory/health/server.py +283 -9
- dory/k8s/__init__.py +69 -0
- dory/k8s/labels.py +505 -0
- dory/migration/__init__.py +49 -0
- dory/migration/s3_store.py +656 -0
- dory/migration/state_manager.py +64 -6
- dory/migration/transfer.py +382 -0
- dory/migration/versioning.py +749 -0
- {dory_sdk-2.1.0.dist-info → dory_sdk-2.1.4.dist-info}/METADATA +37 -32
- {dory_sdk-2.1.0.dist-info → dory_sdk-2.1.4.dist-info}/RECORD +22 -15
- dory_sdk-2.1.4.dist-info/entry_points.txt +2 -0
- dory/sidecar/__init__.py +0 -6
- dory/sidecar/main.py +0 -75
- dory/sidecar/server.py +0 -329
- dory_sdk-2.1.0.dist-info/entry_points.txt +0 -3
- {dory_sdk-2.1.0.dist-info → dory_sdk-2.1.4.dist-info}/WHEEL +0 -0
- {dory_sdk-2.1.0.dist-info → dory_sdk-2.1.4.dist-info}/top_level.txt +0 -0
dory/k8s/labels.py
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
"""Kubernetes label contract between SDK and Orchestrator.
|
|
2
|
+
|
|
3
|
+
This module defines the label contract that enables coordination between
|
|
4
|
+
the Dory SDK and the Dory Orchestrator. Both components must use identical
|
|
5
|
+
label keys and values for proper operation.
|
|
6
|
+
|
|
7
|
+
Label Categories:
|
|
8
|
+
1. Identity Labels - Identify the processor and its managing controller
|
|
9
|
+
2. Workload Labels - Control scheduling and workload placement
|
|
10
|
+
3. Migration Labels - Track failover and migration state
|
|
11
|
+
|
|
12
|
+
Contract Version: 1.0
|
|
13
|
+
Orchestrator Compatibility: v1.0+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# Label Keys (must match orchestrator/pkg/utils/k8s.go)
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
# Identity Labels
|
|
26
|
+
LABEL_MANAGED_BY = "managed-by"
|
|
27
|
+
LABEL_APP_NAME = "app"
|
|
28
|
+
LABEL_PROCESSOR_ID = "processor-id"
|
|
29
|
+
|
|
30
|
+
# Workload Labels
|
|
31
|
+
LABEL_WORKLOAD_TYPE = "workload-type"
|
|
32
|
+
LABEL_WORKLOAD_LOCATION = "workload-location"
|
|
33
|
+
LABEL_NODE_TYPE = "node-type"
|
|
34
|
+
LABEL_INSTANCE_FAMILY = "instance-family"
|
|
35
|
+
|
|
36
|
+
# Migration Labels
|
|
37
|
+
LABEL_MIGRATED_FROM_EDGE = "migrated-from-edge"
|
|
38
|
+
LABEL_ORIGINAL_WORKLOAD_TYPE = "original-workload-type"
|
|
39
|
+
LABEL_ORIGINAL_NODE = "original-edge-node"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# Label Values (must match orchestrator/pkg/utils/k8s.go)
|
|
44
|
+
# =============================================================================
|
|
45
|
+
|
|
46
|
+
VALUE_ORCHESTRATOR_NAME = "dory-orchestrator"
|
|
47
|
+
VALUE_EDGE = "edge"
|
|
48
|
+
VALUE_MANAGED = "managed"
|
|
49
|
+
VALUE_APPLICATION = "application"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# =============================================================================
|
|
53
|
+
# Label Selectors (must match orchestrator/pkg/utils/k8s.go)
|
|
54
|
+
# =============================================================================
|
|
55
|
+
|
|
56
|
+
SELECTOR_EDGE_NODES = "node-type=edge"
|
|
57
|
+
SELECTOR_MANAGED_NODES = "workload-type=application"
|
|
58
|
+
SELECTOR_MANAGED_PODS = "managed-by=dory-orchestrator"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# =============================================================================
|
|
62
|
+
# Enums for Type Safety
|
|
63
|
+
# =============================================================================
|
|
64
|
+
|
|
65
|
+
class WorkloadLocation(Enum):
|
|
66
|
+
"""Workload location for pods.
|
|
67
|
+
|
|
68
|
+
Determines where the pod should run:
|
|
69
|
+
- EDGE: Runs on externally-managed edge nodes (node-type=edge)
|
|
70
|
+
- MANAGED: Runs on Karpenter-managed cloud nodes (workload-type=application)
|
|
71
|
+
"""
|
|
72
|
+
EDGE = VALUE_EDGE
|
|
73
|
+
MANAGED = VALUE_MANAGED
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class NodeType(Enum):
|
|
77
|
+
"""Node type classification.
|
|
78
|
+
|
|
79
|
+
Used for node selection:
|
|
80
|
+
- EDGE: Edge nodes with intermittent connectivity
|
|
81
|
+
- SYSTEM: System nodes (control plane, etc.)
|
|
82
|
+
- APPLICATION: Karpenter-managed application nodes
|
|
83
|
+
"""
|
|
84
|
+
EDGE = "edge"
|
|
85
|
+
SYSTEM = "system"
|
|
86
|
+
APPLICATION = VALUE_APPLICATION
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# =============================================================================
|
|
90
|
+
# Label Builder
|
|
91
|
+
# =============================================================================
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class DoryLabels:
|
|
95
|
+
"""Builder for Dory-compatible Kubernetes labels.
|
|
96
|
+
|
|
97
|
+
Creates labels that are compatible with the Dory Orchestrator's
|
|
98
|
+
label contract. Use this to ensure your pods are properly managed.
|
|
99
|
+
|
|
100
|
+
Usage:
|
|
101
|
+
# Create labels for an edge processor
|
|
102
|
+
labels = DoryLabels(
|
|
103
|
+
app_name="my-processor",
|
|
104
|
+
processor_id="proc-123",
|
|
105
|
+
workload_location=WorkloadLocation.EDGE,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Get as dictionary for Kubernetes manifest
|
|
109
|
+
pod_labels = labels.to_dict()
|
|
110
|
+
|
|
111
|
+
# Create labels for a migrated pod
|
|
112
|
+
migrated_labels = DoryLabels(
|
|
113
|
+
app_name="my-processor",
|
|
114
|
+
processor_id="proc-123",
|
|
115
|
+
workload_location=WorkloadLocation.EDGE,
|
|
116
|
+
migrated_from_edge=True,
|
|
117
|
+
original_node="edge-node-1",
|
|
118
|
+
)
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
app_name: str
|
|
122
|
+
processor_id: str | None = None
|
|
123
|
+
workload_location: WorkloadLocation = WorkloadLocation.MANAGED
|
|
124
|
+
migrated_from_edge: bool = False
|
|
125
|
+
original_workload_type: str | None = None
|
|
126
|
+
original_node: str | None = None
|
|
127
|
+
custom_labels: dict[str, str] | None = None
|
|
128
|
+
|
|
129
|
+
def to_dict(self) -> dict[str, str]:
|
|
130
|
+
"""Convert to Kubernetes labels dictionary.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dictionary of label key-value pairs
|
|
134
|
+
"""
|
|
135
|
+
labels = {
|
|
136
|
+
LABEL_MANAGED_BY: VALUE_ORCHESTRATOR_NAME,
|
|
137
|
+
LABEL_APP_NAME: self.app_name,
|
|
138
|
+
LABEL_WORKLOAD_LOCATION: self.workload_location.value,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if self.processor_id:
|
|
142
|
+
labels[LABEL_PROCESSOR_ID] = self.processor_id
|
|
143
|
+
|
|
144
|
+
if self.migrated_from_edge:
|
|
145
|
+
labels[LABEL_MIGRATED_FROM_EDGE] = "true"
|
|
146
|
+
|
|
147
|
+
if self.original_workload_type:
|
|
148
|
+
labels[LABEL_ORIGINAL_WORKLOAD_TYPE] = self.original_workload_type
|
|
149
|
+
|
|
150
|
+
if self.original_node:
|
|
151
|
+
labels[LABEL_ORIGINAL_NODE] = self.original_node
|
|
152
|
+
|
|
153
|
+
if self.custom_labels:
|
|
154
|
+
labels.update(self.custom_labels)
|
|
155
|
+
|
|
156
|
+
return labels
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def from_dict(cls, labels: dict[str, str]) -> "DoryLabels":
|
|
160
|
+
"""Create from existing labels dictionary.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
labels: Kubernetes labels dictionary
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
DoryLabels instance
|
|
167
|
+
"""
|
|
168
|
+
workload_location = WorkloadLocation.MANAGED
|
|
169
|
+
if labels.get(LABEL_WORKLOAD_LOCATION) == VALUE_EDGE:
|
|
170
|
+
workload_location = WorkloadLocation.EDGE
|
|
171
|
+
|
|
172
|
+
return cls(
|
|
173
|
+
app_name=labels.get(LABEL_APP_NAME, ""),
|
|
174
|
+
processor_id=labels.get(LABEL_PROCESSOR_ID),
|
|
175
|
+
workload_location=workload_location,
|
|
176
|
+
migrated_from_edge=labels.get(LABEL_MIGRATED_FROM_EDGE) == "true",
|
|
177
|
+
original_workload_type=labels.get(LABEL_ORIGINAL_WORKLOAD_TYPE),
|
|
178
|
+
original_node=labels.get(LABEL_ORIGINAL_NODE),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def is_edge_workload(self) -> bool:
|
|
182
|
+
"""Check if this is an edge workload."""
|
|
183
|
+
return self.workload_location == WorkloadLocation.EDGE
|
|
184
|
+
|
|
185
|
+
def is_migrated(self) -> bool:
|
|
186
|
+
"""Check if this pod was migrated from edge."""
|
|
187
|
+
return self.migrated_from_edge
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# =============================================================================
|
|
191
|
+
# Label Reader Utilities
|
|
192
|
+
# =============================================================================
|
|
193
|
+
|
|
194
|
+
def get_label(labels: dict[str, str] | None, key: str, default: str = "") -> str:
|
|
195
|
+
"""Safely get a label value.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
labels: Labels dictionary (may be None)
|
|
199
|
+
key: Label key
|
|
200
|
+
default: Default value if not found
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Label value or default
|
|
204
|
+
"""
|
|
205
|
+
if labels is None:
|
|
206
|
+
return default
|
|
207
|
+
return labels.get(key, default)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def get_app_name(labels: dict[str, str] | None) -> str:
|
|
211
|
+
"""Get app name from labels."""
|
|
212
|
+
return get_label(labels, LABEL_APP_NAME)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def get_processor_id(labels: dict[str, str] | None) -> str:
|
|
216
|
+
"""Get processor ID from labels."""
|
|
217
|
+
return get_label(labels, LABEL_PROCESSOR_ID)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def get_workload_location(labels: dict[str, str] | None) -> WorkloadLocation | None:
|
|
221
|
+
"""Get workload location from labels.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
WorkloadLocation enum or None if not set
|
|
225
|
+
"""
|
|
226
|
+
value = get_label(labels, LABEL_WORKLOAD_LOCATION)
|
|
227
|
+
if value == VALUE_EDGE:
|
|
228
|
+
return WorkloadLocation.EDGE
|
|
229
|
+
elif value == VALUE_MANAGED:
|
|
230
|
+
return WorkloadLocation.MANAGED
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def is_managed_by_dory(labels: dict[str, str] | None) -> bool:
|
|
235
|
+
"""Check if pod is managed by Dory Orchestrator."""
|
|
236
|
+
return get_label(labels, LABEL_MANAGED_BY) == VALUE_ORCHESTRATOR_NAME
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def is_edge_workload(labels: dict[str, str] | None) -> bool:
|
|
240
|
+
"""Check if pod is an edge workload."""
|
|
241
|
+
return get_label(labels, LABEL_WORKLOAD_LOCATION) == VALUE_EDGE
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def is_migrated_from_edge(labels: dict[str, str] | None) -> bool:
|
|
245
|
+
"""Check if pod was migrated from edge."""
|
|
246
|
+
return get_label(labels, LABEL_MIGRATED_FROM_EDGE) == "true"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def get_original_node(labels: dict[str, str] | None) -> str:
|
|
250
|
+
"""Get original edge node for migrated pod."""
|
|
251
|
+
return get_label(labels, LABEL_ORIGINAL_NODE)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# =============================================================================
|
|
255
|
+
# Environment-Based Label Detection
|
|
256
|
+
# =============================================================================
|
|
257
|
+
|
|
258
|
+
def get_pod_labels_from_env() -> dict[str, str]:
|
|
259
|
+
"""Get pod labels from environment variables.
|
|
260
|
+
|
|
261
|
+
The Kubernetes Downward API can expose labels as environment variables.
|
|
262
|
+
This function reads common label values from the environment.
|
|
263
|
+
|
|
264
|
+
Expected environment variables:
|
|
265
|
+
- POD_NAME: Pod name
|
|
266
|
+
- POD_NAMESPACE: Pod namespace
|
|
267
|
+
- POD_IP: Pod IP address
|
|
268
|
+
- NODE_NAME: Node name
|
|
269
|
+
- DORY_APP_NAME: App name (from label)
|
|
270
|
+
- DORY_PROCESSOR_ID: Processor ID (from label)
|
|
271
|
+
- DORY_WORKLOAD_LOCATION: Workload location (from label)
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Dictionary of detected labels
|
|
275
|
+
"""
|
|
276
|
+
labels = {}
|
|
277
|
+
|
|
278
|
+
if app_name := os.environ.get("DORY_APP_NAME"):
|
|
279
|
+
labels[LABEL_APP_NAME] = app_name
|
|
280
|
+
|
|
281
|
+
if processor_id := os.environ.get("DORY_PROCESSOR_ID"):
|
|
282
|
+
labels[LABEL_PROCESSOR_ID] = processor_id
|
|
283
|
+
|
|
284
|
+
if workload_location := os.environ.get("DORY_WORKLOAD_LOCATION"):
|
|
285
|
+
labels[LABEL_WORKLOAD_LOCATION] = workload_location
|
|
286
|
+
|
|
287
|
+
# Managed by is always the orchestrator if we're in Dory context
|
|
288
|
+
if labels:
|
|
289
|
+
labels[LABEL_MANAGED_BY] = VALUE_ORCHESTRATOR_NAME
|
|
290
|
+
|
|
291
|
+
return labels
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def detect_workload_context() -> dict[str, Any]:
|
|
295
|
+
"""Detect workload context from environment.
|
|
296
|
+
|
|
297
|
+
Reads environment variables set by Kubernetes Downward API and
|
|
298
|
+
Dory-specific variables to determine the workload context.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Dictionary with context information:
|
|
302
|
+
- pod_name: str | None
|
|
303
|
+
- pod_namespace: str | None
|
|
304
|
+
- pod_ip: str | None
|
|
305
|
+
- node_name: str | None
|
|
306
|
+
- app_name: str | None
|
|
307
|
+
- processor_id: str | None
|
|
308
|
+
- workload_location: WorkloadLocation | None
|
|
309
|
+
- is_edge: bool
|
|
310
|
+
- is_kubernetes: bool
|
|
311
|
+
"""
|
|
312
|
+
pod_name = os.environ.get("POD_NAME")
|
|
313
|
+
pod_namespace = os.environ.get("POD_NAMESPACE")
|
|
314
|
+
pod_ip = os.environ.get("POD_IP")
|
|
315
|
+
node_name = os.environ.get("NODE_NAME")
|
|
316
|
+
app_name = os.environ.get("DORY_APP_NAME")
|
|
317
|
+
processor_id = os.environ.get("DORY_PROCESSOR_ID")
|
|
318
|
+
workload_location_str = os.environ.get("DORY_WORKLOAD_LOCATION", "")
|
|
319
|
+
|
|
320
|
+
workload_location = None
|
|
321
|
+
if workload_location_str == VALUE_EDGE:
|
|
322
|
+
workload_location = WorkloadLocation.EDGE
|
|
323
|
+
elif workload_location_str == VALUE_MANAGED:
|
|
324
|
+
workload_location = WorkloadLocation.MANAGED
|
|
325
|
+
|
|
326
|
+
is_kubernetes = pod_name is not None and pod_namespace is not None
|
|
327
|
+
is_edge = workload_location == WorkloadLocation.EDGE
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
"pod_name": pod_name,
|
|
331
|
+
"pod_namespace": pod_namespace,
|
|
332
|
+
"pod_ip": pod_ip,
|
|
333
|
+
"node_name": node_name,
|
|
334
|
+
"app_name": app_name,
|
|
335
|
+
"processor_id": processor_id,
|
|
336
|
+
"workload_location": workload_location,
|
|
337
|
+
"is_edge": is_edge,
|
|
338
|
+
"is_kubernetes": is_kubernetes,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# =============================================================================
|
|
343
|
+
# Node Selector Builders
|
|
344
|
+
# =============================================================================
|
|
345
|
+
|
|
346
|
+
def edge_node_selector() -> dict[str, str]:
|
|
347
|
+
"""Get node selector for edge nodes.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Node selector dictionary for edge node placement
|
|
351
|
+
"""
|
|
352
|
+
return {LABEL_NODE_TYPE: NodeType.EDGE.value}
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def managed_node_selector() -> dict[str, str]:
|
|
356
|
+
"""Get node selector for managed (Karpenter) nodes.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Node selector dictionary for managed node placement
|
|
360
|
+
"""
|
|
361
|
+
return {LABEL_WORKLOAD_TYPE: NodeType.APPLICATION.value}
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def edge_toleration() -> dict[str, Any]:
|
|
365
|
+
"""Get toleration for edge nodes.
|
|
366
|
+
|
|
367
|
+
Edge nodes typically have a taint to prevent non-edge workloads
|
|
368
|
+
from scheduling on them.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
Toleration dictionary for edge nodes
|
|
372
|
+
"""
|
|
373
|
+
return {
|
|
374
|
+
"key": "edge-node",
|
|
375
|
+
"operator": "Equal",
|
|
376
|
+
"value": "true",
|
|
377
|
+
"effect": "NoSchedule",
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
# =============================================================================
|
|
382
|
+
# Contract Documentation
|
|
383
|
+
# =============================================================================
|
|
384
|
+
|
|
385
|
+
CONTRACT_VERSION = "1.0"
|
|
386
|
+
|
|
387
|
+
LABEL_CONTRACT = """
|
|
388
|
+
Dory SDK/Orchestrator Label Contract v1.0
|
|
389
|
+
=========================================
|
|
390
|
+
|
|
391
|
+
This document defines the label contract between the Dory SDK and
|
|
392
|
+
the Dory Orchestrator. Both components MUST use these exact labels
|
|
393
|
+
for proper coordination.
|
|
394
|
+
|
|
395
|
+
IDENTITY LABELS
|
|
396
|
+
---------------
|
|
397
|
+
These labels identify the processor and its managing controller.
|
|
398
|
+
|
|
399
|
+
| Label Key | Description | Required | Example Value |
|
|
400
|
+
|----------------|--------------------------------|----------|--------------------|
|
|
401
|
+
| managed-by | Managing controller | Yes | dory-orchestrator |
|
|
402
|
+
| app | Application name | Yes | my-processor |
|
|
403
|
+
| processor-id | Unique processor identifier | No | proc-123 |
|
|
404
|
+
|
|
405
|
+
WORKLOAD LABELS
|
|
406
|
+
---------------
|
|
407
|
+
These labels control scheduling and workload placement.
|
|
408
|
+
|
|
409
|
+
| Label Key | Description | Values | Default |
|
|
410
|
+
|--------------------|---------------------------------|---------------|----------|
|
|
411
|
+
| workload-location | Where the pod runs | edge, managed | managed |
|
|
412
|
+
| workload-type | Node workload type (Karpenter) | application | - |
|
|
413
|
+
| node-type | Node classification | edge, system | - |
|
|
414
|
+
|
|
415
|
+
MIGRATION LABELS
|
|
416
|
+
----------------
|
|
417
|
+
These labels track failover and migration state.
|
|
418
|
+
|
|
419
|
+
| Label Key | Description | Values | Set By |
|
|
420
|
+
|------------------------|--------------------------------|------------|---------------|
|
|
421
|
+
| migrated-from-edge | Pod was migrated from edge | true | Orchestrator |
|
|
422
|
+
| original-workload-type | Original workload type | edge | Orchestrator |
|
|
423
|
+
| original-edge-node | Original edge node name | <node> | Orchestrator |
|
|
424
|
+
|
|
425
|
+
NODE SELECTORS
|
|
426
|
+
--------------
|
|
427
|
+
Edge workloads:
|
|
428
|
+
node-type: edge
|
|
429
|
+
|
|
430
|
+
Managed workloads (Karpenter):
|
|
431
|
+
workload-type: application
|
|
432
|
+
|
|
433
|
+
TOLERATIONS
|
|
434
|
+
-----------
|
|
435
|
+
Edge nodes have a taint that requires this toleration:
|
|
436
|
+
key: edge-node
|
|
437
|
+
operator: Equal
|
|
438
|
+
value: true
|
|
439
|
+
effect: NoSchedule
|
|
440
|
+
|
|
441
|
+
ENVIRONMENT VARIABLES
|
|
442
|
+
---------------------
|
|
443
|
+
The SDK expects these environment variables (set via Downward API):
|
|
444
|
+
|
|
445
|
+
| Variable | Source | Description |
|
|
446
|
+
|------------------------|-------------------------------|-----------------------|
|
|
447
|
+
| POD_NAME | metadata.name | Pod name |
|
|
448
|
+
| POD_NAMESPACE | metadata.namespace | Pod namespace |
|
|
449
|
+
| POD_IP | status.podIP | Pod IP address |
|
|
450
|
+
| NODE_NAME | spec.nodeName | Node name |
|
|
451
|
+
| DORY_APP_NAME | metadata.labels['app'] | App label value |
|
|
452
|
+
| DORY_PROCESSOR_ID | metadata.labels['processor-id']| Processor ID label |
|
|
453
|
+
| DORY_WORKLOAD_LOCATION | metadata.labels['workload-location'] | Location label |
|
|
454
|
+
|
|
455
|
+
FAILOVER FLOW
|
|
456
|
+
-------------
|
|
457
|
+
1. Edge pod running with:
|
|
458
|
+
- workload-location: edge
|
|
459
|
+
- node-type selector: edge
|
|
460
|
+
|
|
461
|
+
2. Edge node fails, Orchestrator:
|
|
462
|
+
- Deletes edge pod
|
|
463
|
+
- Creates new pod with:
|
|
464
|
+
- workload-location: edge (preserved)
|
|
465
|
+
- migrated-from-edge: true (added)
|
|
466
|
+
- original-edge-node: <old-node> (added)
|
|
467
|
+
- workload-type selector: application (changed)
|
|
468
|
+
|
|
469
|
+
3. Edge node recovers, Orchestrator:
|
|
470
|
+
- Deletes migrated pod
|
|
471
|
+
- Creates new pod on edge with original labels
|
|
472
|
+
|
|
473
|
+
USAGE EXAMPLE
|
|
474
|
+
-------------
|
|
475
|
+
```python
|
|
476
|
+
from dory.k8s.labels import DoryLabels, WorkloadLocation
|
|
477
|
+
|
|
478
|
+
# Create labels for edge processor
|
|
479
|
+
labels = DoryLabels(
|
|
480
|
+
app_name="my-processor",
|
|
481
|
+
processor_id="proc-123",
|
|
482
|
+
workload_location=WorkloadLocation.EDGE,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Use in Kubernetes manifest
|
|
486
|
+
pod_spec = {
|
|
487
|
+
"metadata": {
|
|
488
|
+
"labels": labels.to_dict(),
|
|
489
|
+
},
|
|
490
|
+
"spec": {
|
|
491
|
+
"nodeSelector": edge_node_selector(),
|
|
492
|
+
"tolerations": [edge_toleration()],
|
|
493
|
+
},
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def get_contract_documentation() -> str:
|
|
500
|
+
"""Get the label contract documentation.
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
Complete label contract documentation string
|
|
504
|
+
"""
|
|
505
|
+
return LABEL_CONTRACT
|
dory/migration/__init__.py
CHANGED
|
@@ -3,9 +3,58 @@
|
|
|
3
3
|
from dory.migration.state_manager import StateManager
|
|
4
4
|
from dory.migration.serialization import StateSerializer
|
|
5
5
|
from dory.migration.configmap import ConfigMapStore
|
|
6
|
+
from dory.migration.s3_store import S3Store, S3Config, OfflineBuffer
|
|
7
|
+
from dory.migration.transfer import (
|
|
8
|
+
TransferConfig,
|
|
9
|
+
TransferMetrics,
|
|
10
|
+
StateTransferError,
|
|
11
|
+
StateTransferTimeout,
|
|
12
|
+
StateSizeExceeded,
|
|
13
|
+
validate_state_size,
|
|
14
|
+
ORCHESTRATOR_STATE_TIMEOUT_SEC,
|
|
15
|
+
ORCHESTRATOR_MAX_STATE_SIZE,
|
|
16
|
+
)
|
|
17
|
+
from dory.migration.versioning import (
|
|
18
|
+
StateFormatVersion,
|
|
19
|
+
VersionedState,
|
|
20
|
+
StateMetadata,
|
|
21
|
+
VersionNegotiator,
|
|
22
|
+
VersionNegotiationResult,
|
|
23
|
+
get_sdk_version,
|
|
24
|
+
get_supported_versions,
|
|
25
|
+
is_version_supported,
|
|
26
|
+
serialize_state,
|
|
27
|
+
deserialize_state,
|
|
28
|
+
get_version_info,
|
|
29
|
+
SDK_STATE_VERSION,
|
|
30
|
+
)
|
|
6
31
|
|
|
7
32
|
__all__ = [
|
|
8
33
|
"StateManager",
|
|
9
34
|
"StateSerializer",
|
|
10
35
|
"ConfigMapStore",
|
|
36
|
+
"S3Store",
|
|
37
|
+
"S3Config",
|
|
38
|
+
"OfflineBuffer",
|
|
39
|
+
"TransferConfig",
|
|
40
|
+
"TransferMetrics",
|
|
41
|
+
"StateTransferError",
|
|
42
|
+
"StateTransferTimeout",
|
|
43
|
+
"StateSizeExceeded",
|
|
44
|
+
"validate_state_size",
|
|
45
|
+
"ORCHESTRATOR_STATE_TIMEOUT_SEC",
|
|
46
|
+
"ORCHESTRATOR_MAX_STATE_SIZE",
|
|
47
|
+
# Versioning
|
|
48
|
+
"StateFormatVersion",
|
|
49
|
+
"VersionedState",
|
|
50
|
+
"StateMetadata",
|
|
51
|
+
"VersionNegotiator",
|
|
52
|
+
"VersionNegotiationResult",
|
|
53
|
+
"get_sdk_version",
|
|
54
|
+
"get_supported_versions",
|
|
55
|
+
"is_version_supported",
|
|
56
|
+
"serialize_state",
|
|
57
|
+
"deserialize_state",
|
|
58
|
+
"get_version_info",
|
|
59
|
+
"SDK_STATE_VERSION",
|
|
11
60
|
]
|