Skip to content

Commit b968b28

Browse files
authored
add endpoints to render certificate scopes (#756)
thanks @cah-patrickthiem for review suggestions Signed-off-by: Matthias Büchse <[email protected]>
1 parent a76464a commit b968b28

File tree

6 files changed

+88
-17
lines changed

6 files changed

+88
-17
lines changed

compliance-monitor/README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,24 @@ Needs to be authenticated (via basic auth).
209209

210210
Supports content type `text/plain; version=0.0.4; charset=utf-8` only.
211211

212-
### GET /pages
212+
### GET /{view_type}/table\[_full\]
213213

214-
Returns the compliance table for all active subjects (type `text/html`).
214+
Returns the compliance table for all active subjects, where `view_type` can be one of the following:
215215

216-
Query parameters:
216+
- `markdown`: return Markdown fragment (mimetype `text/markdown`)
217+
- `fragment`: return HTML fragment (mimetype `text/html`)
218+
- `page`: return full HTML page (mimetype `text/html`)
219+
220+
If `table_full` is used, then HTTP basic auth must be performed, and the table will show the
221+
privileged view (i.e., any FAIL will be reported regardless of manual approval).
222+
223+
### GET /{view_type}/details\[_full\]/{subject}/{scopeuuid}
224+
225+
Returns compliance details for given subject and scope.
226+
227+
### GET /{view_type}/scope/{scopeuuid}
217228

218-
- `fragment_only` (optional `0` or `1`, default `1`): return just the table (otherwise a complete HTML doc)
229+
Returns spec overview for the given scope.
219230

220231
### GET /subjects
221232

compliance-monitor/monitor.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,12 @@ class ViewType(Enum):
9696
ViewType.fragment: 'overview.md',
9797
ViewType.page: 'overview.html',
9898
}
99-
REQUIRED_TEMPLATES = tuple(set(fn for view in (VIEW_DETAIL, VIEW_TABLE) for fn in view.values()))
99+
VIEW_SCOPE = {
100+
ViewType.markdown: 'scope.md',
101+
ViewType.fragment: 'scope.md',
102+
ViewType.page: 'overview.html',
103+
}
104+
REQUIRED_TEMPLATES = tuple(set(fn for view in (VIEW_DETAIL, VIEW_TABLE, VIEW_SCOPE) for fn in view.values()))
100105

101106

102107
# do I hate these globals, but I don't see another way with these frameworks
@@ -540,14 +545,15 @@ async def get_status(
540545
return convert_result_rows_to_dict2(rows2, get_scopes(), include_report=True)
541546

542547

543-
def render_view(view, view_type, results, base_url='/', title=None):
548+
def render_view(view, view_type, base_url='/', title=None, **kwargs):
544549
media_type = {ViewType.markdown: 'text/markdown'}.get(view_type, 'text/html')
545550
stage1 = stage2 = view[view_type]
546551
if view_type is ViewType.page:
547552
stage1 = view[ViewType.fragment]
553+
def scope_url(uuid): return f"{base_url}page/scope/{uuid}" # noqa: E306,E704
548554
def detail_url(subject, scope): return f"{base_url}page/detail/{subject}/{scope}" # noqa: E306,E704
549555
def report_url(report): return f"{base_url}reports/{report}" # noqa: E306,E704
550-
fragment = templates_map[stage1].render(results=results, detail_url=detail_url, report_url=report_url)
556+
fragment = templates_map[stage1].render(detail_url=detail_url, report_url=report_url, scope_url=scope_url, **kwargs)
551557
if view_type != ViewType.markdown and stage1.endswith('.md'):
552558
fragment = markdown(fragment, extensions=['extra'])
553559
if stage1 != stage2:
@@ -569,7 +575,7 @@ async def get_detail(
569575
rows2, get_scopes(), grace_period_days=GRACE_PERIOD_DAYS,
570576
subjects=(subject, ), scopes=(scopeuuid, ),
571577
)
572-
return render_view(VIEW_DETAIL, view_type, results2, base_url=settings.base_url, title=f'{subject} compliance')
578+
return render_view(VIEW_DETAIL, view_type, results=results2, base_url=settings.base_url, title=f'{subject} compliance')
573579

574580

575581
@app.get("/{view_type}/detail_full/{subject}/{scopeuuid}")
@@ -587,7 +593,7 @@ async def get_detail_full(
587593
results2 = convert_result_rows_to_dict2(
588594
rows2, get_scopes(), include_report=True, subjects=(subject, ), scopes=(scopeuuid, ),
589595
)
590-
return render_view(VIEW_DETAIL, view_type, results2, base_url=settings.base_url, title=f'{subject} compliance')
596+
return render_view(VIEW_DETAIL, view_type, results=results2, base_url=settings.base_url, title=f'{subject} compliance')
591597

592598

593599
@app.get("/{view_type}/table")
@@ -599,7 +605,7 @@ async def get_table(
599605
with conn.cursor() as cur:
600606
rows2 = db_get_relevant_results2(cur, approved_only=True)
601607
results2 = convert_result_rows_to_dict2(rows2, get_scopes(), grace_period_days=GRACE_PERIOD_DAYS)
602-
return render_view(VIEW_TABLE, view_type, results2, base_url=settings.base_url, title="SCS compliance overview")
608+
return render_view(VIEW_TABLE, view_type, results=results2, base_url=settings.base_url, title="SCS compliance overview")
603609

604610

605611
@app.get("/{view_type}/table_full")
@@ -613,7 +619,29 @@ async def get_table_full(
613619
with conn.cursor() as cur:
614620
rows2 = db_get_relevant_results2(cur, approved_only=False)
615621
results2 = convert_result_rows_to_dict2(rows2, get_scopes())
616-
return render_view(VIEW_TABLE, view_type, results2, base_url=settings.base_url, title="SCS compliance overview")
622+
return render_view(VIEW_TABLE, view_type, results=results2, base_url=settings.base_url, title="SCS compliance overview")
623+
624+
625+
@app.get("/{view_type}/scope/{scopeuuid}")
626+
async def get_scope(
627+
request: Request,
628+
conn: Annotated[connection, Depends(get_conn)],
629+
view_type: ViewType,
630+
scopeuuid: str,
631+
):
632+
spec = get_scopes()[scopeuuid].spec
633+
versions = spec['versions']
634+
relevant = sorted([name for name, version in versions.items() if version['_explicit_validity']])
635+
modules_chart = {}
636+
for name in relevant:
637+
for include in versions[name]['include']:
638+
module_id = include['module']['id']
639+
row = modules_chart.get(module_id)
640+
if row is None:
641+
row = modules_chart[module_id] = {'module': include['module'], 'columns': {}}
642+
row['columns'][name] = include
643+
rows = sorted(list(modules_chart.values()), key=lambda row: row['module']['id'])
644+
return render_view(VIEW_SCOPE, view_type, spec=spec, relevant=relevant, rows=rows, base_url=settings.base_url, title=spec['name'])
617645

618646

619647
@app.get("/pages")

compliance-monitor/templates/details.md.j2

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
{% for subject, subject_result in results.items() -%}
2-
# {{ subject }}
2+
{# omit h1 title here because we can only have one of those,
3+
and the html wrapper template will add one anyway -#}
34
{% for scopeuuid, scope_result in subject_result.items() -%}
4-
{% if not scope_result.relevant -%}
55
## {{ scope_result.name }}
66

7+
- [spec overview]({{ scope_url(scopeuuid) }})
8+
9+
{% if not scope_result.relevant -%}
10+
711
No recent test results available.
812

913
{% endif -%}
1014
{% for version in scope_result.relevant -%}
1115
{%- set version_result = scope_result.versions[version] -%}
12-
## {{ scope_result.name }} {{ version }} ({{ version_result.validity }}): {{ version_result.result | verdict }}
16+
### {{ version }} ({{ version_result.validity }}): {{ version_result.result | verdict }}
1317
{% for target, target_result in version_result.targets.items() -%}
14-
### Target {{ target }}: {{ target_result.result | verdict }}
18+
#### Target {{ target }}: {{ target_result.result | verdict }}
1519

1620
| testcase id | result | description |
1721
|---|---|---|

compliance-monitor/templates/overview.html.j2

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@
1010
<meta charset="utf-8">
1111
<title>{{ title or 'SCS compliance overview' }}</title>
1212
</head>
13-
<body>{{fragment}}</body>
13+
<body>
14+
{% if title %}<h1>{{title}}</h1>
15+
{% endif %}{{fragment}}</body>
1416
</html>

compliance-monitor/templates/overview.md.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ we could of course iterate over results etc., but hardcode the table (except the
33
for the time being to have the highest degree of control
44
-#}
55
{% set iaas = '50393e6f-2ae1-4c5c-a62c-3b75f2abef3f' -%}
6-
| Name | Description | Operator | SCS-compatible IaaS | HealthMon |
6+
| Name | Description | Operator | [SCS-compatible IaaS]({{ scope_url(iaas) }}) | HealthMon |
77
|-------|--------------|-----------|----------------------|:----------:|
88
| [gx-scs](https://github.com/SovereignCloudStack/docs/blob/main/community/cloud-resources/plusserver-gx-scs.md) | Dev environment provided for SCS & GAIA-X context | plusserver GmbH |
99
{#- #} [{{ results | passed('gx-scs', iaas) or '–' }}]({{ detail_url('gx-scs', iaas) }}) {# -#}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
| Scope versions -> | {% for name in relevant %} {{name}} |{% endfor %}
2+
| :----------------- | {% for name in relevant %} :-- |{% endfor %}
3+
| State | {% for name in relevant %} {{spec.versions[name].validity | capitalize}} |{% endfor %}
4+
| Stabilized at | {% for name in relevant %} {{spec.versions[name].stabilized_at}} |{% endfor %}
5+
| **Modules** | {% for name in relevant %} |{% endfor %}
6+
{% for row in rows -%}
7+
| [{% if row.module.id.startswith('scs-') %}{{row.module.id}}: {% endif %}{{row.module.name}}]({{row.module.url}}) |{%
8+
for name in relevant
9+
%} {% set column = row.columns[name] %}{%
10+
if column
11+
%}X{%
12+
if column.parameters
13+
%} ({%
14+
for key, value in column.parameters.items()
15+
%}{%
16+
if value.startswith("https://")
17+
%}[{{key}}]({{value}}){%
18+
else
19+
%}{{key}}={{value}}{%
20+
endif %}{{ ", " if not loop.last }}){%
21+
endfor %}{%
22+
endif %}{%
23+
endif %} |{%
24+
endfor
25+
%}
26+
{% endfor %}

0 commit comments

Comments
 (0)