Skip to content

Commit

Permalink
Merge pull request #42 from NREL/pp/status_upgrades
Browse files Browse the repository at this point in the history
Status upgrades + Apptainer
  • Loading branch information
ppinchuk authored Jan 2, 2024
2 parents a99acb1 + 598c184 commit b8a5b45
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 14 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/apptainer-build-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Apptainer Build and Publish

on:
push:
tags:
- '*'

jobs:
build-publish-container:
name: Build and Publish for ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
artifact_name: gaps.sif
asset_name: gaps-linux-amd64
body: GAPs Apptainer Image (ubuntu-latest)
# - os: windows-latest
# artifact_name: mything.exe
# asset_name: mything-windows-amd64
# - os: macos-latest
# artifact_name: mything
# asset_name: mything-macos-amd64
permissions:
contents: read
packages: write

container:
image: quay.io/singularity/singularity:v3.8.1
options: --privileged

steps:

- name: Check out code for the container builds
uses: actions/checkout@v2

- name: Build Container
run: |
singularity build gaps.sif Apptainer
# - name: Login and Deploy Container
# run: |
# echo ${{ secrets.GITHUB_TOKEN }} | singularity remote login -u ${{ secrets.GHCR_USERNAME }} --password-stdin oras://ghcr.io
# singularity push container.sif oras://ghcr.io/${GITHUB_REPOSITORY}:${tag}

- name: Upload container to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ matrix.artifact_name }}
asset_name: ${{ matrix.asset_name }}
tag: ${{ github.ref }}
overwrite: true
body: ${{ matrix.body }}
18 changes: 18 additions & 0 deletions Apptainer
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Bootstrap: docker
From: python:3.11

%labels
Author Paul Pinchuk
Maintainer [email protected]
URL https://github.com/NREL/gaps

%post
echo "Installing vim"
apt-get update && apt-get -y upgrade
apt-get -y --allow-unauthenticated install vim

echo "Installing GAPs..."
pip install NREL-gaps

%runscript
"$@"
24 changes: 24 additions & 0 deletions gaps/_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"""
GAPs CLI entry points.
"""
import click

from gaps.version import __version__
from gaps.cli.status import status_command


@click.group()
@click.version_option(version=__version__)
@click.pass_context
def main(ctx):
"""GAPs command line interface."""
ctx.ensure_object(dict)


main.add_command(status_command(), name="status")


if __name__ == "__main__":
# pylint: disable=no-value-for-parameter
main(obj={})
2 changes: 1 addition & 1 deletion gaps/cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
Generation CLI entry points.
Main CLI entry points.
"""
from functools import partial

Expand Down
9 changes: 8 additions & 1 deletion gaps/cli/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
"""
import logging
import datetime as dt
from pathlib import Path
from warnings import warn
from inspect import signature

from gaps.hpc import submit
from gaps.hpc import submit, DEFAULT_STDOUT_PATH
from gaps.status import (
DT_FMT,
Status,
Expand Down Expand Up @@ -153,6 +154,10 @@ def _kickoff_hpc_job(ctx, cmd, hardware_option, **kwargs):
id_msg = f" (Job ID #{out})" if out else ""
msg = f"Kicked off {command!r} job {name!r}{id_msg}"

stdout_dir = Path(kwargs.get("stdout_path", DEFAULT_STDOUT_PATH))
stdout_log_file = str(stdout_dir / f"{name}_{out}.o")
stdout_err_log_file = str(stdout_dir / f"{name}_{out}.e")

Status.mark_job_as_submitted(
ctx.obj["OUT_DIR"],
pipeline_step=ctx.obj["PIPELINE_STEP"],
Expand All @@ -164,6 +169,8 @@ def _kickoff_hpc_job(ctx, cmd, hardware_option, **kwargs):
StatusField.QOS: kwargs.get("qos") or QOSOption.UNSPECIFIED,
StatusField.JOB_STATUS: StatusOption.SUBMITTED,
StatusField.TIME_SUBMITTED: dt.datetime.now().strftime(DT_FMT),
StatusField.STDOUT_LOG: stdout_log_file,
StatusField.STDOUT_ERR_LOG: stdout_err_log_file,
},
)
logger.info(msg)
Expand Down
2 changes: 2 additions & 0 deletions gaps/cli/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ def main_monitor(folder, pipe_steps, status, include, recursive):
for directory in folders:
if not directory.is_dir():
continue
if directory.name == Status.HIDDEN_SUB_DIR:
continue

pipe_status = Status(directory)
if not pipe_status:
Expand Down
11 changes: 11 additions & 0 deletions gaps/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class StatusField(CaseInsensitiveEnum):
TOTAL_RUNTIME = "total_runtime"
RUNTIME_SECONDS = "runtime_seconds"
MONITOR_PID = "monitor_pid"
STDOUT_LOG = "stdout_log"
STDOUT_ERR_LOG = "stdout_err_log"


# pylint: disable=no-member
Expand Down Expand Up @@ -865,6 +867,7 @@ def _add_elapsed_time(status_df):
has_not_failed = status_df[StatusField.JOB_STATUS] != StatusOption.FAILED
mask = has_start_time & (has_no_end_time & has_not_failed)

status_df = _add_time_cols_if_needed(status_df)
start_times = status_df.loc[mask, StatusField.TIME_START]
start_times = pd.to_datetime(start_times, format=DT_FMT)
elapsed_times = dt.datetime.now() - start_times
Expand All @@ -876,6 +879,14 @@ def _add_elapsed_time(status_df):
return status_df


def _add_time_cols_if_needed(status_df):
"""Adds any missing time cols to avoid pandas 2.0 warnings"""
for col in [StatusField.RUNTIME_SECONDS, StatusField.TOTAL_RUNTIME]:
if col not in status_df:
status_df[col] = None
return status_df


def _load(fpath):
"""Load status json."""
if fpath.is_file():
Expand Down
2 changes: 1 addition & 1 deletion gaps/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""GAPs Version Number. """

__version__ = "0.6.6"
__version__ = "0.6.7"
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@
"dev": TEST_REQUIREMENTS + DEV_REQUIREMENTS,
"docs": TEST_REQUIREMENTS + DEV_REQUIREMENTS + DOC_REQUIREMENTS,
},
entry_points={"console_scripts": ["gaps=gaps._cli:main"]},
)
2 changes: 2 additions & 0 deletions tests/cli/test_cli_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ def _test_submit(cmd):
status = json.load(status_fh)

assert status["run"][job_name][StatusField.HARDWARE] == "eagle"
assert "9999.o" in status["run"][job_name][StatusField.STDOUT_LOG]
assert "9999.e" in status["run"][job_name][StatusField.STDOUT_ERR_LOG]
if high_qos:
assert status["run"][job_name][StatusField.QOS] == "high"
else:
Expand Down
33 changes: 22 additions & 11 deletions tests/cli/test_cli_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import json
import shutil
from pathlib import Path
from contextlib import nullcontext

import psutil
import pytest

from gaps.status import HardwareStatusRetriever, StatusOption, Status
from gaps.cli.status import status_command
from gaps._cli import main
from gaps.warnings import gapsWarning


Expand All @@ -36,7 +38,10 @@
+ "-s dne".split(),
],
)
def test_status(test_data_dir, cli_runner, extra_args, monkeypatch):
@pytest.mark.parametrize("test_main_entry", [True, False])
def test_status(
test_data_dir, cli_runner, extra_args, test_main_entry, monkeypatch
):
"""Test the status command."""

monkeypatch.setattr(psutil, "pid_exists", lambda *__: True, raising=True)
Expand All @@ -47,18 +52,23 @@ def test_status(test_data_dir, cli_runner, extra_args, monkeypatch):
raising=True,
)

status = status_command()
if test_main_entry:
status = main
command_args = ["status"]
else:
status = status_command()
command_args = []

command_args += [(test_data_dir / "test_run").as_posix()] + extra_args

if "dne" in extra_args:
with pytest.warns(gapsWarning):
result = cli_runner.invoke(
status,
[(test_data_dir / "test_run").as_posix()] + extra_args,
)
expected_behavior = pytest.warns(gapsWarning)
else:
result = cli_runner.invoke(
status,
[(test_data_dir / "test_run").as_posix()] + extra_args,
)
expected_behavior = nullcontext()

with expected_behavior:
result = cli_runner.invoke(status, command_args)

lines = result.stdout.split("\n")
cols = [
"job_status",
Expand Down Expand Up @@ -299,6 +309,7 @@ def test_recursive_status(tmp_path, test_data_dir, cli_runner, monkeypatch):
assert any(line == "test_run:" for line in lines)
assert any(line == "test_failed_run:" for line in lines)
assert len(lines) > 20
assert not any(Status.HIDDEN_SUB_DIR in line for line in lines)


if __name__ == "__main__":
Expand Down

0 comments on commit b8a5b45

Please sign in to comment.