owned this note
owned this note
Published
Linked with GitHub
---
title: KCP API Catalog
authors:
- "@dinhxuanvu"
- "@varshaprasad96"
reviewers:
- "@sttts"
- "@ncdc"
approvers:
- "@sttts"
- "@ncdc"
creation-date: 2022-08-09
last-updated: 2022-09-13
status: provisional
---
# kcp-api-catalog
## Release Signoff Checklist
- [ ] Enhancement is `implementable`
- [ ] Design details are appropriately documented from clear requirements
- [ ] Test plan is defined
- [ ] Graduation criteria for dev preview, tech preview, GA
## Summary
A library for kcp API catalog code.
The API Catalog provides publicly available interfaces (APIs, code, CLIs) that allows users to curate a collection of available APIs (`APIExport`) in the KCP environment.
### What is API Catalog?
Each available API is encapsulated in a new API called `CatalogEntry` that is created by the cluster admin/API provider in a specific workspace.
The `CatalogEntry` contains a list of `ExportReference` and each `ExportReference` links to a corresponding `APIExport`. This `1:N` pattern between `CatalogEntry` and `APIExport` facilitates variety of relationships between multiple `APIExport` such as dependencies or grouping.
In addition of `APIExport` identifier information, the `CatalogEntry` have additional fields such as `Icons` and `Description` to allow cluster admin/API provider to add more information to represent the API specification and information for UI/Console usage.
All of `CatalogEntry` in the same workspace is considered to be in the same group (catalog). The availability (available for binding) of a CatalogEntry depends on the location of the workspace and permissions to access the workspace. For example, if the catalog workspace is at `root` level, then the `CatalogEntry` in that catalog is usable for all tenants' workspaces. If the workspace belongs to a specific organization such as `root:redhat`, then it will only be accessible and usable to tenants under that `redhat` organization.
When API consumer wants to bind an available API from a `CatalogEntry`, an `APIBinding` is created with each `ExportReference` in `Exports`.
<!-- ## Motivation
-->
## Goals
### Current Goals
- Initial Catalog API spec to support optional information such as `Description` in the spec
- Link `CatalogEntry` API to singular or multiple `APIExport` using a list of `ExportReference` (name of the APIExport and workspace path)
- CLI command `bind` to create `APIBinding` from `ExportReference` in `CatalogEntry` assuming all necessary RBAC are granted
### Future Goals
- Replace `ExportReference` reference in `Exports` with a `IdentityHash` to avoid exposing `APIExport` name and location
- Create a RBAC generation/control mechanism to facilitate the full `APIBinding` usage such as permission request and maximum permission policy
- Catalog controller to perform validation and additional reconciliation
- More CLI commands
<!-- ## Non-Goals
## Proposal
### User Stories
### Risks and Mitigations -->
## Design Details
### CatalogEntry API
```
type CatalogEntry struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CatalogEntrySpec `json:"spec,omitempty"`
Status CatalogEntryStatus `json:"status,omitempty"`
}
type CatalogEntrySpec struct {
// references is a list of references to APIExports.
References []kcpv1alpha1.ExportReference `json:"references"`
// maximalPermissionPolicy allows for a service provider to set an upper bound on what is allowed
// for a consumer of this API. If the policy is not set, no upper bound is applied,
// i.e the consuming users can do whatever the user workspace allows the user to do.
//
// +optional
MaximalPermissionPolicy *kcpv1alpha1.MaximalPermissionPolicy `json:"maximalPermissionPolicy,omitempty"`
// icon is the representation data that can be displayed in UI
// +optional
Icon Icon `json:"icon,omitempty"`
// description is a human-readable message to describe the information regarding
// the capabilities and features that the API provides
// +optional
Description string `json:"description,omitempty"`
}
type Icon struct {
// data is the encoded icon data
Data []byte `json:"data"`
// mediaType is media type information for icon data
MediaType string `json:"mediaType"`
}
type CatalogEntryStatus struct {
// permissionClaims is a list of permissions requested by the API provider(s)
// for this catalog entry.
// +optional
PermissionClaims []kcpv1alpha1.PermissionClaim `json:"PermissionClaims,omitempty"`
// apiResources is the list of APIs that are provided by this catalog entry.
// +optional
APIResources []metav1.GroupResource `json:"apiResources,omitempty"`
// conditions is a list of conditions that apply to the CatalogEntry.
//
// +optional
Conditions conditionsv1alpha1.Conditions `json:"conditions,omitempty"`
}
```
### Export References
The `CatalogEntry` allows multiple `ExportReferences` which allows user to specify more than one APIExport in the same entry.
When a `CatalogEntry` is bound to a workspace, one `APIBinding` is created for each `ExportReference`. This `1:n` relationship between `CatalogEntry` and `APIExport` enables a group of `APIExport` with certain relationship such as dependencies can be specified in the same entry.
### Permission Policy
The permission information regarding `APIExport` that are included in the `CatalogEntry` will be collected and populated in the entry status for visibility.
The `MaximalPermissionPolicy` field is designed to allow users to specify a maximum permission policy that ensures all included `APIExport` in the `CatalogEntry` not exceed the permission safeguard that is put in place by the entry author. This feature is to encourage the minimal permission principle. As a result, permission claims are encouraged to be small and appropriately scoped.
### CatalogEntry Reconciliation
The Catalog API comes with a controller that reconciles any newly-created and updated `CatalogEntry`. For new entry, the controller will validate `ExportReference` to ensure it is valid as it should point to an existing and valid `APIExport`. The controller also validates all required permissions claims against maximum permission policy to ensure they do exceed the policy. Invalid `ExportReference` or permission claims policy result into `CatalogEntry` being marked invalid in the status condition and unable to be "binded" to other workspaces.
As a part of `CatalogEntry` binding process, an `APIBinding` is created for each `ExportReference` in the entry. The created `APIBinding` has a key-value label that establishes a link between the `APIBinding` and the `CatalogEntry`. This link will allow the controller to propogate changes from the entry to the user workspace if changes to th entry happen. The key for the label is `internal.catalog.kcp.dev/entry` and its value is hash `base62(sha224())` value of `<workspace>:<exportName>`.
When the entry is updated to add more `ExportReference`, the controller queries all existing `APIBinding` with this label to determine if additional `APIBinding` needs to be created. If an `ExportReference` is removed, the controller will add a condition in `APIBinding` status to indicate this `APIBinding` is no longer a part of entry and it is up to the users to take action regarding this removal change. The controller explitely avoids deleting `APIBinding` to avoid potential data loss.
## Open Questions
1. ~~Who will be responsible for creating Export? The producer workspace or controller?~~
2. ~~Can the provider specify UI descriptors directly in APIExport instead of CatalogEntry?~~
3. What would APIBindingReference look like (Do we need to add anything more to the existing APIBinding)? (Later. Out of scoped for now)
4. How would binding work with RBAC? (Later. Out of scoped for now)
5. ~~What is meant by Export replication with respect to sharding concept? We maybe missing something from this perspective.~~
- Answer: if the producer (APIResourceSchema, APIExport, Export) exists in shard123, and the consumer (APIBinding) is in shard456, we have to make sure the consumer can find the producer's data.
6. ~~What would be the visibility/discoverability of Export API?~~
- Answer: Export would be hidden to the consumer workspace, only visible to producer and controller.
7. Based on the current design we could understand this:
- APIExport -> created by producer in producer workspace
- CatalogEntry -> curated by the producer who wants to add their APIs to the catalog.
~~- Export -> **Who is responsible for creating this?** Is it the controller or the user itself. If it's the controller, then APIExport should have a field to indicate that it wants an Export to be created given not all APIExports need to be necessarily available to be added to the catalog.~~
8. ~~**Where would Export be created?** Same as APIExport workspace or in a separate workspace.~~
- ~~**If separate workspaces are needed to isolate APIExport and Export, then what is the permission model (RBAC) to grant users the access to create Export in a separate workspace?**~~
9. Where would the `CatalogEntry` reside? Would it be in the producer ws where APIExport resides, or in a separate workspace.
10. How do look up the `APIExport` using the hash (`APIExportKey`) in `CatalogEntry`? The controller will have no indication of the workspace where APIExport is and it needs to know the refernce to APIExport to be able to bind?
11. How do we link the `CatalogEntry` with `Catalog`? Can the `CatalogEntry` have an array of `Catalog` to which it belongs so that controller can look at it?
12. What do we need a controller for? In the current scenario:
- (Producer) creates one/many `CatalogEntry` in a workspace.
- (Consumer/Viewer) lists or binds to the `CatalogEntry`.
Isn't a cli interface sufficient to curate the CatalogEntries and let user bind to them? What would be the use of having an operator for catalogs?
13. What would be the user experience while binding to the API? When is the permission given to the consumer to bind to the API - is it before creating the APIBinding? Will the user be allowed to create APIBinding without rbac, or can the APIBinding be created and the user can request for RBAC. Is there any documentation around how APIBinding works currently?
14. How would garbbage collection of CalaogEntries work? If an APIExport is removed, who is responsible for deleting the CatalogEntry. Similarly, do we need validation that CatalogEntry references a valid APIExport?
15. What happens when - APIExport and CE are created producer, APIBinding is successful by consumer - when the consumer is using the APIs, what if producer deletes the APIExport.
## Alternatives
### APIExport
```
type APIExport struct
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec holds the desired state.
//
// +optional
Spec APIExportSpec `json:"spec,omitempty"`
// Status communicates the observed state.
//
// +optional
Status APIExportStatus `json:"status,omitempty"`
}
type APIExportSpec struct {
LatestResourceSchemas []string `json:"latestResourceSchemas,omitempty"`
Identity *Identity `json:"identity,omitempty"`
MaximalPermissionPolicy *MaximalPermissionPolicy `json:"maximalPermissionPolicy,omitempty"`
PermissionClaims []PermissionClaim `json:"permissionClaims,omitempty"`
}
```
### Export
```
type Export struct
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec holds the desired state.
// +required
Spec ExportSpec `json:"exportspec"`
}
type ExportSpec struct
// Spec holds the desired state.
// +required
Reference ExportReference `json:"exportreference"`
}
type ExportReference struct {
// group is the group of the referenced object.
//
// kubebuilder:validation:MinLength=1
// +required
Group string `json:"group"`
// resource is the resource of the referenced object.
//
// kubebuilder:validation:MinLength=1
// +required
Resource string `json:"resource"`
// workspace is the workspace the referenced object is in.
//
// If the workspace is empty, the referenced object is in the same workspace.
//
// kubebuilder:validation:MinLength=1
// It should be a hash not the name of the workspace:
// base62(sha256(<workspace>:<name>))
WorkspaceKey string `json:"workspacekey"`
// name is the name of the referenced object in the given workspace.
//
// The name is defaulted to the first segment of the object's name.
//
// +required
// kubebuilder:validation:MinLength=1
Name string `json:"name"`
}
```
### CatalogEntry
```
type CatalogEntry struct{
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Reference to an export
Reference Reference `json:"reference"`
// +optional
Icon []Icon `json:"icon,omitempty"`
// +optional
Description string `json:"description,omitempty"`
}
type Reference struct {
// Name of the export
Name string `json:"name"`
// Workspace of the export
Workspace string `json:"workspace"`
}
type Icon struct {
Data string `json:"base64data"`
MediaType string `json:"mediatype"`
}
```
<!-- ### Test Plan
### Graduation Criteria
## Implementation History
## Drawbacks -->