flipt-io / cup

Git Contribution Automation
https://cup.flipt.io
Apache License 2.0
76 stars 1 forks source link

Implement API Server #12

Closed GeorgeMac closed 1 year ago

GeorgeMac commented 1 year ago

The API Server will be the HTTP entrypoint to cup.

Initially, it will handle two core competancies:

  1. List the available resource definitions per configured repository
  2. Expose each available resource type as its own prefixed section of the API

Resource Definitions API

This section of the API is for discovery of which definitions have been registered with cup. It should be possible to list all the groups, versions and kinds available per target repository.

All resource definitions across all sources:

/apis

All resources definitions for a particular source:

/apis/<repository>

Resource APIs

Each repo / resource definition combo will have its own prefix in the form:

/apis/<repository>/<group>/<version>/namespaces/<namespace>/<plural>

Within this prefix we expose the four initial operations per namespace:

Get a single instance:

GET /apis/<repository>/<group>/<version>/namespaces/<namespace>/<plural>/<name>

List multiple instances:

GET /apis/<repository>/<group>/<version>/namespaces/<namespace>/<plural>

Put a single instance:

PUT /apis/<repository>/<group>/<version>/namespaces/<namespace>/<plural>/<name>

Delete a single instance:

DELETE /apis/<repository>/<group>/<version>/namespaces/<namespace>/<plural>/<name>

Dependencies

The API server will depend on a number of abstractions on which to perform its job.

The controller will encapsulate away details on how a FilesystemStore chooses to share details on the undelying filesystem. This will allow the implementations to switch between using fs.FS, a temporary real directory on disk and potentially the wazero filesystem abstraction in the future (when it becomes available). The APIServer doesn't really care, it just need to make sure the right controller gets the appropraite configuration from the relevant store when requested.

package controller

type FSConfig struct {
    fs fs.FS
    dir *string
    // sysfs.FS for wazero
}

func WithFS(fs.FS) containers.Option[FSConfig] {}

func WithDir(string) containers.Option[FSConfig] {}

Filesystem Store

The purpose of this abstraction is to materialize the relevant filesystem for a particular resource controller to operate over. There are two main methods for this abstraction. One for read operates and one transactional write operation method.

type FSFunc func(controller.FSConfig) error

type FilesystemStore interface {
    // View invokes the function provided with configuration for a controller
    // which leads to a read-only view when mounted as a filesystem
    View(_ context.Context, repository, revision string, fn FSFunc) error
    // Update invokes the function provided with configuration for a controller
    // which leads to a writeable filesystem being mounted and any changes
    // being packaged into either a pull request or an immediate local change (implementation dependent).
    Update(_ context.Context, repository, revision string, fn FSFunc) (*Result, error)
}

Resource Controller and Store

The ControllerStore abstraction encapsulates the details of sourcing a particular instance of a Controller by group name. The Controller abstracts away the details of any particular controller implementation itself from the APIServer.

type Controller interface {
    Get(_ context.Context, c *controller.GetRequest) (*cup.Resource, error)
    List(_ context.Context, c *controller.ListRequest) ([]*cup.Resource, error)
    Put(_ context.Context, c *controller.PutRequest) error
    Delete(_ context.Context, c *controller.DeleteRequest) error
}

type ControllerStore interface {
    Controller(_ context.Context, group string) (Controller, error)
}