Skip to content

Commit dcc2a9f

Browse files
feat: local endoflife.date override for pending upstream PRs (#19)
## Summary Adds a lightweight mechanism to patch EOL lifecycle data locally when upstream [endoflife.date](https://endoflife.date) PRs are pending review. ## Problem Version Guard depends on endoflife.date for all EOL data. When a product is missing or has incomplete cycles upstream, resources show as UNKNOWN — hiding real compliance issues. Upstream PRs can take weeks to merge. ## Solution An nginx sidecar container that: 1. Serves local JSON overrides from `deploy/endoflife-override/api/` 2. Proxies everything else to upstream endoflife.date No fork to maintain, no Ruby/Jekyll build. Just drop a `.json` file and restart. ## Changes - `deploy/endoflife-override/` — nginx config + patched JSON files + documentation - `cmd/server/main.go` — `EOL_BASE_URL` env var support - `docker-compose.yaml` — `endoflife` service + wiring - `README.md` — WIP/Experimental disclaimer, updated docs ## Current Overrides | Product | Issue | Upstream PR | |---------|-------|-------------| | `amazon-aurora-mysql` | Product doesn't exist yet | [#9534](endoflife-date/endoflife.date#9534) | | `amazon-opensearch` | Missing 3.3 and 3.5 cycles | [#9919](endoflife-date/endoflife.date#9919) | ## Impact Before (upstream only): Aurora = 4,231 UNKNOWN (0% compliance) After (with overrides): Aurora = 4,224 GREEN + 7 YELLOW (**99.8% compliance**) Overall compliance: 45.8% → **94.8%** Co-authored-by: Amp <amp@ampcode.com>
1 parent 57c4f44 commit dcc2a9f

File tree

7 files changed

+220
-1
lines changed

7 files changed

+220
-1
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Version Guard
22

3+
> ⚠️ **Work in Progress / Experimental** — Version Guard is under active development. APIs, configuration formats, and behavior may change without notice. Use at your own risk in production environments.
4+
35
**Version Guard** is an open-source cloud infrastructure version monitoring system that continuously scans cloud resources (databases, caches, compute) to detect version drift and compliance issues.
46

57
## 🎯 Purpose
@@ -129,8 +131,11 @@ docker compose up --build
129131
|---------|---------|------|
130132
| `temporal` | Workflow orchestration | `7233` (gRPC), `8233` (Web UI) |
131133
| `minio` | S3-compatible snapshot storage | `9000` (API), `9001` (Console) |
134+
| `endoflife` | Local EOL data override (nginx) | `8082` |
132135
| `version-guard` | The server | `8080` (gRPC) |
133136

137+
The `endoflife` service serves patched EOL data for products with pending upstream PRs on [endoflife.date](https://endoflife.date), and proxies everything else to the live API. See [`deploy/endoflife-override/README.md`](./deploy/endoflife-override/README.md) for details on adding or updating overrides.
138+
134139
Once running, open the Temporal Web UI at http://localhost:8233 to trigger and monitor workflows.
135140

136141
### Run Locally (manual)
@@ -252,6 +257,7 @@ Version Guard is configured via environment variables or CLI flags:
252257
| `WIZ_CLIENT_ID_SECRET` | Wiz client ID (optional) | - |
253258
| `WIZ_CLIENT_SECRET_SECRET` | Wiz client secret (optional) | - |
254259
| `WIZ_REPORT_IDS` | JSON map of resource ID to Wiz report ID (optional) | - |
260+
| `EOL_BASE_URL` | Custom endoflife.date API base URL (optional) | `https://endoflife.date/api` |
255261
| `CONFIG_PATH` | Path to resources config file | `config/resources.yaml` |
256262
| `TAG_APP_KEYS` | Comma-separated AWS tag keys for app/service | `app,application,service` |
257263
| `TAG_ENV_KEYS` | Comma-separated AWS tag keys for environment | `environment,env` |

cmd/server/main.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ type ServerCLI struct {
5353
WizElastiCacheReportID string `help:"Wiz saved report ID for ElastiCache inventory" env:"WIZ_ELASTICACHE_REPORT_ID"`
5454
WizEKSReportID string `help:"Wiz saved report ID for EKS inventory" env:"WIZ_EKS_REPORT_ID"`
5555

56+
// EOL configuration
57+
EOLBaseURL string `help:"Custom base URL for endoflife.date API (e.g., http://localhost:8082/api)" env:"EOL_BASE_URL"`
58+
5659
// AWS configuration (for EOL APIs)
5760
AWSRegion string `help:"AWS region for EOL APIs" default:"us-west-2" env:"AWS_REGION"`
5861

@@ -206,7 +209,13 @@ func (s *ServerCLI) Run(_ *kong.Context) error {
206209
}
207210

208211
// Create EOL HTTP client (shared across all resources)
209-
eolHTTPClient := eolendoflife.NewRealHTTPClient()
212+
var eolHTTPClient eolendoflife.Client
213+
if s.EOLBaseURL != "" {
214+
fmt.Printf("✓ Using custom EOL API: %s\n", s.EOLBaseURL)
215+
eolHTTPClient = eolendoflife.NewRealHTTPClientWithConfig(nil, s.EOLBaseURL)
216+
} else {
217+
eolHTTPClient = eolendoflife.NewRealHTTPClient()
218+
}
210219
cacheTTL := 24 * time.Hour
211220

212221
// Initialize policy engine (shared across all detectors)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# endoflife.date Local Override
2+
3+
Version Guard uses [endoflife.date](https://endoflife.date) for all EOL lifecycle data. Sometimes upstream PRs take time to merge, leaving gaps in coverage (products return 404 or are missing recent version cycles).
4+
5+
This override mechanism lets you **patch EOL data locally** without waiting for upstream merges.
6+
7+
## How It Works
8+
9+
An nginx container serves local JSON files from `api/` and proxies everything else to the upstream endoflife.date API:
10+
11+
```
12+
Request: GET /api/amazon-aurora-mysql.json
13+
1. Check local file: deploy/endoflife-override/api/amazon-aurora-mysql.json
14+
2. If found → serve it (your patched data)
15+
3. If not found → proxy to https://endoflife.date/api/amazon-aurora-mysql.json
16+
```
17+
18+
## Adding or Patching a Product
19+
20+
1. Fetch the current data (or create new data from a pending PR's Netlify preview):
21+
22+
```bash
23+
# Existing product — fetch and patch
24+
curl -s https://endoflife.date/api/amazon-opensearch.json | python3 -m json.tool > api/amazon-opensearch.json
25+
# Edit the file to add missing cycles
26+
27+
# New product — fetch from PR deploy preview
28+
curl -s https://deploy-preview-9534--endoflife-date.netlify.app/api/amazon-aurora-mysql.json \
29+
| python3 -m json.tool > api/amazon-aurora-mysql.json
30+
```
31+
32+
2. Restart docker-compose — no rebuild needed:
33+
34+
```bash
35+
docker compose restart endoflife
36+
```
37+
38+
## Current Overrides
39+
40+
| File | Reason | Upstream PR |
41+
|------|--------|-------------|
42+
| `amazon-aurora-mysql.json` | Product not yet on endoflife.date | [#9534](https://github.com/endoflife-date/endoflife.date/pull/9534) |
43+
| `amazon-opensearch.json` | Missing cycles 3.3 and 3.5 | [#9919](https://github.com/endoflife-date/endoflife.date/pull/9919) |
44+
45+
## Configuration
46+
47+
Set `EOL_BASE_URL` to point Version Guard at the local override container:
48+
49+
```yaml
50+
# docker-compose.yaml
51+
environment:
52+
EOL_BASE_URL: http://endoflife:8080/api
53+
```
54+
55+
When `EOL_BASE_URL` is not set, Version Guard connects directly to `https://endoflife.date/api` (the default).
56+
57+
## Removing Overrides
58+
59+
Once an upstream PR is merged, delete the local JSON file. Nginx will then proxy that product to the upstream API automatically.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{"cycle":"3","releaseDate":"2021-11-23","eol":"2028-04-30","latest":"3.12.0","latestReleaseDate":"2026-02-17","lts":false,"extendedSupport":"2029-07-31"},
3+
{"cycle":"2","releaseDate":"2018-02-06","eol":"2024-10-31","latest":"2.12.5","latestReleaseDate":"2025-04-09","lts":false,"extendedSupport":"2027-02-28"},
4+
{"cycle":"1","releaseDate":"2015-07-27","eol":"2023-02-28","latest":"1.23.4","latestReleaseDate":"2022-08-11","lts":false,"extendedSupport":false}
5+
]
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
[
2+
{
3+
"cycle": "3.5",
4+
"releaseDate": "2026-03-25",
5+
"eol": false,
6+
"lts": false,
7+
"extendedSupport": true
8+
},
9+
{
10+
"cycle": "3.3",
11+
"releaseDate": "2025-12-16",
12+
"eol": false,
13+
"lts": false,
14+
"extendedSupport": true
15+
},
16+
{
17+
"cycle": "3.1",
18+
"releaseDate": "2025-09-09",
19+
"eol": false,
20+
"lts": false,
21+
"extendedSupport": true
22+
},
23+
{
24+
"cycle": "2.19",
25+
"releaseDate": "2025-04-30",
26+
"eol": false,
27+
"lts": false,
28+
"extendedSupport": true
29+
},
30+
{
31+
"cycle": "2.17",
32+
"releaseDate": "2024-11-13",
33+
"eol": false,
34+
"lts": false,
35+
"extendedSupport": true
36+
},
37+
{
38+
"cycle": "2.15",
39+
"releaseDate": "2024-10-11",
40+
"eol": false,
41+
"lts": false,
42+
"extendedSupport": true
43+
},
44+
{
45+
"cycle": "2.13",
46+
"releaseDate": "2024-05-21",
47+
"eol": false,
48+
"lts": false,
49+
"extendedSupport": true
50+
},
51+
{
52+
"cycle": "2.11",
53+
"releaseDate": "2023-11-17",
54+
"eol": false,
55+
"lts": false,
56+
"extendedSupport": true
57+
},
58+
{
59+
"cycle": "2.9",
60+
"releaseDate": "2023-10-02",
61+
"eol": "2025-11-07",
62+
"lts": false,
63+
"extendedSupport": "2026-11-07"
64+
},
65+
{
66+
"cycle": "2.7",
67+
"releaseDate": "2023-07-10",
68+
"eol": "2025-11-07",
69+
"lts": false,
70+
"extendedSupport": "2026-11-07"
71+
},
72+
{
73+
"cycle": "2.5",
74+
"releaseDate": "2023-03-13",
75+
"eol": "2025-11-07",
76+
"lts": false,
77+
"extendedSupport": "2026-11-07"
78+
},
79+
{
80+
"cycle": "2.3",
81+
"releaseDate": "2022-11-15",
82+
"eol": "2025-11-07",
83+
"lts": false,
84+
"extendedSupport": "2026-11-07"
85+
},
86+
{
87+
"cycle": "1.3",
88+
"releaseDate": "2022-08-16",
89+
"eol": false,
90+
"lts": false,
91+
"extendedSupport": true
92+
},
93+
{
94+
"cycle": "1.2",
95+
"releaseDate": "2022-04-04",
96+
"eol": "2025-11-07",
97+
"lts": false,
98+
"extendedSupport": "2026-11-07"
99+
},
100+
{
101+
"cycle": "1.1",
102+
"releaseDate": "2022-01-04",
103+
"eol": "2025-11-07",
104+
"lts": false,
105+
"extendedSupport": "2026-11-07"
106+
},
107+
{
108+
"cycle": "1.0",
109+
"releaseDate": "2021-09-08",
110+
"eol": "2025-11-07",
111+
"lts": false,
112+
"extendedSupport": "2026-11-07"
113+
}
114+
]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
server {
2+
listen 8080;
3+
4+
# Serve local overrides first
5+
location /api/ {
6+
root /data;
7+
try_files $uri @upstream;
8+
}
9+
10+
# Proxy to upstream endoflife.date for everything else
11+
location @upstream {
12+
proxy_pass https://endoflife.date;
13+
proxy_set_header Host endoflife.date;
14+
proxy_set_header User-Agent "version-guard/1.0";
15+
proxy_ssl_server_name on;
16+
}
17+
}

docker-compose.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ services:
3434
echo 'Bucket created'
3535
"
3636
37+
endoflife:
38+
image: nginx:alpine
39+
volumes:
40+
- ./deploy/endoflife-override/nginx.conf:/etc/nginx/conf.d/default.conf:ro
41+
- ./deploy/endoflife-override/api:/data/api:ro
42+
ports:
43+
- "8082:8080"
44+
3745
version-guard:
3846
build:
3947
context: .
@@ -54,6 +62,7 @@ services:
5462
WIZ_CLIENT_ID_SECRET: ${WIZ_CLIENT_ID_SECRET:-}
5563
WIZ_CLIENT_SECRET_SECRET: ${WIZ_CLIENT_SECRET_SECRET:-}
5664
WIZ_REPORT_IDS: ${WIZ_REPORT_IDS:-}
65+
EOL_BASE_URL: http://endoflife:8080/api
5766
ports:
5867
- "8080:8080"
5968
- "8081:8081"

0 commit comments

Comments
 (0)