Skip to content

[AISOS-1424] Add swift controller to ORC for bucket creation#1

Merged
eshulman2 merged 33 commits into
eshulman2:mainfrom
forgeSmith-bot:forge/aisos-1424
Jun 23, 2026
Merged

[AISOS-1424] Add swift controller to ORC for bucket creation#1
eshulman2 merged 33 commits into
eshulman2:mainfrom
forgeSmith-bot:forge/aisos-1424

Conversation

@forgeSmith-bot

@forgeSmith-bot forgeSmith-bot commented Jun 21, 2026

Copy link
Copy Markdown

Summary

This PR implements full Swift Object Storage support for the OpenStack Resource Controller (ORC), enabling Kubernetes-native management of Swift containers. Users can now create, update, import, and delete Swift containers using declarative CRDs, with support for metadata management, ACL configuration, and storage policies. This completes a major feature addition that brings object storage capabilities to ORC's supported resource types.

Changes

API Types (api/v1alpha1/)

  • Created swiftcontainer_types.go with SwiftContainerResourceSpec, SwiftContainerFilter, SwiftContainerImport, and SwiftContainerResourceStatus types
  • Added custom validation types: SwiftContainerName (max 256 UTF-8 bytes, no forward slashes), SwiftContainerMetadata (key-value pairs)
  • Implemented kubebuilder validation markers including immutability rules for name and storagePolicy fields
  • Generated CRD manifest at config/crd/bases/openstack.k-orc.cloud_swiftcontainers.yaml

Swift Client (internal/osclients/)

  • Created swiftcontainer.go implementing SwiftContainerClient interface with CRUD operations using Gophercloud v2
  • Implemented ListContainers (iterator pattern), CreateContainer, GetContainer, UpdateContainer, DeleteContainer, and GetContainerMetadata methods
  • Added error client implementation for credential/service unavailability scenarios
  • Generated mock client for unit testing

Controller (internal/controllers/swiftcontainer/)

  • Created actuator.go implementing create, delete, and reconcile actuators with metadata and ACL update reconcilers
  • Created status.go implementing ResourceStatusWriter for condition management and observed state
  • Created controller.go with RBAC markers, credential dependency handling, and manager setup
  • Registered controller in cmd/manager/main.go

Scope Integration (internal/scope/)

  • Added NewSwiftContainerClient method to Scope interface and provider implementation
  • Updated mock scope for testing

CI & Testing

  • Enabled Swift services (s-proxy, s-account, s-container, s-object) in .github/workflows/e2e.yaml
  • Created comprehensive KUTTL E2E tests: create-minimal, create-full, import, update, import-error, validation
  • Added unit tests for actuator operations and client operations

Documentation & Examples

  • Created example manifests in examples/swiftcontainer/: basic.yaml, full.yaml, import.yaml, import-filter.yaml
  • Updated README.md to include Swift container in supported resources table

Implementation Notes

  • Container identification: Swift containers use names (not UUIDs) as identifiers, so SwiftContainerImport uses name field instead of id unlike other ORC resources
  • Metadata key casing: Gophercloud returns metadata keys in Go's canonical HTTP header form (e.g., "Environment" not "environment"); the reconciler builds lowercase-keyed views (currentLower, currentCanonical) of the current metadata before comparison to avoid infinite reconcile loops; the canonical key is used when building the RemoveMetadata list to send correct X-Remove-Container-Meta- headers
  • Two-layer name validation: CRD pattern validation (^[^/]+$) is the primary enforcement for forward slash rejection; actuator-level validation provides defense-in-depth
  • 256-byte limit: kubebuilder MaxLength counts Unicode code points, not bytes; explicit byte-length validation added in actuator for multi-byte UTF-8 characters with message "container name must not exceed 256 bytes"
  • HasCustomImport flag: Added to resource-generator to support resources defining custom Import types
  • Filter predicate logic: When both Name and Prefix are set in ListOSResourcesForImport, the implementation guards against ignoring the prefix by returning early (yielding nothing) when the named container does not satisfy the prefix filter
  • DeleteResource pattern: Uses osResource.Name directly instead of calling getResourceName(orcObject), consistent with other ORC controllers that use the osResource ID for deletion
  • ACL status handling: The status.go checks that the joined ACL string is non-empty before setting status fields; gophercloud returns []string{""} for absent headers which would otherwise cause empty-string ACLs to appear in status
  • Swift ACL constraints: Swift rejects referrer-style ACLs (e.g., .r:*) in X-Container-Write headers with HTTP 400; only X-Container-Read supports referrer ACLs

Testing

  • Unit tests for SwiftContainerClient covering all CRUD operations, pagination, and error handling
  • Unit tests for actuator covering ListOSResourcesForImport, ListOSResourcesForAdoption, CreateResource, and name validation
  • KUTTL E2E tests covering: minimal creation (SC-001), full configuration (SC-002), import workflow (SC-003), update/revert, import error handling, and validation constraints
  • All tests pass: go test ./..., make generate, make manifests, make lint

Related Tickets


Generated by Forge SDLC Orchestrator

Forge added 28 commits June 21, 2026 10:59
…ith kubebuilder markers

Detailed description:
- Created api/v1alpha1/swiftcontainer_types.go with all required types:
  - SwiftContainerName custom type with MinLength=1, MaxLength=256, Pattern
    excluding forward slashes, and CEL XValidation for UTF-8 byte length
  - SwiftContainerMetadata struct for X-Container-Meta-* key-value pairs
  - SwiftContainerMetadataStatus struct for observed metadata state
  - SwiftContainerResourceSpec struct with fields: name (optional,
    immutable), metadata (list), containerRead, containerWrite, and
    storagePolicy (optional, immutable)
- Regenerated api/v1alpha1/zz_generated.deepcopy.go with DeepCopy functions
  for all new types using controller-gen
- Immutability enforced per-field via XValidation CEL rules (name,
  storagePolicy) following existing patterns from network_types.go and
  volume_types.go
- Field documentation follows existing conventions

Closes: AISOS-1560
…ith kubebuilder markers

Auto-committed by Forge container fallback.
Detailed description:
- Added SwiftContainerFilter struct with name (SwiftContainerName) and
  prefix (optional string) fields, with +kubebuilder:validation:MinProperties:=1
- Added SwiftContainerImport struct with name and filter fields (not id,
  since Swift containers are identified by name rather than UUID), with
  +kubebuilder:validation:MinProperties:=1 and MaxProperties:=1 markers
- Regenerated zz_generated.deepcopy.go to include DeepCopy/DeepCopyInto
  methods for both new types
- Follows existing patterns from NetworkFilter/NetworkImport and
  ShareNetworkFilter/ShareNetworkImport

Closes: AISOS-1561
Detailed description:
- Added SwiftContainerResourceStatus struct to api/v1alpha1/swiftcontainer_types.go
  with all required fields: name (MaxLength=256), bytesUsed (int64),
  objectCount (int64), metadata ([]SwiftContainerMetadataStatus, +listType=atomic),
  containerRead (string, optional, MaxLength=1024), containerWrite (string, optional,
  MaxLength=1024), storagePolicy (string, optional, MaxLength=1024), versions (string,
  optional, MaxLength=1024), quotaBytes (*int64, optional), quotaCount (*int64, optional)
- Regenerated api/v1alpha1/zz_generated.deepcopy.go with DeepCopyInto/DeepCopy
  methods for SwiftContainerResourceStatus (handles pointer fields QuotaBytes,
  QuotaCount and slice field Metadata)
- SwiftContainerMetadataStatus was already defined by a prior task (AISOS-1560)
- All string status fields have +kubebuilder:validation:MaxLength=1024 markers
- metadata slice has +listType=atomic marker
- Build verified: go build ./api/v1alpha1/... succeeds

Closes: AISOS-1562
…types

Detailed description:
- Added HasCustomImport flag to resource-generator to support resources
  that define their own Import type (SwiftContainerImport uses Name instead
  of UUID ID)
- Added SwiftContainer to resource-generator resources list
- Generated api/v1alpha1/zz_generated.swiftcontainer-resource.go with:
  - SwiftContainerSpec with all CEL validation rules matching network-resource pattern
  - SwiftContainerStatus with conditions, id (*string), and resource fields
  - SwiftContainer with proper kubebuilder markers (categories, subresource, printcolumns)
  - SwiftContainerList with GetItems() method
  - ObjectWithConditions interface (GetConditions) implemented
  - CloudCredentialsRefProvider interface (GetCloudCredentialsRef) implemented
  - Types registered with SchemeBuilder via init()
- Created internal/osclients/swiftcontainer.go with SwiftContainerClient
  interface and implementation using gophercloud objectstorage/v1/containers
- Generated all downstream artifacts: CRD YAML, deepcopy, client stubs,
  mock, informers, listers, adapter, controller stub

Closes: AISOS-1563
…types

Auto-committed by Forge container fallback.
Detailed description:
- PROJECT file already had SwiftContainer entry (added by AISOS-1563)
- Fixed api/v1alpha1/swiftcontainer_types.go:
  - Added omitempty to SwiftContainerMetadata.Key JSON tag (required by
    kubeapilinter requiredfields rule since MinLength=1 means zero value
    is disallowed)
  - Added +kubebuilder:validation:MaxItems:=64 to
    SwiftContainerResourceStatus.Metadata (required by kubeapilinter
    maxlength rule)
- Created internal/controllers/swiftcontainer/controller.go: defines
  controllerName, SetupWithManager, New() - needed because
  zz_generated.controller.go referenced undefined controllerName
- Created internal/controllers/swiftcontainer/actuator.go: implements
  swiftcontainerActuator with osContainerT wrapper type (GetHeader doesn't
  include container name), all CRUD operations using SwiftContainerClient
- Created internal/controllers/swiftcontainer/status.go: implements
  swiftcontainerStatusWriter with ResourceAvailableStatus/ApplyResourceStatus
- Updated internal/scope/scope.go: added NewSwiftContainerClient() to Scope
  interface
- Updated internal/scope/provider.go: added NewSwiftContainerClient() impl
- Updated internal/scope/mock.go: added SwiftContainerClient mock field and
  NewSwiftContainerClient() method
- Ran make generate and make manifests - both complete successfully
- CRD at config/crd/bases/openstack.k-orc.cloud_swiftcontainers.yaml updated
- All lint checks pass with golangci-kube-api-linter (0 issues)

Closes: AISOS-1564
Detailed description:
- Created examples/swiftcontainer/basic.yaml: minimal SwiftContainer CR
  matching the exact template from the task description
- Created examples/swiftcontainer/full.yaml: SwiftContainer with all
  resource options (custom name, metadata, read/write ACLs, storage policy)
- Created examples/swiftcontainer/import.yaml: import existing container
  by exact name using managementPolicy: unmanaged with import.name
- Created examples/swiftcontainer/import-filter.yaml: import existing
  container by prefix filter using managementPolicy: unmanaged with
  import.filter.prefix

All field names validated against CRD schema. YAML syntax verified.
Examples follow conventions from other config/samples/ YAML files.

Closes: AISOS-1565
…ntation

Detailed description:
- Updated SwiftContainerClient interface in internal/osclients/swiftcontainer.go to match spec:
  - DeleteContainer now returns just error (not *containers.DeleteHeader, error)
  - GetContainer now accepts opts containers.GetOptsBuilder parameter
  - Removed non-spec GetContainerMetadata method
  - Renamed parameters to use containerName convention for clarity
- Updated swiftContainerClient implementation methods accordingly
- Updated swiftContainerErrorClient methods to match new interface signatures
- Regenerated internal/osclients/mock/swiftcontainer.go with mockgen to reflect new interface
- Updated internal/controllers/swiftcontainer/actuator.go to pass nil opts to GetContainer
  and to not use discarded DeleteContainer return value

Closes: AISOS-1573
Detailed description:
- Added GetContainerMetadata(ctx, containerName) (map[string]string, error) to SwiftContainerClient interface
- Implemented method on swiftContainerClient using containers.Get().ExtractMetadata(), which strips the X-Container-Meta- prefix automatically via Gophercloud
- Added error stub on swiftContainerErrorClient returning (nil, error)
- Updated mock/swiftcontainer.go with GetContainerMetadata mock method and recorder

Closes: AISOS-1576
Detailed description:
- Created internal/osclients/swiftcontainer_test.go with 10 test cases covering
  all SwiftContainerClient interface methods
- Added NewSwiftContainerClientFromServiceClient() to swiftcontainer.go to allow
  injection of a fake gophercloud.ServiceClient for testing; refactored
  NewSwiftContainerClient() to use it
- Tests use net/http/httptest to simulate a real Swift server without requiring
  OpenStack credentials
- TestListContainers_Pagination: verifies marker-based pagination yields all items
- TestListContainers_Error: uses text/plain 200 response to trigger ExtractInfo
  failure and verify error propagation through the iterator's yieldPage path
- TestCreateContainer_Success: verifies X-Container-Read, X-Container-Write,
  and X-Container-Meta-* headers are sent correctly
- TestGetContainer_Success: verifies header values are parsed into GetHeader fields
- TestGetContainerMetadata_Success: verifies X-Container-Meta- prefix stripping
- TestUpdateContainer_ACLs: verifies POST with correct ACL headers
- TestDeleteContainer_Success: verifies DELETE request to correct path
- TestSwiftContainerErrorClient: verifies all 6 interface methods return the error

Closes: AISOS-1577
Detailed description:
- Added reconcileResourceActuator and resourceReconciler type aliases to
  actuator.go to support the ReconcileResourceActuator interface
- Added interface assertion: var _ reconcileResourceActuator = swiftcontainerActuator{}
- Implemented GetResourceReconcilers() returning [reconcileACLs, reconcileMetadata]
- Implemented reconcileACLs(): compares GetHeader.Read/Write ([]string joined
  as CSV) with spec ContainerRead/ContainerWrite (*string); calls UpdateContainer
  with UpdateOpts{ContainerRead, ContainerWrite} when ACLs differ
- Implemented reconcileMetadata(): fetches current metadata via
  GetContainerMetadata(), lowercases keys for comparison with spec metadata list,
  calls UpdateContainer with UpdateOpts{Metadata, RemoveMetadata} when changes
  are needed (new/modified keys go to Metadata, deleted keys go to RemoveMetadata)
- Both reconcilers return progress.NeedsRefresh() after a successful update and
  wrap non-retryable errors with orcerrors.Terminal()
- Added 'strings' and 'logging' imports

Key implementation notes:
- GetHeader.Read/Write are []string (gophercloud parses comma-separated ACL
  headers into slices); must join with ',' before comparing with spec *string
- GetContainerMetadata() returns map with lowercased keys (gophercloud
  ExtractMetadata behavior); spec keys are lowercased before comparison

Closes: AISOS-1592
…fields

Detailed description:
- Extended osContainerT struct in actuator.go to include Metadata map[string]string
  field, populated via GetContainerMetadata() in all code paths that construct the
  struct (GetOSResourceByID, ListOSResourcesForAdoption, ListOSResourcesForImport,
  CreateResource)
- Updated ApplyResourceStatus() in status.go to populate all observed fields:
  - containerRead: from GetHeader.Read ([]string joined with ',')
  - containerWrite: from GetHeader.Write ([]string joined with ',')
  - metadata: from osContainerT.Metadata map (sorted by key for deterministic SSA patches)
- All pre-existing fields (name, bytesUsed, objectCount, storagePolicy, versions)
  were already correctly set

The containerRead/containerWrite fields are available directly from GetHeader as
[]string slices (gophercloud parses the comma-separated ACL header values).
Container metadata requires a separate API call via ExtractMetadata(), so it is
fetched eagerly and stored in osContainerT alongside the header.

Closes: AISOS-1593
Detailed description:
- Added import for swiftcontainer package in cmd/manager/main.go
- Added swiftcontainer.New(scopeFactory) to the controllers slice in main()
- controller.go was already fully implemented in AISOS-1564 with:
  - controllerName constant as "swiftcontainer"
  - RBAC markers for swiftcontainers and swiftcontainers/status
  - credentialsDependency (defined in zz_generated.controller.go)
  - swiftcontainerReconcilerConstructor implementing interfaces.Controller
  - SetupWithManager with credential watches and reconciler.NewController

Closes: AISOS-1594
Auto-committed by Forge container fallback.
Detailed description:
- Created internal/controllers/swiftcontainer/actuator_test.go with 17 test cases
- Defined mockSwiftContainerClient implementing osclients.SwiftContainerClient interface
- Mock uses containerData map (name -> GetHeader + metadata) for simulated container state
- Missing containers return gophercloud.ErrResourceNotFound (treated as 404 by orcerrors.IsNotFound)
- Test helpers: containerResult, checkFunc, checks(), noError, wantError, findsN, findsID
- TestListOSResourcesForImport: name-based import, not-found, prefix filter, list errors
- TestListOSResourcesForAdoption: explicit name, object name fallback, not-found, metadata pass-through, canAdopt=false for nil spec.resource
- TestCreateResource: minimal config, full config, terminal error for slash in name, terminal error for 257-byte name, terminal error for nil spec.resource
- TestContainerNameValidation: slash rejection, 257-byte rejection, 256-byte acceptance

Closes: AISOS-1596
Detailed description:
- Added s-proxy, s-account, s-container, s-object to enabled_services in
  .github/workflows/e2e.yaml devstack-action configuration
- Swift is a core OpenStack service that does not require additional plugins
  (unlike neutron/manila which need enable_plugin entries)
- All four Swift services are required for full object storage functionality:
  s-proxy (API gateway), s-account, s-container, s-object (storage layers)

Closes: AISOS-1605
Detailed description:
- Created internal/controllers/swiftcontainer/tests/ directory
- Added .gitkeep to track the empty directory in git
- kuttl-test.yaml already contains the swiftcontainer test directory entry
  (./internal/controllers/swiftcontainer/tests/) from a prior code-generation run

Closes: AISOS-1606
…(SC-001)

Detailed description:
- Created README.md documenting test purpose, steps, and SC-001 reference
- Created 00-secret.yaml to create OpenStack credentials secret (pattern from volume tests)
- Created 00-create-resource.yaml with SwiftContainer CR using empty resource spec and managementPolicy: managed
- Created 00-assert.yaml asserting Available=True/Success, Progressing=False/Success, status.id=container name, status.resource.name=metadata.name; CEL expressions verify id is non-empty and equals CR name, and optional fields (metadata, ACLs) are absent
- Created 01-delete-secret.yaml to delete credentials secret with --wait=false
- Created 01-assert.yaml asserting secret has deletionTimestamp and openstack.k-orc.cloud/swiftcontainer finalizer

Key implementation decisions:
- Container name defaults to CR metadata.name ('swiftcontainer-create-minimal') since spec.resource.name is omitted
- status.id is the container name (Swift containers use name as ID, not UUID)
- CEL expression 'swiftcontainer.status.id == swiftcontainer-create-minimal' verifies the name-defaulting behavior
- Finalizer name 'openstack.k-orc.cloud/swiftcontainer' matches GetFinalizerName(controllerName) pattern
- Follows volume-create-minimal test pattern exactly for secret dependency validation

Closes: AISOS-1607
Detailed description:
- Created README.md documenting the test purpose and what it validates
- Created 00-secret.yaml to set up OpenStack credentials secret
- Created 00-create-resource.yaml with SwiftContainer CR using full spec:
  - spec.resource.name set to explicit override name (swiftcontainer-create-full-override)
  - spec.resource.metadata with two key-value pairs (environment/owner)
  - spec.resource.containerRead set to ".r:*" for public read
  - spec.resource.containerWrite set to ".r:*"
- Created 00-assert.yaml verifying:
  - status.resource.name reflects the explicit override name (not metadata.name)
  - status.id equals the override name
  - containerRead and containerWrite ACLs are present in status.resource
  - metadata key-value pairs appear in status.resource.metadata with Go's
    HTTP canonical header casing (Environment, Owner)
  - Available=True and Progressing=False conditions are set

Closes: AISOS-1608
Detailed description:
- Created internal/controllers/swiftcontainer/tests/swiftcontainer-import/README.md
  documenting the 3-step import workflow and SC-003 scenario
- Created 00-secret.yaml: OpenStack credentials secret creation step
- Created 00-import-resource.yaml: SwiftContainer CR with managementPolicy: unmanaged
  and import.filter.name: swiftcontainer-import-external (waiting for external container)
- Created 00-assert.yaml: Asserts import is in progress (Progressing=True, Available=False)
- Created 01-create-trap-resource.yaml: Managed SwiftContainer named
  swiftcontainer-import-external-not-this-one (superstring of filter name, trap pattern)
- Created 01-assert.yaml: Asserts trap container is available and import CR is
  still waiting (filter specificity validation)
- Created 02-create-resource.yaml: Managed SwiftContainer named
  swiftcontainer-import-external (exact match of filter name)
- Created 02-assert.yaml: Asserts import CR is now Available with status.id matching
  the imported container; CEL assertions verify IDs of the two containers differ and
  that the import CR adopted the correct one

Follows the standard import test pattern from flavor-import and volume-import:
Step 00 creates unmanaged import CR, Step 01 creates trap resource, Step 02 creates
the actual matching resource and verifies adoption.

Closes: AISOS-1609
Detailed description:
- Created 8 test files in internal/controllers/swiftcontainer/tests/swiftcontainer-update/
- README.md: Documents the 3-step update/revert workflow
- 00-prerequisites.yaml: Creates openstack-clouds secret (TestStep)
- 00-minimal-resource.yaml: SwiftContainer CR with minimal config (resource: {})
- 00-assert.yaml: Asserts container created with no metadata/ACLs via CEL
- 01-updated-resource.yaml: Adds metadata (environment/team keys) and sets read/write ACLs (.r:*)
- 01-assert.yaml: Asserts metadata and ACLs reflected in status using CEL expressions
- 02-reverted-resource.yaml: Uses kubectl replace to revert to minimal config
- 02-assert.yaml: Asserts reverted state matches initial creation state

Key implementation details:
- Follows the standard 3-step update pattern from other controllers (volume, network, etc.)
- Uses kubectl replace (not kuttl patch) for step 02 to allow field removal
- Metadata keys are asserted with Go HTTP canonical casing (Environment, Team) since Swift API returns headers in canonical form
- CEL expressions used for negative assertions (!has(...)) to verify field absence
- ACL assertions use both inline YAML status matching and CEL for robust verification

Closes: AISOS-1610
Detailed description:
- Created internal/controllers/swiftcontainer/tests/swiftcontainer-import-error/README.md:
  Documents the import error scenario (prefix filter matching multiple containers)
- Created 00-secret.yaml: Creates openstack-clouds secret via TestStep command
- Created 00-create-resources.yaml: Creates two managed SwiftContainers with
  names sharing the 'swiftcontainer-import-error-external' prefix
- Created 00-assert.yaml: Asserts both managed containers become Available
- Created 01-import-resource.yaml: Unmanaged SwiftContainer using
  import.filter.prefix to match both containers
- Created 01-assert.yaml: Asserts Available=False and Progressing=False with
  reason InvalidConfiguration and message 'found more than one matching
  OpenStack resource during import'

The test verifies that when a prefix filter matches multiple Swift containers,
the controller sets a terminal InvalidConfiguration error rather than
arbitrarily choosing one, matching the framework behavior in
internal/controllers/generic/reconciler/resource_actions.go.

Closes: AISOS-1611
…name constraints

Detailed description:
- Added explicit slash validation in actuator.go CreateResource(): checks for
  '/' in container name before calling Swift API, returns a terminal error with
  message 'container name must not contain forward slashes' for defense-in-depth
  (CRD pattern ^[^/]+$ is the primary enforcement, controller check is secondary)
- Updated actuator_test.go: updated test comments to reflect actuator-level
  check; simplified 'slash' test cases to use plain mockSwiftContainerClient
  (since actuator catches '/' before calling client); added assertion that the
  error message contains 'forward slashes'
- Created KUTTL test directory:
  internal/controllers/swiftcontainer/tests/swiftcontainer-validation/
  - README.md: Documents two-layer validation approach and test design
  - 00-secret.yaml: Creates openstack-clouds secret (same as other tests)
  - 00-valid-resource.yaml: Minimal SwiftContainer to verify controller works
  - 00-assert.yaml: Asserts valid container becomes Available=True
  - 01-invalid-slash.yaml: TestStep command that attempts to create a container
    with 'invalid/slash/name' via JSON+kubectl; uses ignoreFailure=true because
    the CRD pattern rejects the resource at the API level
  - 01-assert.yaml: Asserts valid container remains Available=True and invalid
    container does NOT exist in cluster (API rejected it)

The test validates the no-forward-slash constraint via two mechanisms:
1. The KUTTL test confirms the CRD validation rejects the invalid resource
2. Unit tests in actuator_test.go confirm controller-level validation catches
   slashes that bypass CRD validation (defense-in-depth)

Closes: AISOS-1612
Detailed description:
- Fix metadata key case comparison bug in reconcileMetadata that caused
  an infinite reconcile loop: GetContainerMetadata returns keys in Go
  canonical HTTP header form (e.g. "Env") but the code was comparing
  them against lowercased desired-metadata keys (e.g. "env"). Keys never
  matched, so metadata was re-updated on every reconcile. Fixed by
  building lowercase-keyed views (currentLower, currentCanonical) of the
  current metadata before comparison; canonical key is used when building
  the RemoveMetadata list to send correct X-Remove-Container-Meta-<Key>
  headers.

- Fix forward slash validation message: change "container name must not
  contain forward slashes ('/"')" to match spec exactly as
  "container name must not contain forward slashes".

- Add explicit 256-byte name length validation: kubebuilder MaxLength:=256
  counts Unicode code points, not bytes; a name with multi-byte UTF-8
  characters could pass API validation but exceed Swift's 256-byte
  limit. Now validated explicitly with message "container name must not
  exceed 256 bytes" before calling the Swift API.

- Fix misleading comment in swiftcontainer_test.go that incorrectly
  stated gophercloud lowercases metadata keys.

Closes: AISOS-1424-review
Auto-committed by Forge container fallback.
Detailed description:
- Added 'swift container' to the Supported OpenStack resources table in README.md
- The SwiftContainer controller was added in this branch (registered in cmd/manager/main.go)
  but was missing from the supported resources table
- Marked as partially implemented (◐) in main branch only, consistent with other
  newly-added controllers like share network

Closes: AISOS-1424-docs

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The PR adds manager at ~97 MB and resource-generator at ~4.4 MB as executable blobs. These are build outputs, platform-specific, and should not be in the source diff. They should be removed.

Comment thread internal/controllers/swiftcontainer/actuator.go
- Remove committed binary blobs (manager ~97MB, resource-generator ~4.4MB)
  from git tracking via git rm --cached; add /manager and /resource-generator
  to .gitignore so they cannot be accidentally committed again.

- Fix SwiftContainerFilter predicate logic: when both Name and Prefix are
  set in ListOSResourcesForImport, the prefix was silently ignored in the
  name-lookup branch. Add a guard that returns early (yielding nothing) when
  the named container does not satisfy the prefix filter.

- Add unit tests covering: (1) name set + non-matching prefix → 0 results,
  (2) name set + matching prefix → 1 result.
…Name instead of getResourceName

Detailed description:
- internal/controllers/swiftcontainer/actuator.go: Changed DeleteResource to use
  osResource.Name directly instead of calling getResourceName(orcObject), which
  would dereference orcObject.Spec.Resource.Name and panic if Spec.Resource is nil.
- Using osResource.Name is the correct pattern (consistent with all other ORC
  controllers that use the osResource ID for deletion) and avoids a potential
  runtime crash if the object's resource spec is nil at deletion time.

Closes: AISOS-1424-review-review-impl
@github-actions github-actions Bot added the semver:major Breaking change label Jun 22, 2026

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

need to fix linting and regenerate files

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

please re-run make generate again

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

please re-run make generate again

Forge added 2 commits June 22, 2026 11:31
Detailed description:
- Create config/samples/openstack_v1alpha1_swiftcontainer.yaml to fix
  missing sample file referenced in kustomization.yaml, which caused
  kustomize to fail during make generate
- Add SwiftContainer entry to config/manifests/bases/orc.clusterserviceversion.yaml
  between Subnet and Trunk entries, fixing verify-generated CI check
- Fix swiftcontainer-create-full e2e test: remove invalid .r:* containerWrite
  ACL (Swift rejects referrer-style ACLs in X-Container-Write with HTTP 400);
  update containerRead to .r:*,.rlistings and remove all containerWrite assertions
- Fix status.go: check that joined ACL string is non-empty before setting
  status field; gophercloud returns []string{""} for absent headers which
  previously caused empty-string ACL to appear in status, failing !has() assertions
  in swiftcontainer-create-minimal and swiftcontainer-update tests

Closes: AISOS-1424-ci-fix
…d write ACL

The swiftcontainer-update test step 01 set containerWrite: '.r:*', which
Swift rejects with HTTP 400 ('Referrers not allowed in write ACL'). Since
IsRetryable treats 400 as transient, the controller retried indefinitely
and the test timed out.

Fix:
- Remove containerWrite from 01-updated-resource.yaml
- Update containerRead to '.r:*,.rlistings' (a valid read ACL with rlistings)
  so the update step still exercises a meaningful field change
- Update 01-assert.yaml to match: remove containerWrite assertions,
  update containerRead CEL expression and inline status to new value
- Update README.md to remove references to containerWrite in step 01

Closes: AISOS-1424-ci-fix
@eshulman2 eshulman2 merged commit 05fdb64 into eshulman2:main Jun 23, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:major Breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants