Skip to content

Conversation

@digivava
Copy link
Collaborator

@digivava digivava commented Dec 31, 2025

There are use cases where our other non-Operator consumers of the VSO client library want to use the VaultAuth and VaultConnection structs but do not have access to actual VaultAuth and VaultConnection resources. Currently, many of the existing caching client factory-related methods have dependencies on the actual resources existing in cluster.

This PR changes that, by allowing developers to designate the caching client factory (and thus, the client) as "standalone", meaning that it does not require VaultAuth or VaultConnection custom resources in the cluster.

PCI review checklist

  • I have documented a clear reason for, and description of, the change I am making.

  • If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.

  • If applicable, I've documented the impact of any changes to security controls.

    Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc.

- Add isStandalone boolean to cachingClientFactory and CachingClientFactoryConfig
- Modify computeClientCacheKey to accept isStandalone parameter
- When isStandalone=true: use content-based hashes of VaultAuth/VaultConnection specs
- When isStandalone=false: use K8s resource UIDs with strict 36-char validation
- Update all callers to pass isStandalone (defaults to false for VSO)
- GetStandalone uses m.isStandalone flag for content-based caching
@digivava digivava requested a review from a team as a code owner December 31, 2025 19:04
@digivava digivava marked this pull request as draft December 31, 2025 19:04
@digivava digivava marked this pull request as ready for review December 31, 2025 23:13
@digivava digivava requested review from benashz and hashiblaum January 1, 2026 04:19
Copy link
Collaborator

@benashz benashz left a comment

Choose a reason for hiding this comment

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

In general, I like the extension of the client factory support the non-api approach. Although I wonder if we should implement this feature as a standAloneClientFactory rather than extending the ClientFactory interface? For some reason that feels a bit clearer to me.

Comment on lines +179 to +181
authSpecHash := helpers.HashString(fmt.Sprintf("%+v", authObj.Spec))
connSpecHash := helpers.HashString(fmt.Sprintf("%+v", connObj.Spec))

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it make sense to validate the various Spec? Could the "repr" be empty or missing required values?

// Format: "authHash-1.connHash-1.providerUID"
// (generation is always 1 for standalone since we didn't fetch the auth and conn objects from a K8s resource)
input = fmt.Sprintf("%s-%d.%s-%d.%s",
authSpecHash, 1,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: is there a particular reason to interpolate the generation since it is always 1?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was just trying to match the format of the original client cache key, but I suppose it's not necessary!

if len(uid) != requireUIDLen {
errs = errors.Join(errs, fmt.Errorf("%w %d, must be %d", errorInvalidUIDLength, len(uid), requireUIDLen))
var input string
if isStandalone {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if you may have considered setting the UIDs from the Spec, before passing them into this function. That might obviate the need for the special casing here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought about it, but got a bit scared off by the "read-only" description of the UID field (since traditionally the field is set by the API server).

I like your idea about making this whole thing a standaloneClientFactory instead though; that may clean up all of this and also allow us to more definitively avoid breaking existing code.

Get(context.Context, ctrlclient.Client, ctrlclient.Object) (Client, error)
// GetStandalone obtains a client without requiring Kubernetes API access.
// Suitable for standalone usage where K8s resources are unavailable.
GetStandalone(context.Context, *secretsv1beta1.VaultAuth, *secretsv1beta1.VaultConnection) (Client, error)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if you had maybe considered implementing a StandAloneFactory rather than extending the interface?

encClientSetupTimeout time.Duration
// isStandalone indicates this factory will be used to create clients that cannot be validated against real K8s API resources.
// Useful for creating clients using VSO's caching client factory pattern in other use cases besides VSO.
isStandalone bool
Copy link
Collaborator

Choose a reason for hiding this comment

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

When isStandlone is true, does that mean the usual Get() method should not be used?

return nil, errs
}

cacheKey, err = computeClientCacheKey(authObj, connObj, provider.GetUID(), m.isStandalone)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am a bit confused by the meaning of isStandAlone member - is it legal to call GetStandAlone() when it is false?

@digivava
Copy link
Collaborator Author

Closing in favor of #1199

@digivava digivava closed this Jan 12, 2026
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.

2 participants