Coverage for /usr/local/lib/python3.10/site-packages/opal_server-0.0.0-py3.10.egg/opal_server/policy/bundles/api.py: 48%
57 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 11:54 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 11:54 +0000
1import os
2from pathlib import Path
3from typing import List, Optional
5import fastapi.responses
6from fastapi import APIRouter, Depends, Header, HTTPException, Query, Response, status
7from git.repo import Repo
8from opal_common.confi.confi import load_conf_if_none
9from opal_common.git_utils.bundle_maker import BundleMaker
10from opal_common.git_utils.commit_viewer import CommitViewer
11from opal_common.git_utils.repo_cloner import RepoClonePathFinder
12from opal_common.logger import logger
13from opal_common.schemas.policy import PolicyBundle
14from opal_server.config import opal_server_config
15from starlette.responses import RedirectResponse
17router = APIRouter()
20async def get_repo(
21 base_clone_path: str = None,
22 clone_subdirectory_prefix: str = None,
23 use_fixed_path: bool = None,
24) -> Repo:
25 base_clone_path = load_conf_if_none(
26 base_clone_path, opal_server_config.POLICY_REPO_CLONE_PATH
27 )
28 clone_subdirectory_prefix = load_conf_if_none(
29 clone_subdirectory_prefix, opal_server_config.POLICY_REPO_CLONE_FOLDER_PREFIX
30 )
31 use_fixed_path = load_conf_if_none(
32 use_fixed_path, opal_server_config.POLICY_REPO_REUSE_CLONE_PATH
33 )
34 clone_path_finder = RepoClonePathFinder(
35 base_clone_path=base_clone_path,
36 clone_subdirectory_prefix=clone_subdirectory_prefix,
37 use_fixed_path=use_fixed_path,
38 )
39 repo_path = clone_path_finder.get_clone_path()
41 policy_repo_not_found_error = HTTPException(
42 status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
43 detail="policy repo was not found",
44 )
46 if not repo_path:
47 raise policy_repo_not_found_error
49 git_path = Path(os.path.join(repo_path, Path(".git")))
50 # TODO: at the moment opal server will 503 until it finishes cloning the policy repo
51 # we might fix this in the future by signaling to the client that the repo is ready
52 if not git_path.exists(): 52 ↛ 54line 52 didn't jump to line 54 because the condition on line 52 was always true
53 raise policy_repo_not_found_error
54 return Repo(repo_path)
57def normalize_path(path: str) -> Path:
58 return Path(path[1:]) if path.startswith("/") else Path(path)
61async def get_input_paths_or_throw(
62 repo: Repo = Depends(get_repo),
63 paths: Optional[List[str]] = Query(None, alias="path"),
64) -> List[Path]:
65 """Validates the :path query param, and return valid paths.
67 if an invalid path is provided, will throw 404.
68 """
69 paths = paths or []
70 paths = [normalize_path(p) for p in paths]
72 # if the repo is currently being cloned - the repo.heads is empty
73 if len(repo.heads) == 0:
74 raise HTTPException(
75 status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
76 detail="policy repo is not ready",
77 )
79 # verify all input paths exists under the commit hash
80 with CommitViewer(repo.head.commit) as viewer:
81 for path in paths:
82 if not viewer.exists(path):
83 raise HTTPException(
84 status_code=status.HTTP_404_NOT_FOUND,
85 detail=f"requested path {path} was not found in the policy repo!",
86 )
88 # the default of GET /policy (without path params) is to return all
89 # the (opa) files in the repo.
90 paths = paths or [Path(".")]
91 return paths
94@router.get("/policy", response_model=PolicyBundle)
95async def get_policy(
96 repo: Repo = Depends(get_repo),
97 input_paths: List[Path] = Depends(get_input_paths_or_throw),
98 base_hash: Optional[str] = Query(
99 None,
100 description="hash of previous bundle already downloaded, server will return a diff bundle.",
101 ),
102):
103 maker = BundleMaker(
104 repo,
105 in_directories=set(input_paths),
106 extensions=opal_server_config.FILTER_FILE_EXTENSIONS,
107 root_manifest_path=opal_server_config.POLICY_REPO_MANIFEST_PATH,
108 bundle_ignore=opal_server_config.BUNDLE_IGNORE,
109 )
110 # check if commit exist in the repo
111 revision = None
112 if base_hash:
113 try:
114 revision = repo.rev_parse(base_hash)
115 except ValueError:
116 logger.warning(f"base_hash {base_hash} not exist in the repo")
118 if revision is None:
119 return maker.make_bundle(repo.head.commit)
120 try:
121 old_commit = repo.commit(base_hash)
122 return maker.make_diff_bundle(old_commit, repo.head.commit)
123 except ValueError:
124 raise HTTPException(
125 status_code=status.HTTP_404_NOT_FOUND,
126 detail=f"commit with hash {base_hash} was not found in the policy repo!",
127 )