Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.260.002"
VERSION = "0.260.003"
IS_DEVELOPMENT = is_development_env_enabled()

SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax')
Expand Down
55 changes: 35 additions & 20 deletions application/single_app/functions_data_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -13202,6 +13202,7 @@ def _set_job_progress(
total_steps,
current_step=None,
status=DATA_MANAGEMENT_STATUS_RUNNING,
step_status=None,
allow_cancel_requested=False,
):
total_steps = max(1, total_steps)
Expand Down Expand Up @@ -13230,13 +13231,27 @@ def _set_job_progress(
saved_job.get("id"),
current_step or "progress",
saved_job,
status=status,
# A finished step stays "completed" even while the job itself keeps running.
status=step_status or status,
message=message,
details={"progress": saved_job.get("progress") if isinstance(saved_job.get("progress"), dict) else {}},
)
return saved_job


def _complete_job_step(job, message, completed_steps, total_steps, current_step, **kwargs):
"""Advance job progress and stamp the finished step as completed."""
return _set_job_progress(
job,
message,
completed_steps,
total_steps,
current_step=current_step,
step_status=DATA_MANAGEMENT_STATUS_COMPLETED,
**kwargs,
)


def _get_backup_fernet(settings, key_reference=None):
if not settings.get("encryption_enabled"):
return None
Expand Down Expand Up @@ -17973,7 +17988,7 @@ def execute_restore_job(job, settings):
container_client,
fernet,
))
_set_job_progress(job, "Cosmos restore step completed", 1, total_steps, current_step="cosmos")
_complete_job_step(job, "Cosmos restore step completed", 1, total_steps, "cosmos")

Check warning on line 17991 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

artifacts.extend(_execute_restore_search_resources(
job,
Expand All @@ -17983,7 +17998,7 @@ def execute_restore_job(job, settings):
container_client,
fernet,
))
_set_job_progress(job, "AI Search restore step completed", 2, total_steps, current_step="ai_search")
_complete_job_step(job, "AI Search restore step completed", 2, total_steps, "ai_search")

Check warning on line 18001 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

artifacts.extend(_execute_restore_source_blob_resources(
job,
Expand All @@ -17993,7 +18008,7 @@ def execute_restore_job(job, settings):
container_client,
fernet,
))
_set_job_progress(job, "Source blob restore step completed", 3, total_steps, current_step="source_blobs")
_complete_job_step(job, "Source blob restore step completed", 3, total_steps, "source_blobs")

Check warning on line 18011 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

warnings = list(restore_state.get("warnings") or [])
failed_resource_names = [
Expand Down Expand Up @@ -18142,7 +18157,7 @@ def execute_backup_job(job, settings):
warning,
"Skipped disabled Cosmos backup scope",
)
_set_job_progress(job, "Cosmos DB export step completed", 1, total_steps, current_step="cosmos")
_complete_job_step(job, "Cosmos DB export step completed", 1, total_steps, "cosmos")

Check warning on line 18160 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

if backup_plan.get("include_ai_search"):
artifacts.extend(_execute_backup_search_resources(
Expand All @@ -18164,7 +18179,7 @@ def execute_backup_job(job, settings):
warning,
"Skipped disabled AI Search backup scope",
)
_set_job_progress(job, "AI Search export step completed", 2, total_steps, current_step="ai_search")
_complete_job_step(job, "AI Search export step completed", 2, total_steps, "ai_search")

Check warning on line 18182 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

if backup_plan.get("include_source_blobs"):
source_blob_service_client = _get_source_blob_service_client()
Expand Down Expand Up @@ -18233,7 +18248,7 @@ def execute_backup_job(job, settings):
warning,
"Skipped disabled source blob backup scope",
)
_set_job_progress(job, "Source blob export step completed", 3, total_steps, current_step="source_blobs")
_complete_job_step(job, "Source blob export step completed", 3, total_steps, "source_blobs")

Check warning on line 18251 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

_assert_backup_job_lease(job)
artifacts = _backup_state_resource_artifacts(backup_state)
Expand Down Expand Up @@ -18424,13 +18439,13 @@ def execute_migration_job(job, settings):
allow_cancel_requested=True,
)
raise
_set_job_progress(job, "Validated migration selection plan", 1, total_steps, current_step="plan")
_complete_job_step(job, "Validated migration selection plan", 1, total_steps, "plan")

Check warning on line 18442 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state
_record_data_management_job_event(
job.get("id"),
"migration-plan",
job,
status=DATA_MANAGEMENT_STATUS_RUNNING,
status=DATA_MANAGEMENT_STATUS_COMPLETED,
message="Migration selection plan validated",
details={
"migration_plan": plan_summary,
Expand Down Expand Up @@ -18552,21 +18567,21 @@ def preview_heartbeat(message, completed_count=0):
"Pinned server-owned migration inventory preview",
)

_set_job_progress(job, "Migration inventory completed", 2, total_steps, current_step="inventory")
_complete_job_step(job, "Migration inventory completed", 2, total_steps, "inventory")
_set_job_progress(job, "Validating migration destinations", 2, total_steps, current_step="preflight")
migration_state = _run_data_management_migration_preflight(
job,
migration_state,
settings,
migration_plan,
)
_set_job_progress(job, "Destination migration preflight completed", 3, total_steps, current_step="preflight")
_complete_job_step(job, "Destination migration preflight completed", 3, total_steps, "preflight")
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state
_record_data_management_job_event(
job.get("id"),
"migration-preflight",
job,
status=DATA_MANAGEMENT_STATUS_RUNNING,
status=DATA_MANAGEMENT_STATUS_COMPLETED,
message="Verified source and destination migration access",
details=migration_state.get("preflight") if isinstance(migration_state.get("preflight"), dict) else {},
)
Expand All @@ -18578,7 +18593,7 @@ def preview_heartbeat(message, completed_count=0):
settings,
migration_plan,
)
_set_job_progress(job, "Destination Cosmos capacity prepared", 4, total_steps, current_step="capacity")
_complete_job_step(job, "Destination Cosmos capacity prepared", 4, total_steps, "capacity")

Check warning on line 18596 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state

_set_job_progress(job, "Migrating Cosmos records", 4, total_steps, current_step="cosmos")
Expand All @@ -18601,11 +18616,11 @@ def preview_heartbeat(message, completed_count=0):
job.get("id"),
f"migration-cosmos-{target_type}",
job,
status=DATA_MANAGEMENT_STATUS_RUNNING,
status=DATA_MANAGEMENT_STATUS_COMPLETED,
message=f"Migrated {target_type.replace('_', ' ')} Cosmos records",
details={"target_type": target_type, "artifacts": copied},
)
_set_job_progress(job, "Cosmos migration completed", 5, total_steps, current_step="cosmos")
_complete_job_step(job, "Cosmos migration completed", 5, total_steps, "cosmos")

Check warning on line 18623 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state

search_artifacts = []
Expand Down Expand Up @@ -18650,7 +18665,7 @@ def preview_heartbeat(message, completed_count=0):
)
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state
artifacts.extend(search_artifacts)
_set_job_progress(job, "AI Search migration completed", 6, total_steps, current_step="ai_search")
_complete_job_step(job, "AI Search migration completed", 6, total_steps, "ai_search")

Check warning on line 18668 in application/single_app/functions_data_management.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state

_set_job_progress(job, "Migrating source document blobs", 6, total_steps, current_step="source_blobs")
Expand All @@ -18663,7 +18678,7 @@ def preview_heartbeat(message, completed_count=0):
)
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state
artifacts.extend(source_blob_artifacts)
_set_job_progress(job, "Source blob migration completed", 7, total_steps, current_step="source_blobs")
_complete_job_step(job, "Source blob migration completed", 7, total_steps, "source_blobs")
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state

_set_job_progress(job, "Reconciling source and destination", 7, total_steps, current_step="reconciliation")
Expand All @@ -18679,12 +18694,12 @@ def preview_heartbeat(message, completed_count=0):
)
migration_state = job.get("migration_state") if isinstance(job.get("migration_state"), dict) else migration_state
artifacts.append(reconciliation_artifact)
_set_job_progress(job, "Migration reconciliation completed", 8, total_steps, current_step="reconciliation")
_complete_job_step(job, "Migration reconciliation completed", 8, total_steps, "reconciliation")
_record_data_management_job_event(
job.get("id"),
"migration-reconciliation",
job,
status=DATA_MANAGEMENT_STATUS_RUNNING,
status=DATA_MANAGEMENT_STATUS_COMPLETED,
message="Reconciled migration source and destination identities",
details=reconciliation_artifact,
)
Expand Down Expand Up @@ -18762,7 +18777,7 @@ def preview_heartbeat(message, completed_count=0):
settings,
"Migration execution completed",
)
_set_job_progress(job, "Migration execution completed", 10, total_steps, current_step="complete")
_complete_job_step(job, "Migration execution completed", 10, total_steps, "complete")

artifact_summaries = summarize_backup_artifacts(artifacts)
artifact_totals = _backup_artifact_totals(artifact_summaries)
Expand Down
23 changes: 16 additions & 7 deletions application/single_app/static/js/admin/admin_data_management.js
Original file line number Diff line number Diff line change
Expand Up @@ -3853,10 +3853,12 @@ function getMigrationLiveMetrics(job) {
const hasRecentProgress = timestampAgeSeconds(
job?.last_progress_at || migrationState.last_progress_at
) <= 10;
metrics.push({
label: "Liveness",
value: hasRecentProgress ? "Running - progress active" : "Running - alive, no recent progress",
});
if (!isTerminalJobStatus(job?.status)) {
metrics.push({
label: "Liveness",
value: hasRecentProgress ? "Running - progress active" : "Running - alive, no recent progress",
});
}
}
return metrics;
}
Expand All @@ -3869,15 +3871,18 @@ function getBackupLiveMetrics(job) {
const totals = backupState.totals && typeof backupState.totals === "object" ? backupState.totals : {};
const telemetry = backupState.telemetry && typeof backupState.telemetry === "object" ? backupState.telemetry : {};
const sourceCapacity = backupState.source_capacity && typeof backupState.source_capacity === "object" ? backupState.source_capacity : {};
const metrics = [
{ label: "Current container", value: telemetry.current_container || "Waiting" },
const metrics = [];
if (!isTerminalJobStatus(job?.status)) {
metrics.push({ label: "Current container", value: telemetry.current_container || "Waiting" });
}
metrics.push(
{ label: "Checkpoint position", value: formatNumber(telemetry.checkpoint_position || totals.checkpoint_count || 0) },
{ label: "Processed", value: formatNumber(telemetry.records_processed || totals.processed_count || 0) },
{ label: "Transferred", value: formatBytes(telemetry.bytes || totals.bytes || 0) },
{ label: "Request units", value: formatNumber(telemetry.request_units || totals.request_units || 0) },
{ label: "Retries / throttles", value: `${formatNumber(telemetry.retries || totals.retry_attempt_count || 0)} / ${formatNumber(telemetry.throttles || totals.throttle_count || 0)}` },
{ label: "Skipped / failed", value: `${formatNumber(totals.skipped_count || 0)} / ${formatNumber(totals.failed_count || 0)}` },
];
);
if (telemetry.elapsed_seconds !== undefined || totals.elapsed_seconds !== undefined) {
metrics.push({ label: "Elapsed", value: `${formatNumber(telemetry.elapsed_seconds ?? totals.elapsed_seconds ?? 0)}s` });
}
Expand Down Expand Up @@ -3923,6 +3928,10 @@ function getRestoreLiveMetrics(job) {
return metrics;
}

function isTerminalJobStatus(status) {
return ["completed", "completed_with_warnings", "failed", "canceled"].includes(String(status || ""));
}

function timestampAgeSeconds(value) {
const timestamp = Date.parse(value || "");
if (!Number.isFinite(timestamp)) {
Expand Down
Loading
Loading