firecube 0.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.
- firecube/__init__.py +23 -0
- firecube/__main__.py +20 -0
- firecube/_version.py +46 -0
- firecube/cli/_audience.py +135 -0
- firecube/cli/_command_schemas.py +112 -0
- firecube/cli/_ctx.py +85 -0
- firecube/cli/_errors.py +115 -0
- firecube/cli/_formatter.py +195 -0
- firecube/cli/_product.py +47 -0
- firecube/cli/_rename_hints.py +123 -0
- firecube/cli/_shared_options.py +247 -0
- firecube/cli/_slot_env.py +102 -0
- firecube/cli/_slot_planning.py +95 -0
- firecube/cli/_typed_options.py +181 -0
- firecube/cli/_uri_policy.py +110 -0
- firecube/cli/advise.py +321 -0
- firecube/cli/archive.py +870 -0
- firecube/cli/catalog.py +196 -0
- firecube/cli/chunks/__init__.py +74 -0
- firecube/cli/chunks/_claims.py +229 -0
- firecube/cli/chunks/_common.py +84 -0
- firecube/cli/chunks/_delete.py +376 -0
- firecube/cli/chunks/_list.py +334 -0
- firecube/cli/chunks/_manager.py +161 -0
- firecube/cli/chunks/_runs.py +294 -0
- firecube/cli/chunks/_snapshots.py +165 -0
- firecube/cli/completion.py +73 -0
- firecube/cli/main.py +866 -0
- firecube/cli/parquet.py +289 -0
- firecube/cli/plugins/__init__.py +28 -0
- firecube/cli/plugins/commands.py +262 -0
- firecube/cli/plugins/introspect.py +304 -0
- firecube/cli/plugins/mgmt.py +198 -0
- firecube/cli/plugins/registry.py +132 -0
- firecube/cli/zarr.py +1043 -0
- firecube/core/__init__.py +14 -0
- firecube/core/api.py +88 -0
- firecube/core/cf/__init__.py +23 -0
- firecube/core/cf/check_ids.py +126 -0
- firecube/core/cf/report.py +57 -0
- firecube/core/cf/validator.py +313 -0
- firecube/core/config.py +307 -0
- firecube/core/controlplane/__init__.py +47 -0
- firecube/core/controlplane/_event_processor.py +136 -0
- firecube/core/controlplane/_helpers.py +114 -0
- firecube/core/controlplane/_projection.py +253 -0
- firecube/core/controlplane/_snapshot.py +215 -0
- firecube/core/controlplane/_wal_reader.py +402 -0
- firecube/core/controlplane/_wal_writer.py +589 -0
- firecube/core/controlplane/claims.py +294 -0
- firecube/core/controlplane/deletion.py +704 -0
- firecube/core/controlplane/events.py +219 -0
- firecube/core/controlplane/manager.py +990 -0
- firecube/core/controlplane/metrics.py +97 -0
- firecube/core/controlplane/repo.py +804 -0
- firecube/core/controlplane/repo_utils.py +73 -0
- firecube/core/controlplane/time_dim.py +111 -0
- firecube/core/controlplane/types.py +445 -0
- firecube/core/credentials.py +36 -0
- firecube/core/duckdb/__init__.py +27 -0
- firecube/core/duckdb/bridge.py +82 -0
- firecube/core/duckdb/io.py +30 -0
- firecube/core/errors.py +74 -0
- firecube/core/filesystem/__init__.py +60 -0
- firecube/core/filesystem/_obstore_compat.py +60 -0
- firecube/core/filesystem/fsspec_backend.py +308 -0
- firecube/core/filesystem/instrumentation.py +260 -0
- firecube/core/filesystem/obstore_backend.py +406 -0
- firecube/core/filesystem/ops.py +412 -0
- firecube/core/filesystem/protocol.py +130 -0
- firecube/core/filesystem/store_factory.py +149 -0
- firecube/core/formats/__init__.py +46 -0
- firecube/core/formats/_iso.py +84 -0
- firecube/core/formats/discovery.py +165 -0
- firecube/core/formats/hdf5.py +137 -0
- firecube/core/formats/netcdf.py +226 -0
- firecube/core/formats/zip.py +192 -0
- firecube/core/intake.py +530 -0
- firecube/core/observability/__init__.py +72 -0
- firecube/core/observability/_state.py +24 -0
- firecube/core/observability/logging.py +147 -0
- firecube/core/observability/metrics.py +230 -0
- firecube/core/observability/telemetry/__init__.py +87 -0
- firecube/core/observability/telemetry/pushgateway.py +339 -0
- firecube/core/observability/telemetry/sinks.py +155 -0
- firecube/core/observability/tracing.py +126 -0
- firecube/core/product/__init__.py +45 -0
- firecube/core/product/identity.py +76 -0
- firecube/core/product/resolver.py +121 -0
- firecube/core/product/target.py +120 -0
- firecube/core/runtime.py +187 -0
- firecube/core/slot_index.py +205 -0
- firecube/core/storage/__init__.py +45 -0
- firecube/core/storage/binding.py +39 -0
- firecube/core/storage/cache_key.py +40 -0
- firecube/core/storage/completion.py +245 -0
- firecube/core/storage/driver_config.py +125 -0
- firecube/core/storage/results.py +30 -0
- firecube/core/storage/session.py +431 -0
- firecube/core/storage/transfer.py +142 -0
- firecube/core/storage/uri.py +173 -0
- firecube/core/tensogram/__init__.py +37 -0
- firecube/core/tensogram/_compat.py +36 -0
- firecube/core/tensogram/controlplane_codec.py +230 -0
- firecube/core/tensogram/converter.py +390 -0
- firecube/core/tensogram/metadata.py +274 -0
- firecube/core/tensogram/restore.py +303 -0
- firecube/core/tensogram/schema.py +93 -0
- firecube/core/uris.py +182 -0
- firecube/core/workspaces.py +68 -0
- firecube/core/zarr/__init__.py +69 -0
- firecube/core/zarr/_reserved_attrs.py +47 -0
- firecube/core/zarr/_reserved_root_attrs.py +74 -0
- firecube/core/zarr/io.py +88 -0
- firecube/core/zarr/layers.py +184 -0
- firecube/core/zarr/multires.py +178 -0
- firecube/core/zarr/region_writer.py +782 -0
- firecube/core/zarr/scrub.py +200 -0
- firecube/core/zarr/state.py +258 -0
- firecube/core/zarr/time_decode.py +71 -0
- firecube/core/zarr/validation.py +622 -0
- firecube/ingestor/__init__.py +27 -0
- firecube/ingestor/api.py +215 -0
- firecube/ingestor/config/__init__.py +19 -0
- firecube/ingestor/config/coercion.py +157 -0
- firecube/ingestor/config/engine.py +188 -0
- firecube/ingestor/contracts/__init__.py +14 -0
- firecube/ingestor/contracts/interfaces.py +235 -0
- firecube/ingestor/devtools/_templates/__init__.py +14 -0
- firecube/ingestor/devtools/_templates/ingestor_base.py.tpl +72 -0
- firecube/ingestor/devtools/_templates/ingestor_direct_zarr.py.tpl +66 -0
- firecube/ingestor/devtools/_templates/ingestor_parquet.py.tpl +59 -0
- firecube/ingestor/devtools/_templates/ingestor_zarr.py.tpl +55 -0
- firecube/ingestor/devtools/_templates/readme.md.tpl +40 -0
- firecube/ingestor/devtools/scaffolding.py +229 -0
- firecube/ingestor/errors.py +79 -0
- firecube/ingestor/extensions/_binning.py +152 -0
- firecube/ingestor/extensions/duck.py +153 -0
- firecube/ingestor/extensions/grid.py +287 -0
- firecube/ingestor/extensions/healpix.py +330 -0
- firecube/ingestor/registry/__init__.py +14 -0
- firecube/ingestor/registry/loader.py +144 -0
- firecube/ingestor/registry/metadata.py +97 -0
- firecube/ingestor/registry/version_compat.py +95 -0
- firecube/ingestor/runtime/__init__.py +14 -0
- firecube/ingestor/runtime/aggregation.py +112 -0
- firecube/ingestor/runtime/base.py +836 -0
- firecube/ingestor/runtime/base_hooks.py +129 -0
- firecube/ingestor/runtime/batching.py +161 -0
- firecube/ingestor/runtime/configure.py +195 -0
- firecube/ingestor/runtime/coverage.py +177 -0
- firecube/ingestor/runtime/engine.py +793 -0
- firecube/ingestor/runtime/parallel_evidence.py +75 -0
- firecube/ingestor/runtime/parallel_execution_state.py +28 -0
- firecube/ingestor/runtime/parallel_gate.py +164 -0
- firecube/ingestor/runtime/parallel_run_id.py +48 -0
- firecube/ingestor/runtime/preflight.py +40 -0
- firecube/ingestor/runtime/recording.py +425 -0
- firecube/ingestor/runtime/resume_guard.py +556 -0
- firecube/ingestor/runtime/resume_types.py +65 -0
- firecube/ingestor/runtime/telemetry.py +171 -0
- firecube/ingestor/runtime/tensogram/__init__.py +14 -0
- firecube/ingestor/runtime/tensogram/strategy.py +164 -0
- firecube/ingestor/runtime/workspace.py +220 -0
- firecube/ingestor/runtime/zarr/__init__.py +34 -0
- firecube/ingestor/runtime/zarr/append.py +466 -0
- firecube/ingestor/runtime/zarr/append_services.py +650 -0
- firecube/ingestor/runtime/zarr/batch_runner.py +190 -0
- firecube/ingestor/runtime/zarr/contracts.py +58 -0
- firecube/ingestor/runtime/zarr/existing_cube_check.py +282 -0
- firecube/ingestor/runtime/zarr/resume_cache.py +92 -0
- firecube/ingestor/runtime/zarr/staged_metadata.py +249 -0
- firecube/ingestor/runtime/zarr/strategies/__init__.py +15 -0
- firecube/ingestor/runtime/zarr/strategies/append.py +172 -0
- firecube/ingestor/runtime/zarr/strategies/indexed_region.py +426 -0
- firecube/ingestor/runtime/zarr/write.py +212 -0
- firecube/ingestor/runtime/zarr/write_context.py +139 -0
- firecube/ingestor/templates/__init__.py +19 -0
- firecube/ingestor/templates/config.py +118 -0
- firecube/ingestor/templates/direct_zarr.py +790 -0
- firecube/ingestor/templates/generic.py +462 -0
- firecube/ingestor/templates/generic_tensogram.py +170 -0
- firecube/ingestor/types/__init__.py +19 -0
- firecube/ingestor/types/config.py +89 -0
- firecube/ingestor/types/context.py +409 -0
- firecube/ingestor/types/manifest.py +53 -0
- firecube/ingestor/types/planned_range.py +152 -0
- firecube/ingestor/types/result_metrics.py +386 -0
- firecube/ingestor/utils/__init__.py +14 -0
- firecube/ingestor/utils/duckdb_utils.py +235 -0
- firecube/ingestor/validation.py +55 -0
- firecube-0.1.4.dist-info/METADATA +242 -0
- firecube-0.1.4.dist-info/RECORD +197 -0
- firecube-0.1.4.dist-info/WHEEL +4 -0
- firecube-0.1.4.dist-info/entry_points.txt +2 -0
- firecube-0.1.4.dist-info/licenses/AUTHORS.md +5 -0
- firecube-0.1.4.dist-info/licenses/LICENSE +201 -0
firecube/cli/main.py
ADDED
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
# Copyright 2025-2026 EUMETSAT
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Unified Click-based CLI entrypoint for Firecube."""
|
|
16
|
+
|
|
17
|
+
# pyright: reportMissingImports=false, reportFunctionMemberAccess=false, reportCallIssue=false
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import logging
|
|
23
|
+
import os
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
import click
|
|
27
|
+
|
|
28
|
+
from firecube.cli._command_schemas import IngestCommandConfig
|
|
29
|
+
from firecube.cli._ctx import get_storage_config
|
|
30
|
+
from firecube.cli._errors import MissingProductNameError, wrap_user_facing_errors
|
|
31
|
+
from firecube.cli._formatter import (
|
|
32
|
+
COMMAND_GROUPS,
|
|
33
|
+
OPTION_GROUPS,
|
|
34
|
+
FirecubeGroup,
|
|
35
|
+
install_option_groups_patch,
|
|
36
|
+
)
|
|
37
|
+
from firecube.cli._product import require_full_uri
|
|
38
|
+
from firecube.cli._rename_hints import install_rename_hints
|
|
39
|
+
from firecube.cli._shared_options import (
|
|
40
|
+
storage_driver_option,
|
|
41
|
+
storage_type_option,
|
|
42
|
+
write_mode_option,
|
|
43
|
+
)
|
|
44
|
+
from firecube.cli._typed_options import TypedOptionsParam, coerce_options_for_plugin
|
|
45
|
+
from firecube.cli.advise import advise as advise_group
|
|
46
|
+
from firecube.cli.archive import archive as archive_group
|
|
47
|
+
from firecube.cli.catalog import catalog as catalog_group
|
|
48
|
+
from firecube.cli.chunks import chunks as chunks_group
|
|
49
|
+
from firecube.cli.completion import completion_cmd
|
|
50
|
+
from firecube.cli.parquet import parquet as parquet_group
|
|
51
|
+
from firecube.cli.plugins import plugins as plugins_group
|
|
52
|
+
from firecube.cli.zarr import zarr as zarr_group
|
|
53
|
+
from firecube.core.config import get_plugin_defaults, load_config_file
|
|
54
|
+
from firecube.core.observability import set_current_span_attribute, span
|
|
55
|
+
from firecube.core.product.resolver import ProductResolver
|
|
56
|
+
from firecube.core.storage.binding import StorageBinding
|
|
57
|
+
from firecube.core.storage.driver_config import StorageDriverConfig
|
|
58
|
+
from firecube.core.storage.session import StorageSession
|
|
59
|
+
from firecube.core.uris import (
|
|
60
|
+
is_remote_target,
|
|
61
|
+
local_path_from_target,
|
|
62
|
+
)
|
|
63
|
+
from firecube.ingestor.api import IngestContext, StorageContext
|
|
64
|
+
from firecube.ingestor.registry.loader import discover_ingestors
|
|
65
|
+
|
|
66
|
+
log = logging.getLogger("firecube.cli")
|
|
67
|
+
|
|
68
|
+
install_option_groups_patch()
|
|
69
|
+
install_rename_hints()
|
|
70
|
+
|
|
71
|
+
CONTEXT_SETTINGS = {
|
|
72
|
+
"help_option_names": ["-h", "--help"],
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
COMMAND_GROUPS["firecube"] = [
|
|
76
|
+
{
|
|
77
|
+
"name": "Core",
|
|
78
|
+
"commands": ["ingest", "archive"],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "Inspect",
|
|
82
|
+
"commands": ["zarr", "parquet", "chunks", "catalog"],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "Tools",
|
|
86
|
+
"commands": ["plugins", "advise", "completion"],
|
|
87
|
+
},
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
OPTION_GROUPS.update(
|
|
91
|
+
{
|
|
92
|
+
"firecube ingest": [
|
|
93
|
+
{
|
|
94
|
+
"name": "Input",
|
|
95
|
+
"options": ["--input-data", "--target", "--product-name", "--output-format"],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "Execution",
|
|
99
|
+
"options": [
|
|
100
|
+
"--write-mode",
|
|
101
|
+
"--storage-type",
|
|
102
|
+
"--storage-driver",
|
|
103
|
+
"--in-memory",
|
|
104
|
+
"--slot-start",
|
|
105
|
+
"--slot-end",
|
|
106
|
+
"--slot-size",
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "Advanced",
|
|
111
|
+
"options": ["--option", "--show-options"],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
"firecube archive create": [
|
|
115
|
+
{
|
|
116
|
+
"name": "Required",
|
|
117
|
+
"options": ["--source", "--archive"],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "Filtering",
|
|
121
|
+
"options": ["--start-date", "--end-date", "--group", "--variables"],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "Encoding",
|
|
125
|
+
"options": ["--compression"],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "Behavior",
|
|
129
|
+
"options": ["--overwrite", "--allow-nan", "--allow-inf"],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
"firecube archive restore": [
|
|
133
|
+
{
|
|
134
|
+
"name": "Required",
|
|
135
|
+
"options": ["--archive", "--target"],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "Behavior",
|
|
139
|
+
"options": ["--overwrite"],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
"firecube chunks list": [
|
|
143
|
+
{
|
|
144
|
+
"name": "Filtering",
|
|
145
|
+
"options": [
|
|
146
|
+
"--product-name",
|
|
147
|
+
"--start-date",
|
|
148
|
+
"--end-date",
|
|
149
|
+
"--time-range",
|
|
150
|
+
"--include-span",
|
|
151
|
+
"--type",
|
|
152
|
+
"--pattern",
|
|
153
|
+
"--meta",
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "Output",
|
|
158
|
+
"options": ["--format", "--limit"],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "Advanced",
|
|
162
|
+
"options": ["--workspace"],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
"firecube chunks delete": [
|
|
166
|
+
{
|
|
167
|
+
"name": "Scope",
|
|
168
|
+
"options": ["--product-name", "--all-products"],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "Filters",
|
|
172
|
+
"options": [
|
|
173
|
+
"--pattern",
|
|
174
|
+
"--type",
|
|
175
|
+
"--meta",
|
|
176
|
+
"--start-date",
|
|
177
|
+
"--end-date",
|
|
178
|
+
"--range",
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "Mode",
|
|
183
|
+
"options": ["--manifest-only", "--storage-only", "--include-metadata"],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"name": "Safety",
|
|
187
|
+
"options": ["--dry-run", "--yes-i-really-mean-it"],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"name": "Context",
|
|
191
|
+
"options": ["--workspace"],
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
"firecube chunks delete-span": [
|
|
195
|
+
{
|
|
196
|
+
"name": "Required",
|
|
197
|
+
"options": ["--product-name"],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"name": "Filtering",
|
|
201
|
+
"options": ["--run-id", "--batch-id", "--group", "--meta", "--include-replaced"],
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"name": "Safety",
|
|
205
|
+
"options": ["--dry-run", "--force", "--yes-i-really-mean-it"],
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"name": "Advanced",
|
|
209
|
+
"options": ["--workspace"],
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
"firecube chunks runs list": [
|
|
213
|
+
{
|
|
214
|
+
"name": "Required",
|
|
215
|
+
"options": ["--product-name"],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"name": "Output",
|
|
219
|
+
"options": ["--status", "--format", "--workspace"],
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
"firecube chunks runs abandon": [
|
|
223
|
+
{
|
|
224
|
+
"name": "Required",
|
|
225
|
+
"options": ["--product-name", "--run-id", "--reason"],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"name": "Safety",
|
|
229
|
+
"options": ["--dry-run", "--yes-i-really-mean-it"],
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"name": "Advanced",
|
|
233
|
+
"options": ["--workspace"],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
"firecube chunks claims clear": [
|
|
237
|
+
{
|
|
238
|
+
"name": "Scope",
|
|
239
|
+
"options": ["--domain", "--all-stale"],
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"name": "Required",
|
|
243
|
+
"options": ["--product-name"],
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"name": "Safety",
|
|
247
|
+
"options": ["--dry-run", "--yes-i-really-mean-it"],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "Advanced",
|
|
251
|
+
"options": ["--workspace", "--force"],
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
"firecube catalog intake": [
|
|
255
|
+
{
|
|
256
|
+
"name": "Required",
|
|
257
|
+
"options": ["--product", "--output", "--collection-id"],
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"name": "Optional",
|
|
261
|
+
"options": ["--include-storage-options", "--no-storage-options"],
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
"firecube zarr validate": [
|
|
265
|
+
{
|
|
266
|
+
"name": "Required",
|
|
267
|
+
"options": ["--product", "--group"],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"name": "Options",
|
|
271
|
+
"options": [
|
|
272
|
+
"--timeout",
|
|
273
|
+
"--max-chunks",
|
|
274
|
+
"--on-timeout",
|
|
275
|
+
"--storage-type",
|
|
276
|
+
"--storage-driver",
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
"firecube zarr slots": [
|
|
281
|
+
{
|
|
282
|
+
"name": "Required",
|
|
283
|
+
"options": [
|
|
284
|
+
"--target",
|
|
285
|
+
"--product-name",
|
|
286
|
+
"--storage-type",
|
|
287
|
+
"--storage-driver",
|
|
288
|
+
"--write-mode",
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"name": "Options",
|
|
293
|
+
"options": ["--slot-size", "--no-resume", "--format"],
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
"firecube zarr multires": [
|
|
297
|
+
{
|
|
298
|
+
"name": "Required",
|
|
299
|
+
"options": ["--target", "--product-name", "--storage-type", "--storage-driver"],
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"name": "Options",
|
|
303
|
+
"options": ["--resolutions"],
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
"firecube zarr preallocate": [
|
|
307
|
+
{
|
|
308
|
+
"name": "Required",
|
|
309
|
+
"options": [
|
|
310
|
+
"--target",
|
|
311
|
+
"--product-name",
|
|
312
|
+
"--storage-type",
|
|
313
|
+
"--storage-driver",
|
|
314
|
+
"--write-mode",
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"name": "Input",
|
|
319
|
+
"options": ["--input-data"],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"name": "Advanced",
|
|
323
|
+
"options": ["--option"],
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
"firecube parquet validate": [
|
|
327
|
+
{
|
|
328
|
+
"name": "Required",
|
|
329
|
+
"options": ["--product-name"],
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
"firecube parquet consolidate": [
|
|
333
|
+
{
|
|
334
|
+
"name": "Required",
|
|
335
|
+
"options": ["--product", "--output"],
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"name": "Options",
|
|
339
|
+
"options": ["--codec", "--storage-type", "--storage-driver"],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
"firecube plugins list": [
|
|
343
|
+
{
|
|
344
|
+
"name": "Options",
|
|
345
|
+
"options": ["--format"],
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
"firecube plugins describe": [
|
|
349
|
+
{
|
|
350
|
+
"name": "Options",
|
|
351
|
+
"options": ["--format"],
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
"firecube plugins explain": [
|
|
355
|
+
{
|
|
356
|
+
"name": "Options",
|
|
357
|
+
"options": ["--format"],
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
"firecube plugins install": [
|
|
361
|
+
{
|
|
362
|
+
"name": "Options",
|
|
363
|
+
"options": ["--editable"],
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
"firecube advise batch-size": [
|
|
367
|
+
{
|
|
368
|
+
"name": "Required",
|
|
369
|
+
"options": ["--product", "--group"],
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
"firecube advise compliance": [
|
|
373
|
+
{
|
|
374
|
+
"name": "Required",
|
|
375
|
+
"options": ["--profile", "--product", "--group"],
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
"name": "Options",
|
|
379
|
+
"options": [
|
|
380
|
+
"--format",
|
|
381
|
+
"--strict",
|
|
382
|
+
"--storage-type",
|
|
383
|
+
"--storage-driver",
|
|
384
|
+
],
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
"firecube archive info": [
|
|
388
|
+
{
|
|
389
|
+
"name": "Required",
|
|
390
|
+
"options": ["--archive"],
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"name": "Options",
|
|
394
|
+
"options": ["--format"],
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
"firecube archive validate": [
|
|
398
|
+
{
|
|
399
|
+
"name": "Required",
|
|
400
|
+
"options": ["--archive"],
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"name": "Options",
|
|
404
|
+
"options": ["--quick"],
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
"firecube archive list": [
|
|
408
|
+
{
|
|
409
|
+
"name": "Required",
|
|
410
|
+
"options": ["--archive"],
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
"firecube chunks claims list": [
|
|
414
|
+
{
|
|
415
|
+
"name": "Filtering",
|
|
416
|
+
"options": ["--product-name"],
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"name": "Output",
|
|
420
|
+
"options": ["--format"],
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"name": "Advanced",
|
|
424
|
+
"options": ["--workspace"],
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
"firecube chunks snapshots rebuild": [
|
|
428
|
+
{
|
|
429
|
+
"name": "Required",
|
|
430
|
+
"options": ["--product-name"],
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
"name": "Safety",
|
|
434
|
+
"options": ["--dry-run"],
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"name": "Output",
|
|
438
|
+
"options": ["--format"],
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"name": "Advanced",
|
|
442
|
+
"options": ["--workspace"],
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
"firecube chunks snapshots status": [
|
|
446
|
+
{
|
|
447
|
+
"name": "Required",
|
|
448
|
+
"options": ["--product-name"],
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"name": "Output",
|
|
452
|
+
"options": ["--format"],
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"name": "Advanced",
|
|
456
|
+
"options": ["--workspace"],
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@click.group(cls=FirecubeGroup, context_settings=CONTEXT_SETTINGS)
|
|
464
|
+
@click.version_option(package_name="firecube", message="Firecube %(version)s")
|
|
465
|
+
@click.option(
|
|
466
|
+
"--config-file",
|
|
467
|
+
type=click.Path(path_type=Path),
|
|
468
|
+
help="Firecube TOML config file (default: ~/.config/firecube/config.toml)",
|
|
469
|
+
)
|
|
470
|
+
@click.pass_context
|
|
471
|
+
def cli(ctx: click.Context, config_file: Path | None) -> None:
|
|
472
|
+
"""batch ingestion tool for EO datasets
|
|
473
|
+
|
|
474
|
+
Batch ingestion and management tool for Earth Observation datasets.
|
|
475
|
+
Writes Zarr and Parquet stores with built-in resume safety, idempotent
|
|
476
|
+
chunk tracking, and multi-resolution support. Run firecube ingest to
|
|
477
|
+
start a job, or explore subcommands for storage management and diagnostics.
|
|
478
|
+
"""
|
|
479
|
+
ctx.ensure_object(dict)
|
|
480
|
+
if config_file is not None:
|
|
481
|
+
ctx.obj["config_file"] = config_file
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
@cli.command(
|
|
485
|
+
epilog="""\b
|
|
486
|
+
Examples:
|
|
487
|
+
# list available options for a plugin
|
|
488
|
+
firecube ingest <plugin> --show-options
|
|
489
|
+
# run ingestion from a local source to a local target
|
|
490
|
+
firecube ingest <plugin> \\
|
|
491
|
+
--input-data /path/to/input \\
|
|
492
|
+
--target file:///path/to/output.zarr \\
|
|
493
|
+
--product-name <name> \\
|
|
494
|
+
--storage-type local \\
|
|
495
|
+
--storage-driver fsspec \\
|
|
496
|
+
--output-format zarr \\
|
|
497
|
+
--write-mode direct
|
|
498
|
+
# run with staged write mode (write locally then upload)
|
|
499
|
+
firecube ingest <plugin> \\
|
|
500
|
+
--input-data /data/raw \\
|
|
501
|
+
--target s3://bucket/output.zarr \\
|
|
502
|
+
--product-name <name> \\
|
|
503
|
+
--storage-type s3 \\
|
|
504
|
+
--storage-driver fsspec \\
|
|
505
|
+
--output-format zarr \\
|
|
506
|
+
--write-mode staged
|
|
507
|
+
# pass plugin-specific options
|
|
508
|
+
firecube ingest <plugin> \\
|
|
509
|
+
--input-data /data/ \\
|
|
510
|
+
--target file:///tmp/output.zarr \\
|
|
511
|
+
--product-name <name> \\
|
|
512
|
+
--storage-type local \\
|
|
513
|
+
--storage-driver fsspec \\
|
|
514
|
+
--output-format zarr \\
|
|
515
|
+
--write-mode direct \\
|
|
516
|
+
--option horizon=LST \\
|
|
517
|
+
--option no_progress=true
|
|
518
|
+
See also: firecube plugins list, firecube chunks list, firecube advise batch-size
|
|
519
|
+
""",
|
|
520
|
+
)
|
|
521
|
+
@click.argument("plugin")
|
|
522
|
+
@click.option(
|
|
523
|
+
"-i",
|
|
524
|
+
"--input-data",
|
|
525
|
+
"input_data",
|
|
526
|
+
type=str,
|
|
527
|
+
required=False,
|
|
528
|
+
help="Raw plugin input data: local path, file:///abs/path, or s3:// prefix. Interpreted by the plugin.",
|
|
529
|
+
)
|
|
530
|
+
@click.option(
|
|
531
|
+
"-t",
|
|
532
|
+
"--target",
|
|
533
|
+
type=str,
|
|
534
|
+
required=False,
|
|
535
|
+
help=("Target product URI required (file:///abs/path or s3://bucket/key); -t short available."),
|
|
536
|
+
)
|
|
537
|
+
@click.option(
|
|
538
|
+
"-n",
|
|
539
|
+
"--product-name",
|
|
540
|
+
"product_name",
|
|
541
|
+
default=None,
|
|
542
|
+
help=(
|
|
543
|
+
"Logical product name. Overrides the plugin class default and the "
|
|
544
|
+
"per-plugin config default."
|
|
545
|
+
),
|
|
546
|
+
)
|
|
547
|
+
@click.option(
|
|
548
|
+
"--output-format", required=False, help="output format to deliver (e.g. parquet, zarr)"
|
|
549
|
+
)
|
|
550
|
+
@storage_type_option(
|
|
551
|
+
required=False,
|
|
552
|
+
extra_help=(
|
|
553
|
+
"Inferred from URI scheme when omitted (file://→local, s3://→s3). "
|
|
554
|
+
"Explicit value overrides inference and is rejected on mismatch."
|
|
555
|
+
),
|
|
556
|
+
)
|
|
557
|
+
@storage_driver_option(
|
|
558
|
+
required=False,
|
|
559
|
+
extra_help=("Defaults to fsspec; overridable via FIRECUBE_STORAGE_DRIVER or [storage].driver."),
|
|
560
|
+
)
|
|
561
|
+
@write_mode_option(required=False, extra_help="Required. No local-target inference.")
|
|
562
|
+
@click.option(
|
|
563
|
+
"--slot-start",
|
|
564
|
+
"slot_start",
|
|
565
|
+
type=int,
|
|
566
|
+
default=None,
|
|
567
|
+
help="Parallel ingestion: first slot index (inclusive).",
|
|
568
|
+
)
|
|
569
|
+
@click.option(
|
|
570
|
+
"--slot-end",
|
|
571
|
+
"slot_end",
|
|
572
|
+
type=int,
|
|
573
|
+
default=None,
|
|
574
|
+
help="Parallel ingestion: last slot index (exclusive).",
|
|
575
|
+
)
|
|
576
|
+
@click.option(
|
|
577
|
+
"--slot-size",
|
|
578
|
+
"slot_size",
|
|
579
|
+
type=int,
|
|
580
|
+
default=None,
|
|
581
|
+
help="Slot size for orchestrated parallel ingestion range derivation.",
|
|
582
|
+
)
|
|
583
|
+
@click.option(
|
|
584
|
+
"--slot-group",
|
|
585
|
+
default=None,
|
|
586
|
+
help=(
|
|
587
|
+
"Group name this worker owns when running multi-group parallel "
|
|
588
|
+
"ingestion. Only workers with matching group are scheduled writes for "
|
|
589
|
+
"that group. Defaults to None (all groups — single-group or "
|
|
590
|
+
"non-parallel mode)."
|
|
591
|
+
),
|
|
592
|
+
)
|
|
593
|
+
@click.option("--in-memory", is_flag=True, help="use in-memory DuckDB")
|
|
594
|
+
@click.option(
|
|
595
|
+
"--option",
|
|
596
|
+
"extra_options",
|
|
597
|
+
multiple=True,
|
|
598
|
+
type=TypedOptionsParam(),
|
|
599
|
+
help="Plugin/engine option in key=value form.",
|
|
600
|
+
)
|
|
601
|
+
@click.option(
|
|
602
|
+
"--show-options",
|
|
603
|
+
is_flag=True,
|
|
604
|
+
help="show available options for the specified plugin and exit",
|
|
605
|
+
)
|
|
606
|
+
@click.pass_context
|
|
607
|
+
@wrap_user_facing_errors
|
|
608
|
+
def ingest(
|
|
609
|
+
ctx: click.Context,
|
|
610
|
+
plugin: str,
|
|
611
|
+
input_data: str | None,
|
|
612
|
+
target: str | None,
|
|
613
|
+
product_name: str | None,
|
|
614
|
+
storage_type: str | None,
|
|
615
|
+
storage_driver: str | None,
|
|
616
|
+
output_format: str | None,
|
|
617
|
+
write_mode: str | None,
|
|
618
|
+
slot_start: int | None,
|
|
619
|
+
slot_end: int | None,
|
|
620
|
+
slot_size: int | None,
|
|
621
|
+
slot_group: str | None,
|
|
622
|
+
in_memory: bool,
|
|
623
|
+
extra_options,
|
|
624
|
+
show_options: bool,
|
|
625
|
+
) -> None:
|
|
626
|
+
"""run an ingestion job for a plugin
|
|
627
|
+
|
|
628
|
+
The plugin handles source reading; firecube manages write strategy (staged
|
|
629
|
+
or direct), pipeline parallelism, and chunk tracking. Use --option to pass
|
|
630
|
+
plugin-specific overrides, or --show-options to list available options for
|
|
631
|
+
a plugin.
|
|
632
|
+
"""
|
|
633
|
+
extra_options = coerce_options_for_plugin(plugin, extra_options)
|
|
634
|
+
ingest_cfg: IngestCommandConfig | None = None
|
|
635
|
+
if not show_options:
|
|
636
|
+
from firecube.cli._uri_policy import apply_smart_default, parse_product_uri
|
|
637
|
+
|
|
638
|
+
storage_type_resolved: str | None = storage_type
|
|
639
|
+
if target is not None:
|
|
640
|
+
parsed_target = parse_product_uri(target)
|
|
641
|
+
storage_type_resolved = apply_smart_default(parsed_target, storage_type)
|
|
642
|
+
# storage_driver flows through unresolved; build_storage_config handles
|
|
643
|
+
# env > config > default precedence downstream.
|
|
644
|
+
|
|
645
|
+
try:
|
|
646
|
+
ingest_cfg = IngestCommandConfig(
|
|
647
|
+
plugin=plugin,
|
|
648
|
+
input_data=input_data,
|
|
649
|
+
target=target or "",
|
|
650
|
+
write_mode=write_mode,
|
|
651
|
+
storage_type=storage_type_resolved,
|
|
652
|
+
storage_driver=storage_driver,
|
|
653
|
+
product_name=product_name,
|
|
654
|
+
output_format=output_format or "zarr",
|
|
655
|
+
in_memory=in_memory,
|
|
656
|
+
options=dict(extra_options),
|
|
657
|
+
)
|
|
658
|
+
except click.UsageError:
|
|
659
|
+
raise
|
|
660
|
+
|
|
661
|
+
if not show_options:
|
|
662
|
+
if target is None:
|
|
663
|
+
raise click.ClickException("Missing option '--target'.")
|
|
664
|
+
require_full_uri(str(target), option_name="--target")
|
|
665
|
+
|
|
666
|
+
if (slot_start is None) ^ (slot_end is None):
|
|
667
|
+
raise click.UsageError("--slot-start and --slot-end must be provided together")
|
|
668
|
+
|
|
669
|
+
plugins = discover_ingestors()
|
|
670
|
+
if plugin not in plugins:
|
|
671
|
+
raise click.ClickException(f"Unknown plugin: {plugin}")
|
|
672
|
+
|
|
673
|
+
ingestor_cls = plugins[plugin]
|
|
674
|
+
|
|
675
|
+
if show_options:
|
|
676
|
+
_print_plugin_options(plugin, ingestor_cls)
|
|
677
|
+
return
|
|
678
|
+
|
|
679
|
+
if input_data:
|
|
680
|
+
input_data_str = str(input_data)
|
|
681
|
+
if not is_remote_target(input_data_str):
|
|
682
|
+
local_source = local_path_from_target(input_data_str)
|
|
683
|
+
if not local_source.exists():
|
|
684
|
+
raise click.ClickException(f"Input data not found: {input_data}")
|
|
685
|
+
input_data = str(local_source)
|
|
686
|
+
else:
|
|
687
|
+
input_data = input_data_str
|
|
688
|
+
|
|
689
|
+
from firecube.core import observability
|
|
690
|
+
|
|
691
|
+
observability.init_observability(f"firecube-ingestor-{plugin}")
|
|
692
|
+
try:
|
|
693
|
+
with span(
|
|
694
|
+
"firecube.cli.ingest",
|
|
695
|
+
attributes={
|
|
696
|
+
"firecube.plugin": plugin,
|
|
697
|
+
"firecube.write_mode": str(write_mode or ""),
|
|
698
|
+
},
|
|
699
|
+
):
|
|
700
|
+
explicit_config = ctx.obj.get("config_file")
|
|
701
|
+
cfg = load_config_file(explicit_config, strict=explicit_config is not None)
|
|
702
|
+
assert ingest_cfg is not None
|
|
703
|
+
options = get_plugin_defaults(cfg, plugin)
|
|
704
|
+
from firecube.cli._slot_env import resolve_slot_range_from_env
|
|
705
|
+
|
|
706
|
+
resolved_slot_start, resolved_slot_end, resolved_slot_group = (
|
|
707
|
+
resolve_slot_range_from_env(
|
|
708
|
+
slot_start,
|
|
709
|
+
slot_end,
|
|
710
|
+
slot_size,
|
|
711
|
+
slot_group,
|
|
712
|
+
)
|
|
713
|
+
)
|
|
714
|
+
options["slot_start"] = resolved_slot_start
|
|
715
|
+
options["slot_end"] = resolved_slot_end
|
|
716
|
+
options["slot_size"] = slot_size
|
|
717
|
+
options["slot_group"] = resolved_slot_group
|
|
718
|
+
resolved_product_name = (
|
|
719
|
+
ingest_cfg.product_name
|
|
720
|
+
or get_plugin_defaults(cfg, plugin).get("default_product_name")
|
|
721
|
+
or getattr(ingestor_cls, "PRODUCT_NAME", None)
|
|
722
|
+
)
|
|
723
|
+
if not resolved_product_name:
|
|
724
|
+
raise MissingProductNameError(plugin)
|
|
725
|
+
|
|
726
|
+
target_str = ingest_cfg.target
|
|
727
|
+
require_full_uri(target_str, option_name="--target")
|
|
728
|
+
try:
|
|
729
|
+
identity = ProductResolver.resolve(
|
|
730
|
+
target_str,
|
|
731
|
+
format=ingest_cfg.output_format,
|
|
732
|
+
product_name=str(resolved_product_name),
|
|
733
|
+
)
|
|
734
|
+
except ValueError as exc:
|
|
735
|
+
raise click.UsageError(str(exc)) from exc
|
|
736
|
+
storage_overrides: dict[str, str | None] = {
|
|
737
|
+
"storage_type": str(ingest_cfg.storage_type)
|
|
738
|
+
if ingest_cfg.storage_type is not None
|
|
739
|
+
else None,
|
|
740
|
+
"storage_driver": str(ingest_cfg.storage_driver)
|
|
741
|
+
if ingest_cfg.storage_driver is not None
|
|
742
|
+
else None,
|
|
743
|
+
}
|
|
744
|
+
storage_config = get_storage_config(ctx, overrides=storage_overrides, cache=False)
|
|
745
|
+
options.setdefault("storage", {"type": storage_config.storage_type})
|
|
746
|
+
|
|
747
|
+
# Per HIGH-3 fix: read every option from the validated typed config, not raw Click params.
|
|
748
|
+
# IngestCommandConfig has applied defaults and validation; the raw params are inputs to
|
|
749
|
+
# its construction only. This ensures typed defaults (e.g. output_format="zarr") flow through.
|
|
750
|
+
final_write_mode = ingest_cfg.write_mode
|
|
751
|
+
set_current_span_attribute("firecube.write_mode", str(final_write_mode))
|
|
752
|
+
options["write_mode"] = final_write_mode
|
|
753
|
+
|
|
754
|
+
final_output_format = ingest_cfg.output_format
|
|
755
|
+
if final_output_format == "tensogram":
|
|
756
|
+
from firecube.ingestor.api import is_dataset_producer
|
|
757
|
+
|
|
758
|
+
if not is_dataset_producer(ingestor_cls):
|
|
759
|
+
raise click.ClickException(
|
|
760
|
+
f"Plugin '{plugin}' does not support --output-format tensogram. "
|
|
761
|
+
"It must implement the DatasetProducer protocol "
|
|
762
|
+
"(build_dataset + get_batch_groups)."
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
# Instantiate the ingestor *after* setting the global storage config so
|
|
766
|
+
# BaseIngestor can initialise ChunkManager with the correct output base.
|
|
767
|
+
ingestor = ingestor_cls()
|
|
768
|
+
|
|
769
|
+
for key, coerced_value in extra_options:
|
|
770
|
+
options[key] = coerced_value
|
|
771
|
+
|
|
772
|
+
try:
|
|
773
|
+
upload_workers = int(options.get("upload_workers", 4))
|
|
774
|
+
except (TypeError, ValueError) as exc:
|
|
775
|
+
raise click.BadParameter("upload_workers must be an integer >= 1.") from exc
|
|
776
|
+
if upload_workers < 1:
|
|
777
|
+
raise click.BadParameter("upload_workers must be at least 1.")
|
|
778
|
+
|
|
779
|
+
driver_config = StorageDriverConfig.from_storage_config(storage_config)
|
|
780
|
+
session = StorageSession(
|
|
781
|
+
StorageBinding(
|
|
782
|
+
identity=identity,
|
|
783
|
+
driver=driver_config,
|
|
784
|
+
)
|
|
785
|
+
)
|
|
786
|
+
plugin_target = identity.product_uri.to_str()
|
|
787
|
+
log.debug(
|
|
788
|
+
"Ingest target wiring (target=%s plugin_target=%s product_name=%s resolved_product_name=%s write_mode=%s storage_type=%s)",
|
|
789
|
+
target_str,
|
|
790
|
+
plugin_target,
|
|
791
|
+
identity.product_name,
|
|
792
|
+
resolved_product_name,
|
|
793
|
+
final_write_mode,
|
|
794
|
+
storage_config.storage_type,
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
# Generate Run ID (Engine-owned)
|
|
798
|
+
run_id = str(options.get("run_id") or options.get("manifest_run_id") or "")
|
|
799
|
+
if not run_id:
|
|
800
|
+
import uuid
|
|
801
|
+
|
|
802
|
+
hostname = os.getenv("HOSTNAME", "host")
|
|
803
|
+
run_id = f"{plugin}-{hostname}-{uuid.uuid4().hex}"
|
|
804
|
+
|
|
805
|
+
ingest_ctx = IngestContext(
|
|
806
|
+
source=input_data or "",
|
|
807
|
+
target=plugin_target,
|
|
808
|
+
in_memory=ingest_cfg.in_memory,
|
|
809
|
+
output_format=final_output_format,
|
|
810
|
+
options=options,
|
|
811
|
+
storage=StorageContext(output=session),
|
|
812
|
+
run_id=run_id,
|
|
813
|
+
)
|
|
814
|
+
result = ingestor.run(ingest_ctx)
|
|
815
|
+
|
|
816
|
+
if result.manifest:
|
|
817
|
+
click.echo(json.dumps(result.manifest, indent=2))
|
|
818
|
+
else:
|
|
819
|
+
click.echo(
|
|
820
|
+
json.dumps(
|
|
821
|
+
{
|
|
822
|
+
"output_path": result.outputs.primary,
|
|
823
|
+
"output_format": result.output_format,
|
|
824
|
+
},
|
|
825
|
+
indent=2,
|
|
826
|
+
)
|
|
827
|
+
)
|
|
828
|
+
finally:
|
|
829
|
+
from firecube.core.observability import shutdown_observability
|
|
830
|
+
|
|
831
|
+
shutdown_observability(timeout_millis=5000)
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
cli.add_command(archive_group, name="archive")
|
|
835
|
+
cli.add_command(advise_group, name="advise")
|
|
836
|
+
cli.add_command(chunks_group, name="chunks")
|
|
837
|
+
cli.add_command(plugins_group, name="plugins")
|
|
838
|
+
cli.add_command(zarr_group, name="zarr")
|
|
839
|
+
cli.add_command(catalog_group, name="catalog")
|
|
840
|
+
cli.add_command(parquet_group, name="parquet")
|
|
841
|
+
cli.add_command(completion_cmd, name="completion")
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def _print_plugin_options(plugin_name: str, ingestor_cls: type) -> None:
|
|
845
|
+
"""Introspect and print available options for a plugin using tiered config."""
|
|
846
|
+
click.echo(f"Available options for plugin '{plugin_name}':\n")
|
|
847
|
+
|
|
848
|
+
if not hasattr(ingestor_cls, "describe_options"):
|
|
849
|
+
click.echo(" (Plugin does not support option introspection)")
|
|
850
|
+
return
|
|
851
|
+
|
|
852
|
+
# Helper method on BaseIngestor returning dict[Tier, list[str]]
|
|
853
|
+
tiers = ingestor_cls.describe_options()
|
|
854
|
+
|
|
855
|
+
for tier, keys in tiers.items():
|
|
856
|
+
if not keys:
|
|
857
|
+
continue
|
|
858
|
+
|
|
859
|
+
click.echo(f"[{tier}]")
|
|
860
|
+
for key in keys:
|
|
861
|
+
click.echo(f" --option {key}")
|
|
862
|
+
click.echo("")
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
if __name__ == "__main__":
|
|
866
|
+
cli()
|