Skip to content

Commit b3e5d7c

Browse files
committed
RHINENG-26546: add local run
1 parent 8fcbc91 commit b3e5d7c

7 files changed

Lines changed: 376 additions & 4 deletions

conf/workspace_backfill.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
LOG_LEVEL=info
2+
3+
DB_USER=admin
4+
DB_PASSWD=passwd
5+
6+
# Local Docker only: 1000 rows per job run (re-run job until complete). Production uses 50000 via WORKSPACE_BACKFILL_CONFIG.
7+
POD_CONFIG=update_users;update_db_config;wait_for_db=empty;use_testing_db;workspace_backfill_batch_size=1000;workspace_backfill_max_rows_per_run=1000
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Clear denormalized workspace columns without firing row triggers (for backfill e2e).
2+
\set ON_ERROR_STOP on
3+
4+
BEGIN;
5+
SET LOCAL session_replication_role = replica;
6+
7+
UPDATE system_inventory
8+
SET workspace_id = NULL,
9+
workspace_name = NULL
10+
WHERE workspace_id IS NOT NULL
11+
OR workspace_name IS NOT NULL;
12+
13+
COMMIT;
14+
15+
SELECT count(*) AS pending_backfill
16+
FROM system_inventory
17+
WHERE workspace_id IS NULL
18+
AND workspaces IS NOT NULL
19+
AND jsonb_typeof(workspaces) = 'array'
20+
AND jsonb_array_length(workspaces) > 0;

dev/test_generate_data.sql

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
\timing on
2+
\set ON_ERROR_STOP on
23

34
-- constants to drive number of items generateg
45
create table if not exists _const (
@@ -112,12 +113,15 @@ do $$
112113
ji := trunc(rnd*3)+1;
113114
insert into system_inventory
114115
(inventory_id, display_name, rh_account_id, vmaas_json, json_checksum,
115-
last_upload, arch, tags, created, os_name, os_major, rhsm_version,
116-
workspace_id, workspace_name)
116+
last_updated, last_upload, arch, tags, created, os_name, os_major, rhsm_version,
117+
workspaces, workspace_id, workspace_name)
117118
values
118119
(gen_uuid, gen_uuid::text, acc_id, json_data[ji], json_hash[ji],
119-
rnd_date2, 'x86_64', '[]'::jsonb, rnd_date1, 'RHEL', 8, '8.0',
120-
workspace_ids[cnt%3+1], workspace_ids[cnt%3+1]::text)
120+
rnd_date2, rnd_date2, 'x86_64', '[]'::jsonb, rnd_date1, 'RHEL', 8, '8.0',
121+
jsonb_build_array(jsonb_build_object(
122+
'id', workspace_ids[cnt % 3 + 1]::text,
123+
'name', workspace_ids[cnt % 3 + 1]::text)),
124+
workspace_ids[cnt % 3 + 1], workspace_ids[cnt % 3 + 1]::text)
121125
returning id into new_id;
122126
insert into system_patch
123127
(rh_account_id, system_id, last_evaluation,
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
-- Load-test generator for system_inventory (+ required rh_account, system_patch).
2+
-- Faster subset of dev/test_generate_data.sql (no advisories, repos, packages, etc.).
3+
\timing on
4+
\set ON_ERROR_STOP on
5+
6+
CREATE TABLE IF NOT EXISTS _const (
7+
key TEXT PRIMARY KEY,
8+
val INT
9+
);
10+
11+
TRUNCATE _const;
12+
INSERT INTO _const VALUES
13+
('accounts', 50), -- rh_account rows
14+
('systems', 7500), -- system_inventory + system_patch rows
15+
('progress_pct', 10); -- progress NOTICE every N%
16+
17+
-- Minimal vmaas_json samples (same as test_generate_data.sql)
18+
CREATE TABLE IF NOT EXISTS _json (
19+
id INT PRIMARY KEY,
20+
data TEXT,
21+
hash TEXT
22+
);
23+
INSERT INTO _json VALUES
24+
(1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ]}'),
25+
(2, '{ "package_list": [ "libsmbclient-4.6.2-12.el7_4.x86_64", "dconf-0.26.0-2.el7.x86_64"]}'),
26+
(3, '{ "repository_list": [ "rhel-7-server-rpms" ], "releasever": "7Server", "basearch": "x86_64", "package_list": [ "libsmbclient-4.6.2-12.el7_4.x86_64"]}')
27+
ON CONFLICT DO NOTHING;
28+
UPDATE _json SET hash = encode(sha256(data::bytea), 'hex');
29+
30+
-- Wipes accounts and all dependent host data (inventory, patch, advisories links, packages, …)
31+
TRUNCATE rh_account CASCADE;
32+
33+
ALTER SEQUENCE rh_account_id_seq RESTART WITH 1;
34+
DO $$
35+
DECLARE
36+
cnt INT := 0;
37+
wanted INT;
38+
id INT;
39+
BEGIN
40+
SELECT val INTO wanted FROM _const WHERE key = 'accounts';
41+
WHILE cnt < wanted LOOP
42+
id := nextval('rh_account_id_seq');
43+
INSERT INTO rh_account (id, org_id) VALUES (id, 'RHACCOUNT-' || id);
44+
cnt := cnt + 1;
45+
END LOOP;
46+
RAISE NOTICE 'created % rh_accounts', wanted;
47+
END;
48+
$$;
49+
50+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
51+
ALTER SEQUENCE system_inventory_id_seq RESTART WITH 1;
52+
53+
DO $$
54+
DECLARE
55+
cnt INT := 0;
56+
wanted INT;
57+
progress INT;
58+
gen_uuid UUID;
59+
rh_accounts INT;
60+
rnd FLOAT;
61+
json_data TEXT[];
62+
json_hash TEXT[];
63+
rnd_date1 TIMESTAMPTZ;
64+
rnd_date2 TIMESTAMPTZ;
65+
acc_id INT;
66+
new_id BIGINT;
67+
ji INT;
68+
workspace_ids UUID[];
69+
BEGIN
70+
SELECT val INTO wanted FROM _const WHERE key = 'systems';
71+
SELECT val INTO progress FROM _const WHERE key = 'progress_pct';
72+
SELECT count(*) INTO rh_accounts FROM rh_account;
73+
json_data := array(SELECT data FROM _json ORDER BY id);
74+
json_hash := array(SELECT hash FROM _json ORDER BY id);
75+
workspace_ids := array(SELECT uuid_generate_v4() FROM generate_series(1, 3));
76+
77+
WHILE cnt < wanted LOOP
78+
gen_uuid := uuid_generate_v4();
79+
rnd := random();
80+
rnd_date1 := now() - make_interval(days => (rnd * 30)::INT);
81+
rnd_date2 := rnd_date1 + make_interval(days => (rnd * 10)::INT);
82+
acc_id := trunc(rnd * rh_accounts) + 1;
83+
ji := trunc(rnd * 3) + 1;
84+
85+
INSERT INTO system_inventory
86+
(inventory_id, display_name, rh_account_id, vmaas_json, json_checksum,
87+
last_updated, last_upload, arch, tags, created, os_name, os_major, rhsm_version,
88+
workspaces, workspace_id, workspace_name)
89+
VALUES
90+
(gen_uuid, gen_uuid::text, acc_id, json_data[ji], json_hash[ji],
91+
rnd_date2, rnd_date2, 'x86_64', '[]'::jsonb, rnd_date1, 'RHEL', 8, '8.0',
92+
jsonb_build_array(jsonb_build_object(
93+
'id', workspace_ids[cnt % 3 + 1]::text,
94+
'name', workspace_ids[cnt % 3 + 1]::text)),
95+
workspace_ids[cnt % 3 + 1], workspace_ids[cnt % 3 + 1]::text)
96+
RETURNING id INTO new_id;
97+
98+
INSERT INTO system_patch
99+
(rh_account_id, system_id, last_evaluation,
100+
packages_installed, packages_installable, packages_applicable)
101+
VALUES
102+
(acc_id, new_id, rnd_date2, trunc(rnd * 1000), trunc(rnd * 50), trunc(rnd * 50));
103+
104+
IF mod(cnt, greatest(1, ceil((wanted::numeric * progress) / 100.0)::int)) = 0 THEN
105+
RAISE NOTICE 'created % systems (inventory + patch)', cnt;
106+
END IF;
107+
cnt := cnt + 1;
108+
END LOOP;
109+
RAISE NOTICE 'created % systems (inventory + patch)', wanted;
110+
END;
111+
$$;
112+
113+
SELECT 'rh_account' AS tbl, count(*) FROM rh_account
114+
UNION ALL
115+
SELECT 'system_inventory', count(*) FROM system_inventory
116+
UNION ALL
117+
SELECT 'system_patch', count(*) FROM system_patch;
118+
119+
SELECT parent.relname AS parent,
120+
child.relname AS child,
121+
pg_size_pretty(pg_relation_size(child.oid)) AS size
122+
FROM pg_inherits
123+
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
124+
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
125+
WHERE parent.relname IN ('system_inventory', 'system_patch')
126+
ORDER BY 1, 2;

dev/workspace_backfill.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Workspace backfill — local testing
2+
3+
Backfill copies `workspaces` JSON into `workspace_id` and `workspace_name` on `system_inventory` via the `workspace_backfill` job.
4+
5+
**Do not run** `docker-compose.test.yml` and `docker-compose.workspace-backfill.yml` at the same time (both use host port **5433**).
6+
7+
## Files
8+
9+
| File | Purpose |
10+
|------|---------|
11+
| [`docker-compose.workspace-backfill.yml`](../docker-compose.workspace-backfill.yml) | `db` + optional one-shot e2e runner |
12+
| [`conf/workspace_backfill.env`](../conf/workspace_backfill.env) | Local Docker `POD_CONFIG` (**1000** rows per job run) |
13+
| [`dev/test_generate_system_inventory.sql`](test_generate_system_inventory.sql) | Fast load: accounts + inventory + patch only |
14+
| [`dev/prepare_workspace_backfill_test.sql`](prepare_workspace_backfill_test.sql) | Clear `workspace_id` / `workspace_name` without triggers |
15+
| [`dev/verify_workspace_backfill.sql`](verify_workspace_backfill.sql) | Check pending / mismatched rows |
16+
| [`scripts/workspace_backfill_e2e.sh`](../scripts/workspace_backfill_e2e.sh) | Automated pipeline: setup + **one** job run + verify |
17+
18+
## Configuration (`POD_CONFIG`)
19+
20+
| Key | Local Docker ([`conf/workspace_backfill.env`](../conf/workspace_backfill.env)) | Code / production default |
21+
|-----|----------------------------------------------------------------------------------|---------------------------|
22+
| `workspace_backfill_batch_size` | `1000` | `1000` |
23+
| `workspace_backfill_max_rows_per_run` | **`1000`** | **`50000`** ([`tasks/config.go`](../tasks/config.go), [`deploy/clowdapp.yaml`](../deploy/clowdapp.yaml)) |
24+
| `workspace_backfill_batch_sleep_ms` | `0` | `0` |
25+
26+
Local compose loads `workspace_backfill.env` so each job invocation updates at most **1000** rows. Re-run the job manually until logs say `Workspace backfill complete`.
27+
28+
Override per run: `docker compose run --rm -e 'POD_CONFIG=...' workspace-backfill ...`
29+
30+
## Manual workflow (recommended for batched local runs)
31+
32+
### 1. Run DB
33+
34+
```bash
35+
docker compose -f docker-compose.workspace-backfill.yml up -d db
36+
```
37+
38+
Wait for Postgres:
39+
40+
```bash
41+
until PGPASSWORD=passwd psql -h localhost -p 5433 -U admin -d patchman -c 'SELECT 1' >/dev/null 2>&1; do sleep 1; done
42+
```
43+
44+
### 2. Setup once
45+
46+
Migrates, loads test data, clears workspace columns. **Destructive** (`TRUNCATE rh_account CASCADE` in the generator).
47+
48+
```bash
49+
docker compose -f docker-compose.workspace-backfill.yml run --rm workspace-backfill bash -c '
50+
set -e -o pipefail
51+
cd /go/src/app
52+
export WAIT_FOR_EMPTY_DB=1
53+
./dev/scripts/wait-for-services.sh true
54+
go run main.go migrate file://./database_admin/migrations
55+
unset WAIT_FOR_EMPTY_DB
56+
export WAIT_FOR_FULL_DB=1
57+
./dev/scripts/wait-for-services.sh true
58+
unset WAIT_FOR_FULL_DB
59+
PGPASSWORD=passwd psql -h db -p 5432 -U admin -d patchman -f dev/test_generate_system_inventory.sql
60+
PGPASSWORD=passwd psql -h db -p 5432 -U admin -d patchman -f dev/prepare_workspace_backfill_test.sql
61+
'
62+
```
63+
64+
Default generator creates **7500** systems. Lower `_const` in `test_generate_system_inventory.sql` first if you want a smaller dataset (e.g. `500` systems).
65+
66+
### 3. Run job (repeat until complete)
67+
68+
Each run processes up to **1000** rows (local env):
69+
70+
```bash
71+
docker compose -f docker-compose.workspace-backfill.yml run --rm workspace-backfill \
72+
./scripts/entrypoint.sh job workspace_backfill
73+
```
74+
75+
Logs:
76+
77+
- `Workspace backfill paused (per-run limit); more rows remain` — run step 3 again
78+
- `Workspace backfill complete` — done
79+
80+
Check pending count:
81+
82+
```bash
83+
PGPASSWORD=passwd psql -h localhost -p 5433 -U admin -d patchman -c \
84+
"SELECT count(*) AS pending FROM system_inventory WHERE workspace_id IS NULL AND workspaces IS NOT NULL;"
85+
```
86+
87+
Verify when `pending` is 0:
88+
89+
```bash
90+
PGPASSWORD=passwd psql -h localhost -p 5433 -U admin -d patchman -f dev/verify_workspace_backfill.sql
91+
```
92+
93+
Expect `pending = 0` and `mismatched = 0`.
94+
95+
### Teardown
96+
97+
```bash
98+
docker compose -f docker-compose.workspace-backfill.yml down
99+
```
100+
101+
## One-shot e2e (automated script)
102+
103+
Runs setup, **one** job invocation (local limit 1000 rows), then verification:
104+
105+
```bash
106+
docker compose -f docker-compose.workspace-backfill.yml up --build --abort-on-container-exit
107+
```
108+
109+
With the default generator (**7500** systems), verification fails after a single job run because only 1000 rows are backfilled. Use either:
110+
111+
- **≤1000** systems in `_const` before e2e, or
112+
- The **manual workflow** above (setup once, job repeated), or
113+
- A one-off higher limit for a full single-run test only:
114+
115+
```bash
116+
docker compose -f docker-compose.workspace-backfill.yml run --rm \
117+
-e 'POD_CONFIG=update_users;update_db_config;wait_for_db=empty;use_testing_db;workspace_backfill_batch_size=1000;workspace_backfill_max_rows_per_run=10000000' \
118+
workspace-backfill ./scripts/workspace_backfill_e2e.sh
119+
```
120+
121+
## Production
122+
123+
CronJob `workspace-backfill` in [`deploy/clowdapp.yaml`](../deploy/clowdapp.yaml) uses `WORKSPACE_BACKFILL_CONFIG` with `workspace_backfill_max_rows_per_run=50000` (suspended by default). Run on a schedule until pending rows are gone.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# E2E workspace backfill: DB + one-shot runner (no Kafka/platform).
2+
#
3+
# docker compose -f docker-compose.workspace-backfill.yml up --build --abort-on-container-exit
4+
#
5+
# Uses host port 5433 (same as docker-compose.test.yml). Do not run both stacks at once.
6+
7+
services:
8+
db:
9+
container_name: patchman-wb-db
10+
build:
11+
context: .
12+
dockerfile: dev/database/Dockerfile
13+
image: patchman-engine-db
14+
ports:
15+
- 5433:5432
16+
env_file:
17+
- ./conf/database.env
18+
19+
workspace-backfill:
20+
container_name: patchman-wb-runner
21+
build:
22+
context: .
23+
dockerfile: Dockerfile
24+
args:
25+
- INSTALL_TOOLS=yes
26+
target: buildimg
27+
image: patchman-engine-app
28+
env_file:
29+
- ./conf/common.env
30+
- ./conf/database.env
31+
- ./conf/database_admin.env
32+
- ./conf/gorun.env
33+
- ./conf/workspace_backfill.env
34+
depends_on:
35+
- db
36+
user: root
37+
command: ./scripts/workspace_backfill_e2e.sh
38+
volumes:
39+
- ./:/go/src/app/
40+
security_opt:
41+
- label=disable

scripts/workspace_backfill_e2e.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env bash
2+
# End-to-end local run: migrate DB, load system_inventory test data, clear workspace
3+
# columns, run workspace_backfill job once, verify.
4+
set -e -o pipefail
5+
6+
cd "$(dirname "$0")/.."
7+
8+
psql_admin() {
9+
PGPASSWORD="${DB_ADMIN_PASSWD:-passwd}" psql \
10+
-h "${DB_HOST:-db}" \
11+
-p "${DB_PORT:-5432}" \
12+
-U "${DB_ADMIN_USER:-admin}" \
13+
-d "${DB_NAME:-patchman}" \
14+
"$@"
15+
}
16+
17+
echo "==> Waiting for PostgreSQL"
18+
export WAIT_FOR_EMPTY_DB=1
19+
./dev/scripts/wait-for-services.sh true
20+
21+
echo "==> Running migrations"
22+
go run main.go migrate file://./database_admin/migrations
23+
24+
echo "==> Waiting for migrations to finish"
25+
unset WAIT_FOR_EMPTY_DB
26+
export WAIT_FOR_FULL_DB=1
27+
./dev/scripts/wait-for-services.sh true
28+
unset WAIT_FOR_FULL_DB
29+
30+
echo "==> Loading system_inventory test data"
31+
psql_admin -f dev/test_generate_system_inventory.sql
32+
33+
echo "==> Clearing workspace_id / workspace_name (triggers disabled per transaction)"
34+
psql_admin -f dev/prepare_workspace_backfill_test.sql
35+
36+
echo "==> Running workspace_backfill job"
37+
./scripts/entrypoint.sh job workspace_backfill
38+
39+
echo "==> Verifying backfill"
40+
mapfile -t _wb_counts < <(psql_admin -t -A -f dev/verify_workspace_backfill.sql)
41+
pending="${_wb_counts[0]}"
42+
mismatched="${_wb_counts[1]}"
43+
44+
echo "pending=${pending} mismatched=${mismatched}"
45+
46+
if [[ "${pending}" != "0" || "${mismatched}" != "0" ]]; then
47+
echo "Workspace backfill e2e verification failed" >&2
48+
exit 1
49+
fi
50+
51+
echo "Workspace backfill e2e finished successfully"

0 commit comments

Comments
 (0)