一键导入
update-controller
Update an existing ORC controller. Use when adding fields, making fields mutable, adding tag support, or improving error handling.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Update an existing ORC controller. Use when adding fields, making fields mutable, adding tag support, or improving error handling.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Review ORC controller code for Kubernetes best practices and ORC conventions. Use after implementing or modifying a controller.
Add a dependency on another ORC resource to a controller. Use when a resource needs to reference or wait for another resource (e.g., Subnet depends on Network).
Create a new ORC controller for an OpenStack resource. Use when adding support for a new OpenStack resource type (e.g., LoadBalancer, FloatingIP).
Write an enhancement proposal for a new ORC feature. Use for significant new features, breaking changes, or cross-cutting architectural changes.
Run ORC tests (unit tests, linting, and E2E tests). Use after making changes to verify correctness.
Draft release notes for a new ORC version. Use when preparing a release to generate changelog and GitHub release body from git history.
| name | update-controller |
| description | Update an existing ORC controller. Use when adding fields, making fields mutable, adding tag support, or improving error handling. |
| disable-model-invocation | true |
Guide for modifying an existing ORC controller.
Reference: See website/docs/development/ for detailed patterns and rationale.
Research the resource before implementing changes:
Check gophercloud for the resource's API:
go doc <gophercloud-module>.UpdateOpts
go doc <gophercloud-module>.CreateOpts
Check existing controller patterns:
Check OpenStack API documentation for:
When updating controllers, follow the patterns in patterns.md
Update API types in api/v1alpha1/<kind>_types.go:
<Kind>ResourceSpec<Kind>ResourceStatus+kubebuilder:validation:*)Update actuator in internal/controllers/<kind>/actuator.go:
CreateOpts in CreateResource()Update status writer in internal/controllers/<kind>/status.go:
ApplyResourceStatus()Regenerate:
make generate
Update tests to cover the new field (add only what's relevant to your change):
internal/controllers/<kind>/actuator_test.go (if complex logic)internal/controllers/<kind>/tests/:
create-full: Set new field to non-default value and verifycreate-minimal: Verify default value behavior (if field has defaults)update: Test setting and unsetting the field (only if field is mutable)*-dependency: Test dependency behavior (only if adding a new dependency)*import*: Test import filtering (only if adding a new filter field)Add field to <Kind>Filter in api/v1alpha1/<kind>_types.go
Update ListOSResourcesForImport() in actuator to apply the filter
Add import test case
Remove immutability validation from the field:
// Remove or update this validation
// +kubebuilder:validation:XValidation:rule="self == oldSelf"
Implement GetResourceReconcilers() if not already present
Add update handling to the updateResource() reconciler (or create it if not present). Follow the pattern in internal/controllers/securitygroup/actuator.go (or trunk/actuator.go):
UpdateOpts struct using handleXXXUpdate() helpers for each mutable fieldneedsUpdate() helper that serializes the opts to a map and checks len() > 0progress.NeedsRefresh()spec.resource is nilNote: Only use updateResource when the field is updated via the resource's standard Update API. If the field requires a different API (e.g., extra specs, subports, tags on networking resources), create a separate single-concern reconciler instead. See Adding a Single-Concern Reconciler below.
Register in GetResourceReconcilers():
return []resourceReconciler{
actuator.updateResource,
}, nil
When a mutable field uses a separate OpenStack API (not the resource's Update API), create a dedicated reconciler with a descriptive verb+noun name instead of adding logic to updateResource.
Examples: reconcileExtraSpecs (flavor, volumetype), reconcileSubports (trunk), reconcilePassword (user), updateRules (securitygroup).
Key differences from updateResource:
reconcileExtraSpecs), not updateResource.nil when spec.resource is nil (not a terminal error). The terminal error pattern is reserved for updateResource.reconcileSubports, updateRules).func (actuator myActuator) reconcileExtraSpecs(ctx context.Context, obj orcObjectPT, osResource *osResourceT) progress.ReconcileStatus {
resource := obj.Spec.Resource
if resource == nil {
return nil // Not a terminal error (unlike updateResource)
}
// Compute desired vs current diff
// Make API calls (creates, updates, deletes)
// Return progress.NeedsRefresh() if any changes were made
}
Register alongside other reconcilers in GetResourceReconcilers():
return []resourceReconciler{
actuator.updateResource, // general field updates via Update API
actuator.reconcileExtraSpecs, // single-concern: separate API
}, nil
Do NOT duplicate work in CreateResource. If a reconciler handles a concern (e.g., extra specs), do not also set that data in CreateResource. The CreateResource contract forbids actions that can fail after creating the primary resource. The reconciler will handle it on the first reconciliation after creation.
See add-dependency for detailed steps.
For resources with intermediate provisioning states, ensure robust deletion:
func (actuator myActuator) DeleteResource(ctx context.Context, _ orcObjectPT, resource *osResourceT) progress.ReconcileStatus {
// Handle intermediate states
switch resource.ProvisioningStatus {
case ProvisioningStatusPendingDelete:
return progress.WaitingOnOpenStack(progress.WaitingOnReady, deletingPollingPeriod)
case ProvisioningStatusPendingCreate, ProvisioningStatusPendingUpdate:
// Can't delete in pending state, wait for ACTIVE
return progress.WaitingOnOpenStack(progress.WaitingOnReady, availablePollingPeriod)
}
err := actuator.osClient.DeleteResource(ctx, resource.ID)
// Handle 409 (state changed between check and API call)
if orcerrors.IsConflict(err) {
return progress.WaitingOnOpenStack(progress.WaitingOnReady, deletingPollingPeriod)
}
return progress.WrapError(err)
}
Important: Never use cascade delete unless explicitly requested by the user.
Note: Tag handling varies by OpenStack service. Some services (e.g., block storage) include tags in the standard Update API, while others (e.g., networking) require a separate tags API and a dedicated reconciler. Check gophercloud for the specific resource.
Add Tags field to spec and status:
// In ResourceSpec
// +kubebuilder:validation:MaxItems:=64
// +listType=set
Tags []NeutronTag `json:"tags,omitempty"`
// In ResourceStatus
// +listType=atomic
Tags []string `json:"tags,omitempty"`
Sort tags before creation and comparison (use slices.Sort — see patterns.md §3 Deterministic State).
Add a handleTagsUpdate() helper that sorts both desired and current tags, compares with slices.Equal, and sets updateOpts.Tags only if different. Copy before sorting to avoid mutating the original.
Register updateResource (which calls handleTagsUpdate) in GetResourceReconcilers().
For resources with provisioning states, prefer using constants from gophercloud when available. Only define constants in ORC's types.go if gophercloud doesn't provide them.
// Prefer gophercloud constants when available:
import "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers"
if osResource.ProvisioningStatus == loadbalancers.ProvisioningStatusActive { ... }
// Only define in types.go if gophercloud doesn't have them:
const (
MyResourceProvisioningStatusActive = "ACTIVE"
MyResourceProvisioningStatusPendingCreate = "PENDING_CREATE"
MyResourceProvisioningStatusError = "ERROR"
)
See also patterns.md for more details on this pattern.
See patterns.md §4 Error Classification. Wrap non-retryable errors with orcerrors.Terminal; leave transient errors as-is for automatic retry.
Follow testing for running unit tests, linting, and E2E tests.
make generate runs cleanlymake lint passesmake test passes