8000 feat: add cinder DeleteAttachment for stale volume attachments by xtrusia · Pull Request #107 · go-goose/goose · GitHub
[go: up one dir, main page]

Skip to content

feat: add cinder DeleteAttachment for stale volume attachments - #107

Merged
wallyworld merged 4 commits into
go-goose:v5from
xtrusia:force-detach-volume
Jul 20, 2026
Merged

wallyworld merged 4 commits into
go-goose:v5from
xtrusia:force-detach-volume

Conversation

@xtrusia
@xtrusia xtrusia commented Jun 29, 2026
Copy link
Copy Markdown
Contributor

Adds DeleteAttachment to remove a Cinder volume attachment via the attachments API (microversion 3.27), to free a volume left wedged in detaching after its instance was deleted mid-detach.
Cinder refuses with 409 while the instance still uses the volume, so non-duplicate 409s now map to a new Conflict error (errors.IsConflict) — split into its own commit since it affects all goose APIs via handleError.

go test ./... passes; new tests cover the 409 mapping and the cinder path.

xtrusia added 2 commits June 29, 2026 14:32
Add an attachments-API call to remove a volume attachment, which frees a
volume whose attachment has gone stale, for example when the instance was
deleted while a detach was still in flight and the volume is left wedged
in detaching. Cinder checks with the Compute API first: it removes the
attachment once the instance is gone and frees the volume, and refuses
with 409 while the instance is still using it. Unlike the os-force_detach
admin action this is permitted for the volume's owner.

The attachments API needs microversion 3.27, so the request sets the
OpenStack-API-Version header.

Also expose attachment_id on VolumeAttachment. On modern clouds the
volume's attachment entry reports the volume id in its id field, so a
caller targeting a specific attachment needs the real attachment id.

Signed-off-by: Seyeong Kim <seyeong.kim@canonical.com>
Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds support for cleaning up stale Cinder volume attachments by calling the Cinder attachments API (microversion 3.27), and introduces a first-class Conflict error to repr 8000 esent HTTP 409 responses surfaced through the shared HTTP client error handling.

Changes:

  • Map HTTP 409 responses (when not detected as “duplicate”) to a new Conflict error type (errors.IsConflict).
  • Add Client.DeleteAttachment to the Cinder client, backed by a generated deleteAttachment request with microversion volume 3.27.
  • Extend Cinder attachment modeling to expose attachment_id, and add unit tests covering the new behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
http/client.go Map 409 responses to a new Conflict error in shared HTTP error handling.
http/client_test.go Add unit test asserting 409 maps to errors.IsConflict.
errors/errors.go Add Conflict error code/type helpers and constructor.
cinder/client.go Expose DeleteAttachment on the Cinder client API.
cinder/client_test.go Add tests for DeleteAttachment success and conflict cases.
cinder/autogenerated_client.go Add attachment ID field + generated delete-attachment request with microversion header.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread http/client.go Resolved
Comment thread cinder/autogenerated_client.go Resolved
Pass response messages as format arguments for forbidden, duplicate value,
and conflict errors. This preserves percent signs in plain text and JSON
error responses.

Signed-off-by: Seyeong Kim <seyeong.kim@canonical.com>
@xtrusia
xtrusia force-pushed the force-detach-volume branch from a4759a3 to 687d33b Compare July 18, 2026 22:58
Return a not implemented error for Cinder v2 endpoints and for servers
that reject Block Storage microversion 3.27.

Signed-off-by: Seyeong Kim <seyeong.kim@canonical.com>
@xtrusia
xtrusia requested a review from wallyworld July 19, 2026 04:52
@wallyworld
wallyworld merged commit 1fed3fd into go-goose:v5 Jul 20, 2026
2 checks passed
jujubot added a commit to juju/juju that referenced this pull request Jul 20, 2026
#22753

Destroying a model hangs forever when a cinder volume is wedged in `detaching`/`attaching`.
Detach is orchestrated by nova, so if the instance is removed before nova finalises it, the attachment is orphaned and the volume never leaves the transitional state.
`destroyVolume` only issues a Compute-API detach and then polls, so the poll times out and the destroy retries forever — the controller log loops on `destroying volumes: timed out` roughly every 70s.

This change deletes the attachment directly via the cinder Attachments API (`DELETE /v3/{project}/attachments/{id}`, `OpenStack-API-Version: volume 3.27`) right after requesting the ordinary Compute-API detach.
It is permitted for the volume's owner (no admin needed): cinder removes the attachment when the instance is gone, returns 409 (Conflict) while it's still in use (we keep waiting), and on 403 we log and fall back to plain polling — no worse off than before.

In `destroyVolume`, a transitional `detaching`/`attaching` volume now falls through to `DeleteAttachment(attachmentId)` (once per attachment) instead of polling blindly.
The attachment id comes from `VolumeAttachment.AttachmentId` (`attachment_id`), not `Id` — on modern cinder (2026.1, v3) `attachments[].id` is the *volume* id.

Depends on go-goose/goose#107 (`cinder.DeleteAttachment`, the `VolumeAttachment.AttachmentId` field, 409 `IsConflict` handling).
`go.mod` carries a temporary `replace` to the goose fork; bump it to a tagged goose release once that PR merges.

## Checklist

- [x] Code style: imports ordered, good names, simple structure, etc
- [x] Comments saying why design decisions were made
- [x] Go unit tests, with comments saying what you're testing
- [ ] ~~[Integration tests](https://github.com/juju/juju/tree/main/tests), with comments saying what you're testing~~ — provider-level behaviour exercised by the unit tests and the live QA below
- [ ] ~~[doc.go](https://discourse.charmhub.io/t/readme-in-packages/451) added or updated in changed packages~~ — no package responsibilities changed

## QA steps

Unit tests:

```
go test ./internal/provider/openstack/ -check.f TestDestroyVolumesDeleteAttachment -count=1
```

Live (OpenStack 2026.1, cinder v3 mv 3.71), patched 3.6.25 vs stock 3.6.23 controller:

1. `juju deploy postgresql --storage pgdata=cinder,1G`; wait for the volume to be `in-use`.
2. Orphan it: `openstack server delete <instance-id>` (the server is then "No Server found", but the volume keeps an attachment pointing at it).
3. `juju remove-machine 0 --force` — the out-of-band delete leaves Juju's machine/filesystem records stale, blocking storage-attachment cleanup (`filesystem "0" on "machine 0" not found`) before the volume is destroyed; the real bug already has the machine gone, so this just reproduces that state.
4. `juju destroy-model fd-repro --no-prompt --destroy-storage`.
 - Stock 3.6.23: the volume stays wedged, the log loops on `destroying volumes: timed out`, destroy hangs.
 - Patched 3.6.25: `DeleteAttachment` clears the orphan, the volume is deleted, and destroy-model completes.

## Documentation changes

None. `juju` CLI output, flags and the client API are unchanged.

## Links

- Related: [#1845172](https://bugs.launchpad.net/juju/+bug/1845172), [#1784876](https://bugs.launchpad.net/juju/+bug/1784876), [#1854893](https://bugs.launchpad.net/juju/+bug/1854893), [nova #2060696](https://bugs.launchpad.net/nova/+bug/2060696)
- Depends on: [go-goose/goose#107](go-goose/goose#107)
- ~~Jira card~~ — none
jujubot added a commit to juju/juju that referenced this pull request Jul 23, 2026
…on-destroy

#22753

Destroying a model hangs forever when a cinder volume is wedged in `detaching`/`attaching`.
Detach is orchestrated by nova, so if the instance is removed before nova finalises it, the attachment is orphaned and the volume never leaves the transitional state.
`destroyVolume` only issues a Compute-API detach and then polls, so the poll times out and the destroy retries forever — the controller log loops on `destroying volumes: timed out` roughly every 70s.

This change deletes the attachment directly via the cinder Attachments API (`DELETE /v3/{project}/attachments/{id}`, `OpenStack-API-Version: volume 3.27`) right after requesting the ordinary Compute-API detach.
It is permitted for the volume's owner (no admin needed): cinder removes the attachment when the instance is gone, returns 409 (Conflict) while it's still in use (we keep waiting), and on 403 we log and fall back to plain polling — no worse off than before.

In `destroyVolume`, a transitional `detaching`/`attaching` volume now falls through to `DeleteAttachment(attachmentId)` (once per attachment) instead of polling blindly.
The attachment id comes from `VolumeAttachment.AttachmentId` (`attachment_id`), not `Id` — on modern cinder (2026.1, v3) `attachments[].id` is the *volume* id.

Depends on go-goose/goose#107 (`cinder.DeleteAttachment`, the `VolumeAttachment.AttachmentId` field, 409 `IsConflict` handling).
`go.mod` carries a temporary `replace` to the goose fork; bump it to a tagged goose release once that PR merges.

- [x] Code style: imports ordered, good names, simple structure, etc
- [x] Comments saying why design decisions were made
- [x] Go unit tests, with comments saying what you're testing
- [ ] ~~[Integration tests](https://github.com/juju/juju/tree/main/tests), with comments saying what you're testing~~ — provider-level behaviour exercised by the unit tests and the live QA below
- [ ] ~~[doc.go](https://discourse.charmhub.io/t/readme-in-packages/451) added or updated in changed packages~~ — no package responsibilities changed

Unit tests:

```
go test ./internal/provider/openstack/ -check.f TestDestroyVolumesDeleteAttachment -count=1
```

Live (OpenStack 2026.1, cinder v3 mv 3.71), patched 3.6.25 vs stock 3.6.23 controller:

1. `juju deploy postgresql --storage pgdata=cinder,1G`; wait for the volume to be `in-use`.
2. Orphan it: `openstack server delete <instance-id>` (the server is then "No Server found", but the volume keeps an attachment pointing at it).
3. `juju remove-machine 0 --force` — the out-of-band delete leaves Juju's machine/filesystem records stale, blocking storage-attachment cleanup (`filesystem "0" on "machine 0" not found`) before the volume is destroyed; the real bug already has the machine gone, so this just reproduces that state.
4. `juju destroy-model fd-repro --no-prompt --destroy-storage`.
 - Stock 3.6.23: the volume stays wedged, the log loops on `destroying volumes: timed out`, destroy hangs.
 - Patched 3.6.25: `DeleteAttachment` clears the orphan, the volume is deleted, and destroy-model completes.

None. `juju` CLI output, flags and the client API are unchanged.

- Related: [#1845172](https://bugs.launchpad.net/juju/+bug/1845172), [#1784876](https://bugs.launchpad.net/juju/+bug/1784876), [#1854893](https://bugs.launchpad.net/juju/+bug/1854893), [nova #2060696](https://bugs.launchpad.net/nova/+bug/2060696)
- Depends on: [go-goose/goose#107](go-goose/goose#107)
- ~~Jira card~~ — none

(cherry picked from commit eef62a8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0