Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Repository Guidelines

## Project Structure & Module Organization

This is a Go Prometheus exporter for OpenStack database metrics. The entrypoint is `cmd/openstack-database-exporter/main.go`. Collector code lives in `internal/collector/<service>/`, shared database and DSN logic in `internal/db/`, and shared test helpers in `internal/testutil/`. SQL source files are grouped by OpenStack service under `sql/<service>/` with `schema.sql`, `queries.sql`, and optional `indexes.sql` or `prereqs.sql`. Files under `internal/db/<service>/` are generated by `sqlc`; update the SQL inputs instead of editing generated Go directly.

## Build, Test, and Development Commands

- `go build -o /dev/null ./cmd/openstack-database-exporter`: compile the exporter, matching CI.
- `go run ./cmd/openstack-database-exporter --help`: inspect runtime flags and supported database URL environment variables.
- `go test -count=1 -short -race ./...`: run the unit test suite used by CI.
- `go test -v -count=1 -race -tags integration -timeout 15m ./...`: run container-backed integration tests; Docker must be available.
- `sqlc generate`: regenerate `internal/db/<service>/` after changing SQL definitions.
- `docker build -t openstack-database-exporter .`: build the scratch-based runtime image.

## Coding Style & Naming Conventions

Use standard Go formatting (`gofmt`) and keep packages lowercase. Follow the existing service-oriented layout: service packages such as `cinder`, `nova`, and `neutron` contain collectors and tests for that service. Prefer table-driven tests and explicit Prometheus metric comparisons, as seen in existing collector tests. Metric names should continue the `openstack_<service>_<resource>` pattern.

## Testing Guidelines

Unit tests use `testing`, `go-sqlmock`, `testify`, and Prometheus `testutil`; name files `*_test.go` and tests `Test<Behavior>`. Integration tests use the `integration` build tag and `testcontainers-go` with MariaDB 11; name them `TestIntegration_<Collector>` where practical. Set `SKIP_INTEGRATION=1` only when intentionally skipping tagged integration tests.

## Commit & Pull Request Guidelines

Recent history mostly follows Conventional Commit subjects such as `fix: ...`, `feat: ...`, `ci: ...`, and `chore(deps): ...`; use the same concise imperative style. Pull requests should describe the exporter behavior changed, list affected services or metrics, link related issues, and note any SQL or generated-code updates. Run the relevant unit, integration, build, and `sqlc generate` checks before requesting review.

## Local Configuration & Security

Configure database access with oslo.db-style MySQL URLs in environment variables. Keep examples generic and never commit real credentials, endpoint names, local `exporter.sh` values, logs, or captured metric output.

```sh
export NOVA_DATABASE_URL="mysql+pymysql://<user>:<password>@<db-host>:<db-port>/nova"
export NOVA_API_DATABASE_URL="mysql+pymysql://<user>:<password>@<db-host>:<db-port>/nova_api"
export KEYSTONE_DATABASE_URL="mysql+pymysql://<user>:<password>@<db-host>:<db-port>/keystone"
export PLACEMENT_DATABASE_URL="mysql+pymysql://<user>:<password>@<db-host>:<db-port>/placement"
```

Set only the service URLs needed for the collectors under test.
5 changes: 3 additions & 2 deletions internal/collector/cinder/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ func TestIntegration_VolumesCollector(t *testing.T) {
('vol-001', 'boot-vol', 40, 'in-use', 'nova', 1, 'proj-001', 'user-001', 'vtype-001', 0),
('vol-002', 'data-vol', 100, 'available', 'nova', 0, 'proj-001', 'user-001', 'vtype-002', 0),
('vol-003', 'deleted-vol', 50, 'deleted', 'nova', 0, 'proj-002', 'user-002', 'vtype-001', 1)`,
`INSERT INTO volume_attachment (id, volume_id, instance_uuid, deleted) VALUES
('att-001', 'vol-001', 'server-001', 0)`,
`INSERT INTO volume_attachment (id, volume_id, instance_uuid, deleted, attach_status) VALUES
('att-001', 'vol-001', 'server-001', 0, 'attached'),
('att-002', 'vol-001', 'server-001', 0, 'detached')`,
)

collector := NewVolumesCollector(db, logger)
Expand Down
2 changes: 1 addition & 1 deletion internal/db/cinder/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sql/cinder/queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ SELECT
FROM
volumes v USE INDEX (volumes_service_uuid_idx)
LEFT JOIN volume_types vt ON v.volume_type_id = vt.id
LEFT JOIN volume_attachment va ON v.id = va.volume_id AND va.deleted = 0
LEFT JOIN volume_attachment va ON v.id = va.volume_id AND va.deleted = 0 AND va.attach_status = 'attached'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not drop attachments with NULL attach_status

In deployments or fixtures where volume_attachment.attach_status is still NULL for a non-deleted attachment, this join no longer matches the row and the exporter emits server_id="" for an in-use volume even though instance_uuid is present. The column is nullable/defaults to NULL in the repo schema, and the end-to-end Cinder seed in cmd/openstack-database-exporter/main_test.go currently inserts an attachment without attach_status, so this change silently loses the server label for that valid row shape rather than only filtering detached attachments.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After creating and attaching the volume, the real DB shows attach_status='attached', not NULL.

Key results:

attach_status  deleted  rows_total  rows_with_instance_uuid
attached       0        1           1
detached       1        149         143

Join comparison:

old join:              rows=1 volumes=1 rows_with_instance=1
strict attached join:  rows=1 volumes=1 rows_with_instance=1
NULL-inclusive join:   rows=1 volumes=1 rows_with_instance=1

Latest active attachment:

volume status: in-use
volume attach_status: attached
attachment_status: attached
instance_uuid: present
deleted: 0

So on current real Cinder behavior, strict va.attach_status = 'attached' works and does not drop the server label.

WHERE
(v.service_uuid IS NULL OR v.service_uuid IS NOT NULL)
AND v.deleted = 0;