Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
aa24192
Add shared spaces to type
lennardrother Mar 9, 2026
e421d46
Add method to get shared spaces
lennardrother Mar 9, 2026
4f4903d
Add mock for GetSharedSpaceRelationships
lennardrother Mar 9, 2026
3b22954
Add diffSharedSpaces function
lennardrother Mar 10, 2026
92fc93e
Implement observe, create and update
lennardrother Mar 10, 2026
b92b949
Fix controller tests
lennardrother Mar 10, 2026
fe93765
Renaming
lennardrother Mar 11, 2026
f5f4304
align test setup with existing patterns
lennardrother Mar 13, 2026
5856113
Fix controller tests
lennardrother Mar 13, 2026
d66ee9b
Add controller tests
lennardrother Mar 13, 2026
18780d5
Add e2e tests
lennardrother Mar 16, 2026
38274f7
Add example CR
lennardrother Mar 16, 2026
25ae044
Update golangci-lint version and adjust linter settings
SatabdiG Mar 17, 2026
8a2f37e
Revert "Update golangci-lint version and adjust linter settings"
SatabdiG Mar 17, 2026
e8b6da7
Increase golangci-lint timeout to 10m and set concurrency to 2; enabl…
SatabdiG Mar 17, 2026
53c446c
Fix some linting issues
lennardrother Mar 17, 2026
605d357
Merge branch 'main' into feat/service-instance-sharing
lennardrother Mar 20, 2026
7330f65
Use .golangci.yml from main
lennardrother Mar 20, 2026
2ef5c5b
chore(serviceinstance): suppress gocyclo lint warning on Create
lennardrother Mar 20, 2026
482b330
refactor(serviceinstance): address PR review comments on sharing
lennardrother Mar 23, 2026
e31df4b
Merge branch 'main' into feat/service-instance-sharing
lennardrother Apr 9, 2026
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
4 changes: 4 additions & 0 deletions apis/resources/v1alpha1/serviceinstance_types.go

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

7 changes: 7 additions & 0 deletions apis/resources/v1alpha1/zz_generated.deepcopy.go

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

19 changes: 19 additions & 0 deletions apis/resources/v1alpha1/zz_generated.resolvers.go

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

18 changes: 18 additions & 0 deletions examples/serviceinstance/service_instance_shared.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: cloudfoundry.crossplane.io/v1alpha1
kind: ServiceInstance
metadata:
name: my-service-instance-shared
spec:
forProvider:
type: managed
name: my-service-instance-shared
spaceRef:
name: my-space
policy:
resolve: Always
servicePlan:
offering: alert-notification
plan: standard
sharedSpaces:
- spaceRef:
name: my-space-shared
24 changes: 24 additions & 0 deletions internal/clients/fake/serviceinstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ func (m *MockServiceInstance) Delete(ctx context.Context, guid string) (string,
return args.String(0), args.Error(1)
}

// GetSharedSpaceRelationships mocks ServiceInstance.GetSharedSpaceRelationships
func (m *MockServiceInstance) GetSharedSpaceRelationships(ctx context.Context, guid string) (*resource.ServiceInstanceSharedSpaceRelationships, error) {
args := m.Called(guid)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*resource.ServiceInstanceSharedSpaceRelationships), args.Error(1)
}

// ShareWithSpaces mocks ServiceInstance.ShareWithSpaces
func (m *MockServiceInstance) ShareWithSpaces(ctx context.Context, guid string, spaceGUIDs []string) (*resource.ServiceInstanceSharedSpaceRelationships, error) {
args := m.Called(guid, spaceGUIDs)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*resource.ServiceInstanceSharedSpaceRelationships), args.Error(1)
}

// UnShareWithSpaces mocks ServiceInstance.UnShareWithSpaces
func (m *MockServiceInstance) UnShareWithSpaces(ctx context.Context, guid string, spaceGUIDs []string) error {
args := m.Called(guid, spaceGUIDs)
return args.Error(0)
}

// PollComplete mocks ServiceInstance.PollComplete
func (m *MockServiceInstance) PollComplete(ctx context.Context, job string, opt *client.PollingOptions) error {
args := m.Called()
Expand Down
95 changes: 95 additions & 0 deletions internal/clients/serviceinstance/serviceinstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type ServiceInstance interface {
CreateUserProvided(context.Context, *resource.ServiceInstanceUserProvidedCreate) (*resource.ServiceInstance, error)
UpdateUserProvided(context.Context, string, *resource.ServiceInstanceUserProvidedUpdate) (*resource.ServiceInstance, error)
Delete(context.Context, string) (string, error)
GetSharedSpaceRelationships(context.Context, string) (*resource.ServiceInstanceSharedSpaceRelationships, error)
ShareWithSpaces(context.Context, string, []string) (*resource.ServiceInstanceSharedSpaceRelationships, error)
UnShareWithSpaces(ctx context.Context, guid string, spaceGUIDs []string) error
}

// Job defines interfaces to async operations/jobs.
Expand Down Expand Up @@ -338,3 +341,95 @@ func IsUpToDate(in *v1alpha1.ServiceInstanceParameters, observed *resource.Servi
}
return true
}

// AreSharedSpacesUpToDate checks if the shared spaces of a service instance are in sync with the CR
func (c *Client) AreSharedSpacesUpToDate(ctx context.Context, guid string, desired []v1alpha1.SpaceReference) (bool, error) {
currentGUIDs, err := c.getCurrentSharedSpaces(ctx, guid)
if err != nil {
return false, err
}
desiredGUIDs := getDesiredSharedSpaces(desired)

toAdd, toRemove := diffSharedSpaces(currentGUIDs, desiredGUIDs)
return len(toAdd) == 0 && len(toRemove) == 0, nil
}

// UpdateSharedSpaces updates the shared spaces of a service instance to keep them in sync with the CR
func (c *Client) UpdateSharedSpaces(ctx context.Context, guid string, desired []v1alpha1.SpaceReference) error {
currentGUIDs, err := c.getCurrentSharedSpaces(ctx, guid)
if err != nil {
return err
}
desiredGUIDs := getDesiredSharedSpaces(desired)

toAdd, toRemove := diffSharedSpaces(currentGUIDs, desiredGUIDs)
if len(toAdd) > 0 {
_, err := c.ShareWithSpaces(ctx, guid, toAdd)
if err != nil {
return errors.Wrap(err, "cannot share service instance with spaces")
}
}
if len(toRemove) > 0 {
err := c.UnShareWithSpaces(ctx, guid, toRemove)
if err != nil {
return errors.Wrap(err, "cannot unshare service instance from spaces")
}
}

return nil
}

// getCurrentSharedSpaces retrieves the GUIDs of spaces a service instance is shared with
func (c *Client) getCurrentSharedSpaces(ctx context.Context, guid string) ([]string, error) {
relationships, err := c.GetSharedSpaceRelationships(ctx, guid)
Comment thread
SatabdiG marked this conversation as resolved.
if err != nil {
return nil, errors.Wrap(err, "cannot get shared space relationships")
}
if relationships == nil {
return []string{}, nil
}

spaceGUIDs := make([]string, 0, len(relationships.Data))
for _, rel := range relationships.Data {
spaceGUIDs = append(spaceGUIDs, rel.GUID)
}
return spaceGUIDs, nil
}

// getDesiredSharedSpaces extracts space GUIDs from a list of SpaceReference
func getDesiredSharedSpaces(refs []v1alpha1.SpaceReference) []string {
guids := make([]string, 0, len(refs))
for _, ref := range refs {
if ref.Space != nil && *ref.Space != "" {
guids = append(guids, *ref.Space)
}
}
return guids
}

// diffSharedSpaces compares the current and desired shared spaces and returns the spaces to add and remove to match the desired state
func diffSharedSpaces(current, desired []string) (toAdd, toRemove []string) {
currentSet := make(map[string]struct{}, len(current))
for _, guid := range current {
currentSet[guid] = struct{}{}
}

// Find spaces to add (in desired but not in current)
for _, guid := range desired {
if _, exists := currentSet[guid]; !exists {
toAdd = append(toAdd, guid)
}
// Mark as seen by deleting from set
delete(currentSet, guid)
}

// Remaining spaces in currentSet need to be removed
if len(currentSet) > 0 {
toRemove = make([]string, 0, len(currentSet))
for guid := range currentSet {
toRemove = append(toRemove, guid)
}
}

return toAdd, toRemove
}
Loading
Loading