Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
- *shared
- "src/hls-workflows/**"
- "src/hls-utilities/**"
hls-submit:
- *shared
- "src/hls-submit/**"

- name: List changed packages
id: packages
Expand Down
36 changes: 36 additions & 0 deletions pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ py-spy = "*"
[feature.dev.pypi-dependencies]
hls-utilities = { path = "./src/hls-utilities", editable = true, extras = ["dev"] }
hls-workflows = { path = "./src/hls-workflows", editable = true, extras = ["dev"] }
# Operator tooling for submitting experiment jobs; not installed in the image.
hls-submit = { path = "./src/hls-submit", editable = true, extras = ["dev"] }
# Type stubs for boto3 clients (e.g. CloudWatchLogsClient in scripts/plot_metrics.py)
boto3-stubs = { version = ">=1", extras = ["logs"] }

Expand Down
58 changes: 58 additions & 0 deletions src/hls-submit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# hls-submit

Operator tooling for running HLS science experiments (LaSRC, Fmask) on the AWS
Batch infrastructure deployed by `hls-orchestration`, e.g. for performance
testing or validating algorithm changes. It is not installed in the container
image.

Job queues and job definitions are looked up from the orchestration
CloudFormation stack (`--stack-name` or `HLS_STACK_NAME`) by logical ID, so no
ARNs are hardcoded. Job definitions are always submitted without a revision, so
Batch runs the latest ACTIVE one. Pass `--job-queue` and `--job-definition` to
skip the lookup.

## Usage

```
pixi run -e dev hls-submit --help
# or, outside of pixi
uv run --project src/hls-submit hls-submit --help
```

Every submit command is a dry run unless `--submit` is given:

```
export HLS_STACK_NAME=hls-mcp-development-viirs

# A CSV with several columns: name the one holding L1 input granule IDs
hls-submit fmask Belmanip2_HLS_sample_Jan_L1.csv --granule-column L1_ID --sensor L30

# A plain list of L1 IDs, one per line, with or without a header
hls-submit lasrc Belmanip2_HLS_sample_S2.csv --sensor S30 --lasrc-impl rust --submit

# Tag jobs for metrics (HLS_EXPERIMENT_FMASK_BUILD=baseline) and read
# Sentinel-2 inputs from a requester-pays bucket in another account
hls-submit fmask Belmanip2_HLS_sample_S2.csv --sensor S30 --rows 5 \
--experiment fmask_build=baseline \
--input-bucket hls-validation-datasets --input-prefix datasets/belmanip2/S30/ \
--requester-pays --submit

hls-submit terminate --sensor S30
hls-submit terminate --sensor S30 --terminate
```

`--sensor` filters rows by the L1 ID (`LC08`/`LC09` for L30, `S2A`-`S2D` for
S30), so a CSV mixing both sensors works. `.SAFE` suffixes and `A+B` twin
granules are cleaned up.

`--experiment KEY=VALUE` (repeatable) sets `HLS_EXPERIMENT_<KEY>=VALUE`, which
the workflows record as a CloudWatch metrics dimension; the values are also
added to job names. `lasrc` additionally tags each job with
`HLS_EXPERIMENT_LASRC_IMPL=<c|rust>`.

Metrics go to `/hls-orchestration/<stack>/log-metrics` unless
`--metric-log-group` is given; with neither a stack nor a log group, jobs do
not record metrics.

AWS credentials and profile come from the usual environment (`AWS_PROFILE`,
etc).
45 changes: 45 additions & 0 deletions src/hls-submit/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[project]
name = "hls-submit"
version = "0.1.0"
description = "Submit and manage HLS science experiment jobs on AWS Batch"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"boto3>=1.42.34",
"click>=8.1",
]

[project.scripts]
hls-submit = "hls_submit.cli:cli"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project.optional-dependencies]
dev = [
"boto3-stubs[batch,cloudformation]>=1.36.0",
"mypy>=1.14.0",
"pytest>=9.0.2",
"ruff>=0.9.0",
]

[dependency-groups]
dev = [
"hls-submit[dev]",
]

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = ["F", "E", "W", "I", "UP"]
ignore = ["E501", "E731"]

[tool.ruff.lint.isort]
known-first-party = ["hls_submit"]

[tool.mypy]
python_version = "3.13"
strict = true
1 change: 1 addition & 0 deletions src/hls-submit/src/hls_submit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Submit and manage HLS science experiment jobs on AWS Batch."""
32 changes: 32 additions & 0 deletions src/hls-submit/src/hls_submit/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""``hls-submit`` command line entry point."""

from __future__ import annotations

from typing import Any

import click
from botocore.exceptions import BotoCoreError, ClientError

from .fmask import fmask
from .lasrc import lasrc
from .terminate import terminate


class _AwsErrorGroup(click.Group):
"""Report AWS errors (expired credentials, missing stack) without a traceback."""

def invoke(self, ctx: click.Context) -> Any:
try:
return super().invoke(ctx)
except (BotoCoreError, ClientError) as err:
raise click.ClickException(f"AWS error: {err}") from err


@click.group(cls=_AwsErrorGroup)
def cli() -> None:
"""Submit and manage HLS science experiment jobs on AWS Batch."""


cli.add_command(fmask)
cli.add_command(lasrc)
cli.add_command(terminate)
Loading
Loading