frisch 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. frisch-0.1.1/.dockerignore +14 -0
  2. frisch-0.1.1/.github/workflows/main.yml +12 -0
  3. frisch-0.1.1/.gitignore +18 -0
  4. frisch-0.1.1/.gitreview +5 -0
  5. frisch-0.1.1/.pre-commit-config.yaml +35 -0
  6. frisch-0.1.1/CLAUDE.md +59 -0
  7. frisch-0.1.1/Dockerfile +47 -0
  8. frisch-0.1.1/LICENSE +176 -0
  9. frisch-0.1.1/Makefile +19 -0
  10. frisch-0.1.1/PKG-INFO +99 -0
  11. frisch-0.1.1/README.md +75 -0
  12. frisch-0.1.1/alembic.ini +39 -0
  13. frisch-0.1.1/compose.yaml +60 -0
  14. frisch-0.1.1/etc/frisch.yaml.sample +105 -0
  15. frisch-0.1.1/frisch/__init__.py +6 -0
  16. frisch-0.1.1/frisch/cli/__init__.py +0 -0
  17. frisch-0.1.1/frisch/cli/commands.py +142 -0
  18. frisch-0.1.1/frisch/cli/main.py +26 -0
  19. frisch-0.1.1/frisch/collect.py +174 -0
  20. frisch-0.1.1/frisch/collectors/__init__.py +24 -0
  21. frisch-0.1.1/frisch/collectors/argocd.py +367 -0
  22. frisch-0.1.1/frisch/collectors/base.py +65 -0
  23. frisch-0.1.1/frisch/collectors/debs.py +176 -0
  24. frisch-0.1.1/frisch/collectors/gitrepo.py +187 -0
  25. frisch-0.1.1/frisch/collectors/puppet.py +227 -0
  26. frisch-0.1.1/frisch/config.py +245 -0
  27. frisch-0.1.1/frisch/db/__init__.py +0 -0
  28. frisch-0.1.1/frisch/db/models.py +123 -0
  29. frisch-0.1.1/frisch/db/queries.py +256 -0
  30. frisch-0.1.1/frisch/db/session.py +22 -0
  31. frisch-0.1.1/frisch/errors.py +18 -0
  32. frisch-0.1.1/frisch/ingest.py +232 -0
  33. frisch-0.1.1/frisch/migrations/env.py +38 -0
  34. frisch-0.1.1/frisch/migrations/script.py.mako +23 -0
  35. frisch-0.1.1/frisch/migrations/versions/0001_initial.py +96 -0
  36. frisch-0.1.1/frisch/model.py +113 -0
  37. frisch-0.1.1/frisch/web/__init__.py +0 -0
  38. frisch-0.1.1/frisch/web/api.py +67 -0
  39. frisch-0.1.1/frisch/web/app.py +155 -0
  40. frisch-0.1.1/frisch.egg-info/PKG-INFO +99 -0
  41. frisch-0.1.1/frisch.egg-info/SOURCES.txt +87 -0
  42. frisch-0.1.1/frisch.egg-info/dependency_links.txt +1 -0
  43. frisch-0.1.1/frisch.egg-info/entry_points.txt +11 -0
  44. frisch-0.1.1/frisch.egg-info/requires.txt +14 -0
  45. frisch-0.1.1/frisch.egg-info/scm_file_list.json +83 -0
  46. frisch-0.1.1/frisch.egg-info/scm_version.json +8 -0
  47. frisch-0.1.1/frisch.egg-info/top_level.txt +1 -0
  48. frisch-0.1.1/frontend/index.html +34 -0
  49. frisch-0.1.1/frontend/package-lock.json +1311 -0
  50. frisch-0.1.1/frontend/package.json +22 -0
  51. frisch-0.1.1/frontend/src/App.jsx +58 -0
  52. frisch-0.1.1/frontend/src/api.js +21 -0
  53. frisch-0.1.1/frontend/src/format.js +25 -0
  54. frisch-0.1.1/frontend/src/main.jsx +13 -0
  55. frisch-0.1.1/frontend/src/pages/Matrix.jsx +241 -0
  56. frisch-0.1.1/frontend/src/pages/Service.jsx +134 -0
  57. frisch-0.1.1/frontend/src/pages/Status.jsx +69 -0
  58. frisch-0.1.1/frontend/src/styles.css +290 -0
  59. frisch-0.1.1/frontend/src/theme.js +53 -0
  60. frisch-0.1.1/frontend/src/theme.test.js +35 -0
  61. frisch-0.1.1/frontend/src/useTheme.js +21 -0
  62. frisch-0.1.1/frontend/vite.config.js +19 -0
  63. frisch-0.1.1/pyproject.toml +68 -0
  64. frisch-0.1.1/pyrightconfig.json +6 -0
  65. frisch-0.1.1/releasenotes/config.yaml +4 -0
  66. frisch-0.1.1/releasenotes/notes/dark-mode-toggle-9c41d7e2a5b8f036.yaml +7 -0
  67. frisch-0.1.1/releasenotes/notes/initial-release-4b8f2a1c9d3e6f70.yaml +23 -0
  68. frisch-0.1.1/releasenotes/notes/matrix-instance-filter-7e3a91d4c2b6f580.yaml +9 -0
  69. frisch-0.1.1/releasenotes/notes/puppetdb-tls-relax-strict-7c2e91a4b5d0f3e8.yaml +14 -0
  70. frisch-0.1.1/renovate.json +9 -0
  71. frisch-0.1.1/setup.cfg +4 -0
  72. frisch-0.1.1/tests/conftest.py +124 -0
  73. frisch-0.1.1/tests/fixtures/argocd/event-exporter.yaml +21 -0
  74. frisch-0.1.1/tests/fixtures/argocd/k8s-infra.yaml +18 -0
  75. frisch-0.1.1/tests/fixtures/argocd/mariadb-operator.yaml +26 -0
  76. frisch-0.1.1/tests/fixtures/puppet/tier1.yaml +21 -0
  77. frisch-0.1.1/tests/test_api.py +92 -0
  78. frisch-0.1.1/tests/test_argocd.py +222 -0
  79. frisch-0.1.1/tests/test_cli.py +246 -0
  80. frisch-0.1.1/tests/test_collect.py +174 -0
  81. frisch-0.1.1/tests/test_config.py +216 -0
  82. frisch-0.1.1/tests/test_debs.py +127 -0
  83. frisch-0.1.1/tests/test_gitrepo.py +84 -0
  84. frisch-0.1.1/tests/test_ingest.py +234 -0
  85. frisch-0.1.1/tests/test_model.py +65 -0
  86. frisch-0.1.1/tests/test_puppet.py +206 -0
  87. frisch-0.1.1/tests/test_queries.py +255 -0
  88. frisch-0.1.1/tests/test_web_app.py +171 -0
  89. frisch-0.1.1/tox.ini +51 -0
@@ -0,0 +1,14 @@
1
+ .git
2
+ .tox
3
+ .venv
4
+ venv
5
+ __pycache__
6
+ *.egg-info
7
+ build
8
+ dist
9
+ cover
10
+ htmlcov
11
+ node_modules
12
+ frontend/dist
13
+ .dev
14
+ tests
@@ -0,0 +1,12 @@
1
+ name: 'Build Release'
2
+ on:
3
+ push:
4
+ tags:
5
+ - '*'
6
+
7
+ permissions:
8
+ contents: write
9
+
10
+ jobs:
11
+ release:
12
+ uses: NeCTAR-RC/gh-actions/.github/workflows/build-release.yaml@master
@@ -0,0 +1,18 @@
1
+ *.py[cod]
2
+ __pycache__/
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .tox/
8
+ .coverage
9
+ .coverage.*
10
+ htmlcov/
11
+ cover/
12
+ .pytest_cache/
13
+ .venv/
14
+ venv/
15
+ node_modules/
16
+
17
+ # Local dev state: sqlite db and git mirror cache produced by frisch-collect.
18
+ /.dev/
@@ -0,0 +1,5 @@
1
+ [gerrit]
2
+ host=review.rc.nectar.org.au
3
+ port=29418
4
+ project=NeCTAR-RC/frisch.git
5
+ defaultbranch=master
@@ -0,0 +1,35 @@
1
+ ---
2
+ default_language_version:
3
+ # force all unspecified python hooks to run python3
4
+ python: python3
5
+ repos:
6
+ - repo: https://github.com/pre-commit/pre-commit-hooks
7
+ rev: v6.0.0
8
+ hooks:
9
+ - id: trailing-whitespace
10
+ - id: mixed-line-ending
11
+ args: ['--fix', 'lf']
12
+ exclude: '.*\.(svg)$'
13
+ - id: fix-byte-order-marker
14
+ - id: check-executables-have-shebangs
15
+ - id: check-merge-conflict
16
+ - id: debug-statements
17
+ - id: check-yaml
18
+ files: .*\.(yaml|yml)$
19
+ - repo: https://github.com/astral-sh/ruff-pre-commit
20
+ rev: v0.16.4
21
+ hooks:
22
+ - id: ruff
23
+ args: ['--fix', '--unsafe-fixes']
24
+ - id: ruff-format
25
+ - repo: https://opendev.org/openstack/hacking
26
+ rev: 7.0.0
27
+ hooks:
28
+ - id: hacking
29
+ additional_dependencies:
30
+ - flake8-import-order~=0.19.2
31
+ exclude: '^(doc|releasenotes|tools)/.*$'
32
+ - repo: https://github.com/crate-ci/typos
33
+ rev: v1.49.0
34
+ hooks:
35
+ - id: typos
frisch-0.1.1/CLAUDE.md ADDED
@@ -0,0 +1,59 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this is
6
+
7
+ `frisch` tracks the deployed version of every Nectar service in **test** and
8
+ **prod** and when each environment changed to that version. Data comes from
9
+ three sources: ArgoCD apps repos (git), puppet site repos + shared hieradata
10
+ (git), and the PuppetDB package inventory (live). The full design rationale
11
+ lives in the plan at the repo's inception; the short form is in README.md.
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ tox # default envlist: py314 (pytest + coverage) and pep8
17
+ tox -e pep8 # lint only
18
+ .venv/bin/pytest tests/test_ingest.py::test_change_appends_event # single test
19
+ pip install -e '.[test]' # editable install (needed to refresh CLI entry points)
20
+ frisch db upgrade # alembic upgrade head against the configured DB
21
+ frisch-collect --backfill --config etc/dev.yaml # full history rebuild
22
+ ```
23
+
24
+ - `tox -e releasenotes` (reno) needs at least one git commit; not in the
25
+ default envlist.
26
+ - `tox -e frontend` builds the SPA (needs npm); Python tests never require it.
27
+
28
+ ## Architecture
29
+
30
+ The pipeline is `collectors -> Observation -> ingest -> DB -> queries -> API/SPA`.
31
+
32
+ - **`model.py`** holds the `Observation` contract and service-identity
33
+ resolution. Collectors are pure producers: they emit `Observation`s and
34
+ never touch the DB. **`ingest.py`** is the single writer; everything else
35
+ reads via `db/queries.py`.
36
+ - Versions are **opaque strings**: never parse, compare or order them
37
+ (kolla tags like `2024.1-eom-26-g...` are not semver). Ordering is always
38
+ by time.
39
+ - Git collectors share `collectors/gitrepo.py` (bare mirror clones, cursor
40
+ per repo in the `git_cursor` table, `log --first-parent -M -C
41
+ --name-status` walk, `git show` per blob). Idempotency: `version_event`
42
+ is unique on `(instance_id, ref)` where ref = commit sha, so re-walking
43
+ history (or a cursor reset after a force-push) is safe.
44
+ - The deb collector has no git history: `precision="interval"` events with
45
+ `interval_start`, and the UI must say "changed between X and Y", never a
46
+ fake exact time. A failed/unavailable source aborts that env's run and
47
+ keeps prior state -- `frisch-collect` exits non-zero so the CronJob goes
48
+ red rather than silently serving stale data.
49
+ - The puppet hiera files contain multi-KB `ENC[GPG,...]` blobs and the
50
+ renovate annotation is a YAML *comment*: the puppet collector line-scans,
51
+ it must NOT yaml.safe_load site data files.
52
+ - DB is MariaDB in deployments, SQLite in dev/tests (same SQLAlchemy URL
53
+ mechanism) -- keep column types portable and timestamps naive UTC.
54
+
55
+ ## Conventions
56
+
57
+ Gerrit review (no branches, no PRs), conventional commits with `-s`, reno
58
+ release notes, ruff line-length 79, tox for tests. The reference sibling
59
+ project for patterns is `nectar-conformance`.
@@ -0,0 +1,47 @@
1
+ # syntax=docker/dockerfile:1
2
+ # Single image for the web Deployment (frisch-web), the collector CronJob
3
+ # (frisch-collect) and the admin CLI (frisch) -- same image, different command.
4
+
5
+ # --- Stage 1: build the SPA -------------------------------------------------
6
+ FROM node:26-alpine AS frontend
7
+ WORKDIR /build
8
+ COPY frontend/package.json frontend/package-lock.json* ./
9
+ RUN npm install
10
+ COPY frontend/ ./
11
+ RUN npm run build
12
+
13
+ # --- Stage 2: python runtime ------------------------------------------------
14
+ FROM python:3.14-slim AS runtime
15
+ ENV PYTHONUNBUFFERED=1 \
16
+ PIP_NO_CACHE_DIR=1 \
17
+ FRISCH_WEB_STATIC=/app/web-static \
18
+ FRISCH_GIT_CACHE_DIR=/var/cache/frisch/git
19
+
20
+ # The git collectors shell out to git against bare mirror clones.
21
+ RUN apt-get update \
22
+ && apt-get install -y --no-install-recommends git openssh-client \
23
+ && rm -rf /var/lib/apt/lists/*
24
+
25
+ WORKDIR /app
26
+
27
+ # Install the package. setuptools-scm derives the version from git, but .git is
28
+ # not in the build context, so the Makefile passes the git-derived version in and
29
+ # we hand it to setuptools-scm. ARG (not ENV) keeps it out of the final image's
30
+ # environment.
31
+ ARG VERSION
32
+ COPY pyproject.toml README.md ./
33
+ COPY frisch/ ./frisch/
34
+ RUN SETUPTOOLS_SCM_PRETEND_VERSION_FOR_FRISCH="$VERSION" pip install .
35
+
36
+ # Drop in the built SPA; the web app picks it up via FRISCH_WEB_STATIC.
37
+ COPY --from=frontend /build/dist/ /app/web-static/
38
+
39
+ # Run unprivileged. The git cache dir is the PVC mount point in k8s; created here
40
+ # so local runs work too.
41
+ RUN useradd --system --uid 42420 app \
42
+ && mkdir -p /var/cache/frisch/git /var/lib/frisch \
43
+ && chown -R app:app /var/cache/frisch /var/lib/frisch
44
+ USER app
45
+
46
+ EXPOSE 8080
47
+ CMD ["frisch-web", "--host", "0.0.0.0", "--port", "8080"]
frisch-0.1.1/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
frisch-0.1.1/Makefile ADDED
@@ -0,0 +1,19 @@
1
+ PROJECT=frisch
2
+ REPO=registry.rc.nectar.org.au/nectar
3
+ DESCRIBE=$(shell git describe --tags)
4
+ # PEP 440-safe package version for setuptools-scm. Docker tags can't contain '+',
5
+ # so the image tag keeps the raw `git describe` form while the package version
6
+ # rewrites `<tag>-<n>-g<sha>` into the local-version form `<tag>+<n>.g<sha>`.
7
+ VERSION := $(shell git describe --tags | sed -e 's/-/+/' -e 's/-/./g')
8
+ IMAGE_TAG := $(if $(TAG),$(TAG),$(DESCRIBE))
9
+ IMAGE=$(REPO)/$(PROJECT):$(IMAGE_TAG)
10
+ BUILDER=docker
11
+ BUILDER_ARGS=
12
+ build:
13
+ echo "Derived image tag: $(DESCRIBE)"
14
+ echo "Actual image tag: $(IMAGE_TAG)"
15
+ echo "Package version: $(VERSION)"
16
+ $(BUILDER) build $(BUILDER_ARGS) --build-arg VERSION=$(VERSION) -t $(IMAGE) .
17
+ push:
18
+ $(BUILDER) push $(IMAGE)
19
+ .PHONY: build push
frisch-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.4
2
+ Name: frisch
3
+ Version: 0.1.1
4
+ Summary: Deployment version visibility dashboard for the Nectar Research Cloud
5
+ Author: ARDC Nectar Core Services
6
+ License: Apache-2.0
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: httpx>=0.24
11
+ Requires-Dist: PyYAML>=6
12
+ Requires-Dist: SQLAlchemy>=2
13
+ Requires-Dist: alembic>=1.13
14
+ Requires-Dist: PyMySQL>=1.1
15
+ Requires-Dist: cliff>=4
16
+ Requires-Dist: fastapi>=0.110
17
+ Requires-Dist: uvicorn[standard]>=0.27
18
+ Requires-Dist: prometheus-client>=0.19
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest>=7; extra == "test"
21
+ Requires-Dist: pytest-cov>=4; extra == "test"
22
+ Requires-Dist: respx>=0.20; extra == "test"
23
+ Dynamic: license-file
24
+
25
+ # frisch
26
+
27
+ Deployment version visibility for the Nectar Research Cloud.
28
+
29
+ Nectar services are deployed three ways: ArgoCD applications on kubernetes,
30
+ docker containers on puppet-managed VMs, and debian packages on VMs and bare
31
+ metal, each in a test and a prod environment (test is upgraded first). frisch
32
+ collects the deployed version of every service in both environments, records
33
+ when each environment changed to that version, and serves a dashboard that
34
+ shows the version matrix and per-service history, making promotion lag from
35
+ test to prod visible at a glance.
36
+
37
+ ## How it works
38
+
39
+ ```
40
+ collectors -> Observation -> ingest (single writer) -> MariaDB -> API -> SPA
41
+ ```
42
+
43
+ Three collectors, one normalised record:
44
+
45
+ - **argocd** (git-declared): walks the history of the per-environment
46
+ ArgoCD apps repos, parsing each Application CRD's
47
+ `spec.source.targetRevision` (and `spec.sources` for multi-source apps).
48
+ Change dates are exact git commit times; the full history is backfillable.
49
+ - **puppet** (git-declared): walks the puppet site repos (one per
50
+ environment) and the shared `hieradata` repo, scanning hiera
51
+ `*::image_tag` keys and their
52
+ `# renovate:` annotations. Also exact, backfillable.
53
+ - **deb** (live): queries the PuppetDB package inventory in each environment
54
+ for configured package names/patterns. Package versions are not in git, so
55
+ frisch's own records are the only durable history; change times are
56
+ intervals ("changed between X and Y"), bounded by the collection interval.
57
+
58
+ Versions are opaque strings everywhere -- they are never compared or ordered,
59
+ only equality-checked and timestamped.
60
+
61
+ ## Running
62
+
63
+ ```
64
+ pip install -e '.[test]'
65
+ frisch db upgrade # create/upgrade the schema
66
+ frisch-collect --backfill # first run: full git history
67
+ frisch-collect # incremental (the CronJob entry point)
68
+ frisch-web # serve the dashboard on :8080
69
+ frisch show matrix # the same table, in a terminal
70
+ ```
71
+
72
+ Configuration is layered: `FRISCH_*` environment variables over a YAML config
73
+ file (`--config`, `FRISCH_CONFIG`, `~/.config/frisch/frisch.yaml` or
74
+ `/etc/frisch/frisch.yaml`) over defaults. See `etc/frisch.yaml.sample`.
75
+ For local development, point the repo urls at your existing checkouts and use
76
+ the default SQLite database.
77
+
78
+ The SPA lives in `frontend/` (vite + React); `npm run dev` proxies `/api` to a
79
+ locally running `frisch-web`. In the container image the built bundle is
80
+ served by the backend.
81
+
82
+ ## Deployment prerequisites
83
+
84
+ - A database and user on the MariaDB cluster in each environment.
85
+ - A read-only deploy key for the five source repos.
86
+ - PuppetDB package inventory enabled (`package_inventory_enabled` on agents)
87
+ and network/auth access to the PuppetDB endpoints; until then the deb
88
+ source reports itself unavailable on the status page.
89
+
90
+ ## Development
91
+
92
+ ```
93
+ tox # unit tests and lint
94
+ tox -e pep8 # lint only
95
+ tox -e frontend # build the SPA (needs npm)
96
+ ```
97
+
98
+ Releases use [reno](https://docs.openstack.org/reno/) for release notes.
99
+ Contributions go through gerrit; use conventional commits and `git commit -s`.
frisch-0.1.1/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # frisch
2
+
3
+ Deployment version visibility for the Nectar Research Cloud.
4
+
5
+ Nectar services are deployed three ways: ArgoCD applications on kubernetes,
6
+ docker containers on puppet-managed VMs, and debian packages on VMs and bare
7
+ metal, each in a test and a prod environment (test is upgraded first). frisch
8
+ collects the deployed version of every service in both environments, records
9
+ when each environment changed to that version, and serves a dashboard that
10
+ shows the version matrix and per-service history, making promotion lag from
11
+ test to prod visible at a glance.
12
+
13
+ ## How it works
14
+
15
+ ```
16
+ collectors -> Observation -> ingest (single writer) -> MariaDB -> API -> SPA
17
+ ```
18
+
19
+ Three collectors, one normalised record:
20
+
21
+ - **argocd** (git-declared): walks the history of the per-environment
22
+ ArgoCD apps repos, parsing each Application CRD's
23
+ `spec.source.targetRevision` (and `spec.sources` for multi-source apps).
24
+ Change dates are exact git commit times; the full history is backfillable.
25
+ - **puppet** (git-declared): walks the puppet site repos (one per
26
+ environment) and the shared `hieradata` repo, scanning hiera
27
+ `*::image_tag` keys and their
28
+ `# renovate:` annotations. Also exact, backfillable.
29
+ - **deb** (live): queries the PuppetDB package inventory in each environment
30
+ for configured package names/patterns. Package versions are not in git, so
31
+ frisch's own records are the only durable history; change times are
32
+ intervals ("changed between X and Y"), bounded by the collection interval.
33
+
34
+ Versions are opaque strings everywhere -- they are never compared or ordered,
35
+ only equality-checked and timestamped.
36
+
37
+ ## Running
38
+
39
+ ```
40
+ pip install -e '.[test]'
41
+ frisch db upgrade # create/upgrade the schema
42
+ frisch-collect --backfill # first run: full git history
43
+ frisch-collect # incremental (the CronJob entry point)
44
+ frisch-web # serve the dashboard on :8080
45
+ frisch show matrix # the same table, in a terminal
46
+ ```
47
+
48
+ Configuration is layered: `FRISCH_*` environment variables over a YAML config
49
+ file (`--config`, `FRISCH_CONFIG`, `~/.config/frisch/frisch.yaml` or
50
+ `/etc/frisch/frisch.yaml`) over defaults. See `etc/frisch.yaml.sample`.
51
+ For local development, point the repo urls at your existing checkouts and use
52
+ the default SQLite database.
53
+
54
+ The SPA lives in `frontend/` (vite + React); `npm run dev` proxies `/api` to a
55
+ locally running `frisch-web`. In the container image the built bundle is
56
+ served by the backend.
57
+
58
+ ## Deployment prerequisites
59
+
60
+ - A database and user on the MariaDB cluster in each environment.
61
+ - A read-only deploy key for the five source repos.
62
+ - PuppetDB package inventory enabled (`package_inventory_enabled` on agents)
63
+ and network/auth access to the PuppetDB endpoints; until then the deb
64
+ source reports itself unavailable on the status page.
65
+
66
+ ## Development
67
+
68
+ ```
69
+ tox # unit tests and lint
70
+ tox -e pep8 # lint only
71
+ tox -e frontend # build the SPA (needs npm)
72
+ ```
73
+
74
+ Releases use [reno](https://docs.openstack.org/reno/) for release notes.
75
+ Contributions go through gerrit; use conventional commits and `git commit -s`.
@@ -0,0 +1,39 @@
1
+ # Dev convenience for running `alembic` directly; `frisch db upgrade` builds
2
+ # this configuration programmatically and takes the URL from frisch config.
3
+ [alembic]
4
+ script_location = frisch/migrations
5
+ sqlalchemy.url = sqlite:///.dev/frisch.db
6
+
7
+ [loggers]
8
+ keys = root,sqlalchemy,alembic
9
+
10
+ [handlers]
11
+ keys = console
12
+
13
+ [formatters]
14
+ keys = generic
15
+
16
+ [logger_root]
17
+ level = WARNING
18
+ handlers = console
19
+ qualname =
20
+
21
+ [logger_sqlalchemy]
22
+ level = WARNING
23
+ handlers =
24
+ qualname = sqlalchemy.engine
25
+
26
+ [logger_alembic]
27
+ level = INFO
28
+ handlers =
29
+ qualname = alembic
30
+
31
+ [handler_console]
32
+ class = StreamHandler
33
+ args = (sys.stderr,)
34
+ level = NOTSET
35
+ formatter = generic
36
+
37
+ [formatter_generic]
38
+ format = %(levelname)-5.5s [%(name)s] %(message)s
39
+ datefmt = %H:%M:%S
@@ -0,0 +1,60 @@
1
+ # Local dev / demo: MariaDB + web + a looping collector -- the docker-compose
2
+ # analogue of the k8s web Deployment + collector CronJob + MariaDB cluster.
3
+ #
4
+ # docker compose up --build
5
+ # open http://localhost:8080
6
+ #
7
+ # The collector reads the repos configured in etc/frisch.yaml.sample; for a
8
+ # local demo, bind-mount your checkouts and point a config copy at them, e.g.
9
+ # uncomment the volumes below and set the repo urls to /repos/<name>.
10
+ services:
11
+ db:
12
+ image: mariadb:10.11
13
+ environment:
14
+ MARIADB_DATABASE: frisch
15
+ MARIADB_USER: frisch
16
+ MARIADB_PASSWORD: frisch
17
+ MARIADB_RANDOM_ROOT_PASSWORD: '1'
18
+ healthcheck:
19
+ test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
20
+ interval: 5s
21
+ timeout: 5s
22
+ retries: 12
23
+
24
+ web:
25
+ build:
26
+ context: .
27
+ args:
28
+ VERSION: '${VERSION:-0.0.0}'
29
+ command: ['frisch-web', '--host', '0.0.0.0', '--port', '8080']
30
+ environment: &env
31
+ FRISCH_CONFIG: /etc/frisch/frisch.yaml
32
+ FRISCH_DATABASE_URL: mysql+pymysql://frisch:frisch@db/frisch?charset=utf8mb4
33
+ volumes:
34
+ - ./etc/frisch.yaml.sample:/etc/frisch/frisch.yaml:ro
35
+ # - ~/src/argo-apps-test:/repos/argo-apps-test:ro
36
+ # - ~/src/argo-apps-prod:/repos/argo-apps-prod:ro
37
+ ports:
38
+ - '8080:8080'
39
+ depends_on:
40
+ db:
41
+ condition: service_healthy
42
+
43
+ collector:
44
+ build:
45
+ context: .
46
+ args:
47
+ VERSION: '${VERSION:-0.0.0}'
48
+ command: ['frisch-collect', '--interval', '300']
49
+ environment: *env
50
+ volumes:
51
+ - ./etc/frisch.yaml.sample:/etc/frisch/frisch.yaml:ro
52
+ - git-cache:/var/cache/frisch/git
53
+ # - ~/src/argo-apps-test:/repos/argo-apps-test:ro
54
+ # - ~/src/argo-apps-prod:/repos/argo-apps-prod:ro
55
+ depends_on:
56
+ db:
57
+ condition: service_healthy
58
+
59
+ volumes:
60
+ git-cache: