-
Notifications
You must be signed in to change notification settings - Fork 37
improved skill #711
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
improved skill #711
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,22 +23,36 @@ Ask the user one by one about: | |
|
|
||
| ## Step 1: Research the OpenStack Resource | ||
|
|
||
| **Before scaffolding**, research the resource to understand the exact field names: | ||
| **Before scaffolding**, research the resource to understand field names, types, and mutability: | ||
|
|
||
| 1. **Read the gophercloud struct** to get exact field names: | ||
| 1. **Read the gophercloud struct** to get exact field names and types: | ||
| ```bash | ||
| go doc <gophercloud-module>.<Type> | ||
| go doc <gophercloud-module>.CreateOpts | ||
| ``` | ||
| - Note field types precisely: `int32` in gophercloud → `*int32` in status (not `*int`) | ||
|
|
||
| 2. **Look at a similar existing controller** for patterns (if user provided one): | ||
| 2. **Check OpenStack API documentation** for update capabilities: | ||
| - URL: `https://docs.openstack.org/api-ref/<service>/` | ||
| - Which fields can be updated? (determines mutability in spec) | ||
|
|
||
| 3. **Look at a similar existing controller** for patterns (if user provided one): | ||
| - Check their `*_types.go` for API structure | ||
| - Check their `actuator.go` for implementation patterns | ||
|
|
||
| 3. **Note the exact field names** from gophercloud - use these when defining API types: | ||
| - If gophercloud has `VipSubnetID`, name the ORC field `VipSubnetRef` (not just `SubnetRef`) | ||
| - If gophercloud has `FlavorID`, name the ORC field `FlavorRef` | ||
| - Preserve prefixes like `Vip`, `Source`, `Destination` etc. | ||
| 4. **Field naming rules for API types**: | ||
| - **Status**: Preserve gophercloud names exactly, just camelCase for JSON | ||
| - Example: gophercloud `ProjectID` → status `projectID` | ||
| - **Expose ALL fields from gophercloud struct** unless they contain sensitive data or are redundant | ||
| - Include: core fields (IDs, names), observability fields (state, network details), metadata (timestamps like `createdAt`, `updatedAt`) | ||
| - **Spec**: Convert OpenStack ID fields to ORC references using `*KubernetesNameRef` with `Ref` suffix | ||
| - **Naming rule**: Use the ORC resource name (e.g., `networkRef`, `subnetRef`, `flavorRef`) | ||
| - **Keep prefixes only if they have semantic meaning or prevent collision**: | ||
| - Keep semantic prefixes: `vipSubnetRef` (VIP has meaning), `floatingNetworkRef` (floating has meaning), `sourceSecurityGroupRef` (distinguishes from destination) | ||
| - Drop service prefixes: `NeutronNetID` → `networkRef` (not `neutronNetRef` - users think in terms of ORC Network resource) | ||
| - Keep distinguishing prefixes: If resource has both `NetworkID` and `ExternalNetworkID` → `networkRef` and `externalNetworkRef` | ||
| - **Only if ORC has a controller for that resource** - otherwise omit the field and add a TODO comment | ||
| - Example: `// TODO(controller): Add AvailabilityZoneRef when AvailabilityZone controller is implemented` | ||
|
|
||
| ## Step 2: Run Scaffolding Tool | ||
|
|
||
|
|
@@ -66,7 +80,7 @@ Use the field names discovered in Step 1 to inform your implementation later. | |
| | `-deleting-polling-period` | Polling period in seconds while waiting for resource to be deleted | `0` (deleted immediately) | | ||
| | `-required-create-dependency` | Required dependency for creation (can repeat flag for multiple) | none | | ||
| | `-optional-create-dependency` | Optional dependency for creation (can repeat flag for multiple) | none | | ||
| | `-import-dependency` | Dependency for import filter (can repeat flag for multiple) | none | | ||
| | `-import-dependency` | Dependency for import filter (can repeat flag for multiple). Prefer enabling all available dependencies that can be used for filtering. | none | | ||
| | `-interactive` | Run in interactive mode | `true` (set to `false` for scripted use) | | ||
|
|
||
| ### Common Gophercloud Clients | ||
|
|
@@ -198,24 +212,38 @@ Implement: | |
| - `CreateResource()` - Build CreateOpts, call OpenStack API | ||
| - `DeleteResource()` - Call delete API | ||
| - `ListOSResourcesForImport()` - Apply filter to list results | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, we also want to add all available fields for filtering. Wdyt? |
||
| - `ListOSResourcesForAdoption()` - Match by spec fields | ||
| - `ListOSResourcesForAdoption()` - Match by spec fields. **Include all available fields** that the OpenStack List API supports for filtering (e.g., name, description, and other immutable configuration fields) | ||
| - `GetResourceReconcilers()` - (if resource supports updates) | ||
|
|
||
| ### Implementation Patterns | ||
|
|
||
| Follow the patterns in @.agents/skills/new-controller/patterns.md when implementing the actuator and API types. | ||
| Follow the patterns in patterns.md when implementing the actuator and API types. | ||
|
|
||
| ### Status Writer (internal/controllers/<kind>/status.go) | ||
|
|
||
| Implement: | ||
| - `ResourceAvailableStatus()` - When is resource available? | ||
| - `ApplyResourceStatus()` - Map OpenStack fields to status | ||
| - **Expose ALL fields from the gophercloud struct** unless they contain sensitive data (passwords, tokens) or are redundant with ORC object metadata | ||
| - Include core fields (IDs, names, primary configuration) | ||
| - Include observability fields (state, status, network details, capacity information) | ||
| - Include metadata fields (timestamps like `createdAt`, `updatedAt` if available) | ||
| - Use conditional writes for optional/zero-value fields (see existing controllers for patterns) | ||
|
|
||
| ## Step 7: Write and Run Tests | ||
|
|
||
| **This step is required** - do not skip it. | ||
|
|
||
| Complete the test stubs in `internal/controllers/<kind>/tests/` and run tests following @.agents/skills/testing/SKILL.md | ||
| The scaffolding tool creates test directory stubs in `internal/controllers/<kind>/tests/` with README files explaining each test suite's purpose. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it'd be good to include an orientation on implementing all kinds of tests that we provide. That is, e2e tests, unit tests via Regarding the latter, @mandre pushed a PR making changes to the apivalidation package. If you all agree, it'd also be good to add them here as well. |
||
|
|
||
| **Read the README files** in each test directory to understand: | ||
| - What each test suite validates | ||
| - The test structure and steps | ||
| - What assertions are needed | ||
|
|
||
| Complete the test stubs and run tests following the testing skill. | ||
|
|
||
| **Test Assertion Pattern**: In create-full tests, validate **all status fields** including optional ones (observability fields, timestamps), not just basic fields like name and description. | ||
|
|
||
| ## Checklist | ||
|
|
||
|
|
@@ -229,20 +257,31 @@ Complete the test stubs in `internal/controllers/<kind>/tests/` and run tests fo | |
| - [ ] OpenStack client added to scope | ||
| - [ ] Controller registered in main.go | ||
| - [ ] API types implemented: | ||
| - [ ] Correct field names (matching OpenStack conventions) | ||
| - [ ] Status field names match gophercloud exactly | ||
| - [ ] All gophercloud fields exposed in status (core, observability, metadata/timestamps) | ||
| - [ ] Field types match gophercloud precisely (int32 vs int) | ||
| - [ ] Spec ID fields converted to *KubernetesNameRef with Ref suffix using ORC resource names | ||
| - [ ] Semantic prefixes preserved (vip, floating, source, destination, external) | ||
| - [ ] Service prefixes dropped (neutron → network/subnet) | ||
| - [ ] Stricter types where appropriate (IPvAny, custom tag types) | ||
| - [ ] Status constants in types.go (if resource has provisioning states) | ||
| - [ ] Actuator methods implemented: | ||
| - [ ] DeleteResource: no cascade unless explicitly requested | ||
| - [ ] DeleteResource: handles pending states and 409 Conflict (if resource has intermediate states) | ||
| - [ ] ListOSResourcesForAdoption: includes all available filterable fields (name, description, immutable config) | ||
| - [ ] CreateResource includes tags with sorting (if applicable) | ||
| - [ ] Proper error classification (Terminal vs retryable) | ||
| - [ ] Descriptive dependency variable names | ||
| - [ ] Status writer implemented | ||
| - [ ] Status writer implemented: | ||
| - [ ] All gophercloud fields mapped to status | ||
| - [ ] Conditional writes for optional/zero-value fields | ||
| - [ ] Timestamps included (createdAt, updatedAt if available) | ||
| - [ ] Update reconciler includes tags update (if tags are mutable) | ||
| - [ ] All TODOs resolved | ||
| - [ ] `make generate` runs cleanly | ||
| - [ ] `make lint` passes | ||
| - [ ] `make test` passes | ||
| - [ ] E2E tests written (including dependency tests if applicable) | ||
| - [ ] E2E tests written: | ||
| - [ ] Read README files in test directories | ||
| - [ ] All test stubs completed | ||
| - [ ] Create-full test validates ALL status fields (not just name/description) | ||
| - [ ] E2E tests passing | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always? Maybe I'm splitting hairs here, but a scenario where I don't believe we should have it as a pointer is when the zero-value doesn't matter. For instance, the new AddressScope controller has an IPVersion field returned by the API, and it should always be returned because we must specify it at creation and it can't be null. Here, the zero value does not matter since the possible values are only two: 4 or 6. Does it make sense?