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
1 change: 1 addition & 0 deletions changelog/+gitrepo-shared-access.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the `shared` option to `GitRepo` in `infrahub_sdk.testing.repository`. With `shared=True`, every user on the machine can read and write the Git repository that `GitRepo` creates, so Infrahub containers that run as another user than the test process can fetch from it and push to it. The default, `False`, keeps the current behavior.
30 changes: 30 additions & 0 deletions infrahub_sdk/testing/repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import asyncio
import os
import shutil
from dataclasses import dataclass, field
from enum import Enum
Expand Down Expand Up @@ -43,6 +44,8 @@ class GitRepo:
directories_to_ignore: list[str] = field(default_factory=list)
remote_directory_name: str = "/remote"
_branches: list[str] = field(default_factory=list)
shared: bool = False
"""Give every user read and write access to the repository, for Infrahub containers that run as another user."""

@property
def repo(self) -> GitRepoManager:
Expand Down Expand Up @@ -75,6 +78,33 @@ def init(self) -> None:
porcelain.commit(repo=self._repo.git.path, message="First commit")
porcelain.checkout(repo=self._repo.git, target=self.initial_branch)

if self.shared:
self._share()

def _share(self) -> None:
"""Give every user read and write access to the repository, now and to the files Git adds later.

`core.sharedRepository` makes Git create the files of a later commit or push with the same access.
The files that exist already, including those copied from `src_directory`, get it here.
"""
config = self.repo.git.get_config()
config.set((b"core",), b"sharedRepository", b"0666")
config.write_to_path()

root_directory = self.dst_directory / self.name
root_directory.chmod(0o777)
for root, directories, files in os.walk(root_directory):
for directory in directories:
directory_path = Path(root) / directory
if not directory_path.is_symlink():
directory_path.chmod(0o777)
for file in files:
file_path = Path(root) / file
if file_path.is_symlink():
continue
mode = file_path.stat().st_mode
file_path.chmod(mode | 0o666 | (0o111 if mode & 0o111 else 0))

async def add_to_infrahub(self, client: InfrahubClient, branch: str | None = None) -> dict:
input_data = {
"data": {
Expand Down
41 changes: 41 additions & 0 deletions tests/unit/sdk/test_repository.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import stat
import tempfile
from collections.abc import Generator
from pathlib import Path
Expand Down Expand Up @@ -92,3 +93,43 @@ def test_gitrepo_init(temp_dir: str) -> None:
commit = repo._repo.git[repo._repo.git.head()]
assert isinstance(commit, Commit)
assert commit.message.decode("utf-8") == "First commit"


def _private_source(temp_dir: str) -> Path:
"""A source directory that only its owner can access, as pytest's `tmp_path` creates them."""
source = Path(temp_dir) / "source"
source.mkdir(mode=0o700)
(source / ".infrahub.yml").write_text("---\n", encoding="utf-8")
(source / "script.sh").write_text("#!/bin/sh\n", encoding="utf-8")
(source / "script.sh").chmod(0o700)
return source


def _permissions(path: Path) -> int:
return stat.S_IMODE(path.stat().st_mode)


def test_gitrepo_keeps_the_permissions_of_the_source_by_default(temp_dir: str) -> None:
destination = Path(temp_dir) / "remotes"
repo = GitRepo(name="private", src_directory=_private_source(temp_dir), dst_directory=destination)

assert _permissions(destination / "private") == 0o700
with pytest.raises(KeyError):
repo.repo.git.get_config().get((b"core",), b"sharedRepository")


def test_gitrepo_shared_gives_every_user_read_and_write_access(temp_dir: str) -> None:
destination = Path(temp_dir) / "remotes"
repo = GitRepo(name="shared", src_directory=_private_source(temp_dir), dst_directory=destination, shared=True)
root = destination / "shared"

assert repo.repo.git.get_config().get((b"core",), b"sharedRepository") == b"0666"
for path in [root, *root.rglob("*")]:
if path.is_symlink():
continue
if path.is_dir():
assert _permissions(path) == 0o777, path
else:
assert _permissions(path) & 0o666 == 0o666, path
assert _permissions(root / "script.sh") == 0o777
assert _permissions(root / ".infrahub.yml") == 0o666
Loading