Documentation
¶
Overview ¶
Package providerclaims is the one answer to "which permission claims does a tenant's APIBinding to a provider's export carry".
A provider declares what it needs once, in CatalogEntry.spec.requires; codegen stamps the same list onto its APIExport. kcp, however, acts only on the claims the tenant's own binding lists, and the binding is written by the hub's Enable flow — once. This package holds the policy that keeps the two in step, and the reconciler that applies it whenever either side changes, so a provider that grows a requirement does not need every tenant to re-enable it.
The policy, in one place:
- A requirement that names no provider (a platform builtin, a core kind) is the provider's own machinery; the tenant consented to the provider, so the hub accepts it on their behalf.
- A requirement that names another provider is a composition: a claim on somebody else's kinds. It is Accepted only where the workspace's Grant records that consent, and Rejected otherwise — never silently granted.
- A claim the binding already holds Rejected stays Rejected (the writer in pkg/hub/kcp enforces this), and an existing claim keeps its selector.
Index ¶
- func ConsentedCompositions(grant *tenancyv1alpha1.Grant) map[string]bool
- func Desired(prov providers.Provider, grant *tenancyv1alpha1.Grant) []kcp.ProviderClaim
- func Key(group, resource string) string
- func RequiredClaims(requires []providersv1alpha1.ProviderRequirement, ...) []kcp.ProviderClaim
- func SetupWithManager(mgr mcmanager.Manager, clusters ClusterNamer, ops BindingOps, ...) error
- type BindingOps
- type ClusterNamer
- type GrantReader
- type ProviderLookup
- type Reconciler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConsentedCompositions ¶
func ConsentedCompositions(grant *tenancyv1alpha1.Grant) map[string]bool
ConsentedCompositions reads the compositions a Grant records as accepted, keyed by Key(group, resource).
func Desired ¶
func Desired(prov providers.Provider, grant *tenancyv1alpha1.Grant) []kcp.ProviderClaim
Desired is the claim set the hub keeps on a tenant's binding to prov's export: every requirement the provider declares, with the provider's own requirements Accepted and compositions Accepted only where grant records the workspace's consent. grant may be nil (no consent recorded).
Own requirements are accepted for platform and org-owned providers alike: an org-owned provider is registered by the Org's own admins, who already hold admin in every workspace of that Org, so a claim they declare widens nothing they could not grant directly. AGENTS.md §5.1 records that decision.
A composition the grant consents to is marked Consented, so the merge in pkg/hub/kcp knows the Rejected it may be replacing was the hub's own placeholder, not a tenant's decision.
func Key ¶
Key identifies a claim by what identifies it on a binding: group and resource. Verbs and selectors ride along with the declaration.
func RequiredClaims ¶
func RequiredClaims(requires []providersv1alpha1.ProviderRequirement, isAccepted func(providersv1alpha1.RequiredCoordinate) bool) []kcp.ProviderClaim
RequiredClaims renders spec.requires as the binding's permission claims, one per declared coordinate, Accepted as isAccepted says. These are the same claims provider-sdk/cmd/apiexportgen puts on the generated APIExport, so the binding lists exactly what the export declares.
A verb coordinate ("instances/exec") is claimed whole: the verb IS the capability, and which HTTP method it uses — which is what kcp maps onto an RBAC verb — is the serving provider's transport detail, so the claim spells every verb.
func SetupWithManager ¶
func SetupWithManager(mgr mcmanager.Manager, clusters ClusterNamer, ops BindingOps, registry ProviderLookup, grants GrantReader) error
SetupWithManager wires the reconciler into mgr. clusters is the manager's multicluster provider (its engaged cluster set), grants may be nil.
Types ¶
type BindingOps ¶
type BindingOps interface {
// EnsureCoreBindingClaims brings the hub's own `railgrid` binding in the
// cluster to the embedded core export's claim set.
EnsureCoreBindingClaims(ctx context.Context, clusterName, bindingName string) (bool, error)
// ReconcileProviderBindingClaims merges claims onto a provider binding
// under the rules documented on kcp.Bootstrapper.ReacceptProviderAPIBindingClaims.
ReconcileProviderBindingClaims(ctx context.Context, clusterName, bindingName string, claims []kcp.ProviderClaim) (bool, error)
// ResolveClusterPath returns a logical cluster's workspace path.
ResolveClusterPath(ctx context.Context, clusterName string) (string, error)
}
BindingOps is the slice of the kcp Bootstrapper the reconciler writes through. Reads come from the core.railgrid.ai virtual workspace, which serves APIBindings read-only; every write goes to the workspace by its logical cluster with the hub's own credential.
type ClusterNamer ¶
type ClusterNamer interface {
ClusterNames() []multicluster.ClusterName
}
ClusterNamer lists the logical clusters a multicluster provider currently engages. apiexportprovider.Provider.Clusters satisfies it.
type GrantReader ¶
type GrantReader interface {
Get(ctx context.Context, key hubaccess.GrantKey) (*tenancyv1alpha1.Grant, error)
}
GrantReader reads the consent a workspace recorded for a provider. *hubaccess.Store satisfies it; nil means no grant store is wired and no composition is ever accepted by this reconciler.
type ProviderLookup ¶
type ProviderLookup interface {
Get(name string) (providers.Provider, bool)
GetForOrg(orgUUID, name string) (providers.Provider, bool)
OnRequirementsChange(fn providers.RequirementsListener) (unregister func())
}
ProviderLookup is the registry view the reconciler needs: resolve the provider behind an export reference, and learn when requirements change.
type Reconciler ¶
type Reconciler struct {
// contains filtered or unexported fields
}
Reconciler keeps every tenant APIBinding's accepted permission claims in lockstep with what the bound export declares today.
It runs on the core.railgrid.ai multicluster manager: every tenant workspace binds that export, and the export claims apis.kcp.io/apibindings, so its virtual workspace serves every APIBinding in every tenant workspace that accepted the claim. Two kinds of binding are reconciled, and nothing else:
- the hub's own `railgrid` binding to core.railgrid.ai, brought to the embedded export's claim set. This is also how the apibindings claim itself reaches workspaces enabled before it existed: kcp serves an export's own bindings through its virtual workspace regardless of acceptance, so the reconciler sees `railgrid` first, accepts the claim, and only then sees the provider bindings beside it.
- a tenant's binding to a provider's export, brought to the claim set Desired derives from the provider's current spec.requires and the workspace's Grant.
Triggers are the binding events themselves and, through the registry's requirements listener, a CatalogEntry whose spec.requires changed, which fans out to every binding of that export the manager can see. There is no timer and no fleet walk: a workspace the manager has not engaged is one that does not hold the core binding, and so holds nothing to reconcile.
func (*Reconciler) Reconcile ¶
func (r *Reconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error)
Reconcile fetches the binding through the virtual workspace and hands it to reconcileBinding.