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
6 changes: 3 additions & 3 deletions .github/workflows/ci-cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
version: "0.11.7"
enable-cache: true

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version-file: "pyproject.toml"

Expand Down
2 changes: 1 addition & 1 deletion ansible-runner/project
11 changes: 6 additions & 5 deletions modules/coactd.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ class RepoRegistration(Registration):
'RepoUpdateFeature'
]

# facilities whose repos get a POSIX group minted in Grouper at creation time
GROUPER_FACILITIES = ('cryoem', 'lsstsci')

REPO_USERS_GQL = gql("""
query getRepoUsers ( $repo: RepoInput! ) {
repo( filter: $repo ) {
Expand Down Expand Up @@ -532,17 +535,15 @@ def do_new_repo(
if repo_allocation_end_delta is None:
repo_allocation_end_delta = pdl.duration(years=5)

# For CryoEM repos (ct* / ce*), create a POSIX group via Grouper
# for qualifying facilities, create a POSIX group via Grouper
repo_gid = None
grouper_name = ""
uses_grouper = (
facility.lower() == 'cryoem'
)
uses_grouper = facility.lower() in self.GROUPER_FACILITIES
if uses_grouper:
grouper_name = f"sdf-{facility.lower()}-{repo.lower()}"
try:
if not self.grouper_password_file:
raise ValueError("Grouper password file must be provided for CryoEM ct/ce repos")
raise ValueError(f"Grouper password file must be provided for {facility} repos")
grouper_kwargs = dict(
grouper_name=grouper_name,
state="present",
Expand Down
40 changes: 40 additions & 0 deletions tests/test_repo_registration_gid.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,46 @@ def test_all_cryoem_repos_use_grouper(self, repo_registration, mock_ansible_runn
# Should create both slurm and posixgroup features (4 back_channel calls: user query + repoUpsert + 2 features)
assert repo_registration.back_channel.execute.call_count == 4

def test_lsstsci_repos_use_grouper(self, repo_registration, mock_ansible_runner):
"""Test that lsstsci repos use grouper and pass the gid through to add_repo.yaml."""
# Setup
repo_registration.run_playbook.return_value = mock_ansible_runner
repo_registration.extract_grouper_values = Mock(return_value=('67890', 'sdf-lsstsci-dp1'))
repo_registration.back_channel.execute.side_effect = [
{'repo': None}, # Query returns null (repo not found)
{'repoUpsert': {'Id': 'repo-789'}},
{'repoUpsertFeature': {'Id': 'feature-slurm'}},
{'repoUpsertFeature': {'Id': 'feature-posix'}}
]

# Execute
result = repo_registration.do_new_repo(
repo='dp1',
facility='lsstsci',
principal='test-user'
)

# Verify
assert result is True
repo_registration.run_playbook.assert_any_call(
'coact/grouper.yml',
grouper_name='sdf-lsstsci-dp1',
state='present',
grouper_description='POSIX group for lsstsci dp1 repository access',
grouper_password_file='/tmp/test-grouper-password',
)
# the gid drives the repo directory ownership in ansible-role-s3df-repo
repo_registration.run_playbook.assert_any_call(
'coact/add_repo.yaml',
facility='lsstsci',
repo='dp1',
repo_principal='test-user',
repo_users=['test-user'],
gidNumber='67890',
groupName='sdf-lsstsci-dp1'
)
assert repo_registration.back_channel.execute.call_count == 4


class TestRepoIdempotency:
"""Test idempotency fixes for NewRepo request workflow."""
Expand Down
9 changes: 9 additions & 0 deletions tests/test_s3df_posixgroup_ldif.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import os
from unittest.mock import Mock

import pytest

# Mock the ansible modules imported by s3df_posixgroup
sys.modules['ansible.module_utils.basic'] = Mock()
sys.modules['ansible.module_utils.common.text.converters'] = Mock()
Expand All @@ -17,6 +19,13 @@
if LIBRARY_PATH not in sys.path:
sys.path.insert(0, LIBRARY_PATH)

# sdf-ansible not available in CI
if not os.path.isfile(os.path.join(LIBRARY_PATH, 's3df_posixgroup.py')):
pytest.skip(
"sdf-ansible submodule not checked out; run `git submodule update --init`",
allow_module_level=True,
)

from s3df_posixgroup import _change_membership_template # noqa: E402

DN = "cn=sdf-cryoem-cd10,ou=Group,dc=sdf,dc=slac,dc=stanford,dc=edu"
Expand Down
16 changes: 8 additions & 8 deletions tests/test_slurm_node_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,35 @@ def setup_method(self):

def test_parse_slurm_nodelist_single(self):
"""Test parsing a single node name."""
result = self.importer.parse_slurm_nodelist("sdfmilan0271")
assert result == ["sdfmilan0271"]
result = self.importer.parse_slurm_nodelist("sdfmilan271")
assert result == ["sdfmilan271"]

def test_parse_slurm_nodelist_range(self):
"""Test parsing a SLURM node range."""
result = self.importer.parse_slurm_nodelist("sdfmilan[269-272]")
expected = ["sdfmilan0269", "sdfmilan0270", "sdfmilan0271", "sdfmilan0272"]
expected = ["sdfmilan269", "sdfmilan270", "sdfmilan271", "sdfmilan272"]
assert result == expected

def test_parse_slurm_nodelist_list(self):
"""Test parsing a comma-separated list of nodes."""
result = self.importer.parse_slurm_nodelist("sdfmilan[006,011,027]")
expected = ["sdfmilan0006", "sdfmilan0011", "sdfmilan0027"]
expected = ["sdfmilan006", "sdfmilan011", "sdfmilan027"]
assert result == expected

def test_parse_slurm_nodelist_mixed(self):
"""Test parsing a mixed range and list."""
result = self.importer.parse_slurm_nodelist("sdfmilan[001-003,010,020-022]")
expected = [
"sdfmilan0001", "sdfmilan0002", "sdfmilan0003",
"sdfmilan0010",
"sdfmilan0020", "sdfmilan0021", "sdfmilan0022"
"sdfmilan001", "sdfmilan002", "sdfmilan003",
"sdfmilan010",
"sdfmilan020", "sdfmilan021", "sdfmilan022"
]
assert result == expected

def test_parse_slurm_nodelist_different_prefix(self):
"""Test parsing with different node prefix."""
result = self.importer.parse_slurm_nodelist("sdfrome[001-003]")
expected = ["sdfrome0001", "sdfrome0002", "sdfrome0003"]
expected = ["sdfrome001", "sdfrome002", "sdfrome003"]
assert result == expected

def test_parse_slurm_nodelist_unparseable(self):
Expand Down