Documentation
¶
Overview ¶
Package liquidapi provides a runtime library for servers and clients implementing the LIQUID protocol: <https://pkg.go.dev/github.com/sapcc/go-api-declarations/liquid>
- func Run() provides a full-featured runtime that handles OpenStack credentials, authorization, and more.
- type Client is a specialized gophercloud.ServiceClient for use in Limes and limesctl.
- The other functions in this package contain various numeric algorithms that are useful for LIQUID implementations.
Index ¶
- func AtLeastZero(x int) uint64
- func DistributeDemandFairly[K comparable](total uint64, demands map[K]liquid.ResourceDemandInAZ, balance map[K]float64) map[K]uint64
- func DistributeFairly[K comparable](total uint64, requested map[K]uint64) map[K]uint64
- func ForeachOptionTypeInLIQUID[T any](action func(...any) T) []T
- func RestrictToKnownAZs[T any](input map[liquid.AvailabilityZone][]T, allAZs []liquid.AvailabilityZone) map[liquid.AvailabilityZone][]T
- func Run(ctx context.Context, logic Logic, opts RunOpts) error
- func SaturatingSub[T interface{ ... }](lhs, rhs T) uint64
- type Client
- func (c *Client) ChangeCommitments(ctx context.Context, req liquid.CommitmentChangeRequest) (result liquid.CommitmentChangeResponse, err error)
- func (c *Client) GetCapacityReport(ctx context.Context, req liquid.ServiceCapacityRequest) (result liquid.ServiceCapacityReport, err error)
- func (c *Client) GetInfo(ctx context.Context) (result liquid.ServiceInfo, err error)
- func (c *Client) GetUsageReport(ctx context.Context, projectUUID string, req liquid.ServiceUsageRequest) (result liquid.ServiceUsageReport, err error)
- func (c *Client) PutQuota(ctx context.Context, projectUUID string, req liquid.ServiceQuotaRequest) (err error)
- type ClientOpts
- type Logic
- type RunOpts
- type State
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AtLeastZero ¶
AtLeastZero safely converts int values (which often appear in Gophercloud types) to uint64 by clamping negative values to 0.
func DistributeDemandFairly ¶
func DistributeDemandFairly[K comparable](total uint64, demands map[K]liquid.ResourceDemandInAZ, balance map[K]float64) map[K]uint64
DistributeDemandFairly is used to distribute cluster capacity or cluster-wide usage between different resources. Each tier of demand is distributed fairly (while supplies last).
Then anything not yet distributed is split according to the given balance numbers. For example, if balance = { "foo": 3, "bar": 1 }, then "foo" gets 3/4 of the remaining capacity, "bar" gets 1/4, and all other resources do not get anything extra.
func DistributeFairly ¶
func DistributeFairly[K comparable](total uint64, requested map[K]uint64) map[K]uint64
DistributeFairly takes a number of resource requests, as well as a total available capacity, and tries to fulfil all requests as fairly as possible.
If the sum of all requests exceeds the available total, this uses the <https://en.wikipedia.org/wiki/Largest_remainder_method>.
func ForeachOptionTypeInLIQUID ¶
ForeachOptionTypeInLIQUID calls action with every Option[] type that appears in the LIQUID API and returns a slice with the results. This is intended for use with the cmpopts.EquateComparable() function when using github.com/google/go-cmp/cmp.
func RestrictToKnownAZs ¶
func RestrictToKnownAZs[T any](input map[liquid.AvailabilityZone][]T, allAZs []liquid.AvailabilityZone) map[liquid.AvailabilityZone][]T
RestrictToKnownAZs takes a mapping of objects sorted by AZ, and moves all objects in unknown AZs into the pseudo-AZ "unknown".
The resulting map will have an entry for each known AZ (possibly a nil slice), and at most one additional key (the well-known value "unknown").
func Run ¶
Run spawns an HTTP server that serves the LIQUID API, using the provided Logic to answer requests.
It will connect to OpenStack using the standard OS_* environment variables. The provided credentials need to have enough access to execute all the API requests that the Logic type wants to do.
Incoming requests will be authorized using oslo.policy config loaded from a policy file at $LIQUID_POLICY_PATH. The following rules must be defined:
- "liquid:get_info"
- "liquid:get_capacity"
- "liquid:get_usage" (object parameter "project_uuid")
- "liquid:set_quota" (object parameter "project_uuid")
- "liquid:change_commitments"
Please refer to the documentation on type RunOpts for various other behaviors that this function provides.
func SaturatingSub ¶
SaturatingSub is like `lhs - rhs`, but never underflows below 0.
Types ¶
type Client ¶
type Client struct {
gophercloud.ServiceClient
}
Client provides structured access to a LIQUID API.
func NewClient ¶
func NewClient(client *gophercloud.ProviderClient, endpointOpts gophercloud.EndpointOpts, opts ClientOpts) (*Client, error)
NewClient creates a Client for interacting with a liquid.
func (*Client) ChangeCommitments ¶
func (c *Client) ChangeCommitments(ctx context.Context, req liquid.CommitmentChangeRequest) (result liquid.CommitmentChangeResponse, err error)
ChangeCommitments executes POST /v1/change-commitments.
func (*Client) GetCapacityReport ¶
func (c *Client) GetCapacityReport(ctx context.Context, req liquid.ServiceCapacityRequest) (result liquid.ServiceCapacityReport, err error)
GetCapacityReport executes POST /v1/report-capacity.
func (*Client) GetUsageReport ¶
func (c *Client) GetUsageReport(ctx context.Context, projectUUID string, req liquid.ServiceUsageRequest) (result liquid.ServiceUsageReport, err error)
GetUsageReport executes POST /v1/projects/:uuid/report-usage.
type ClientOpts ¶
type ClientOpts struct { // The service type of the liquid in the Keystone catalog. // Required if EndpointOverride is not given. ServiceType string // Skips inspecting the Keystone catalog and assumes that the liquid's API is // located at this base URL. Required if ServiceType is not given. EndpointOverride string }
ClientOpts contains additional options for NewClient().
type Logic ¶
type Logic interface { // Init is called once at the start of Run(). The logic can use this to // obtain its gophercloud.ServiceClient instances. Init(ctx context.Context, provider *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) error // BuildServiceInfo will be called once directly after Init(), and then // periodically (as configured in RunOpts). The previous ServiceInfo will be // served until this call returns, so it's not a big deal if this call takes // a long time. // // If any long-lived state is computed here and needs to be preserved, // in addition to the information stored in the ServiceInfo instance, // we recommend adding fields of type liquidapi.State to the Logic object. // Then, only fill these state slots immediately before returning success from BuildServiceInfo. BuildServiceInfo(ctx context.Context) (liquid.ServiceInfo, error) // These methods represent all the API endpoints of LIQUID that do actual work. // // The latest ServiceInfo is provided for reference. Only a shallow copy is // provided, so implementations must make sure to not edit the ServiceInfo in // order to uphold thread-safety. // // All these functions should not store long-lived state in the Logic object. // Long-lived state should only be computed and updated during BuildServiceInfo(). // // ReviewCommitmentChange() does not need to check the req.InfoVersion value. // The caller will already have done so. ScanCapacity(ctx context.Context, req liquid.ServiceCapacityRequest, serviceInfo liquid.ServiceInfo) (liquid.ServiceCapacityReport, error) ScanUsage(ctx context.Context, projectUUID string, req liquid.ServiceUsageRequest, serviceInfo liquid.ServiceInfo) (liquid.ServiceUsageReport, error) SetQuota(ctx context.Context, projectUUID string, req liquid.ServiceQuotaRequest, serviceInfo liquid.ServiceInfo) error ReviewCommitmentChange(ctx context.Context, req liquid.CommitmentChangeRequest, serviceInfo liquid.ServiceInfo) (liquid.CommitmentChangeResponse, error) }
Logic is the interface for types that implement the core logic of a liquid.
Besides Init, all methods may be called in parallel, so the implementation must make sure to protect shared data with mutexes. Note that gophercloud.ServiceClient instances are generally safe to use concurrently.
type RunOpts ¶
type RunOpts struct { // If set, the file at $LIQUID_CONFIG_PATH will be json.Unmarshal()ed into // the Logic instance to supply configuration to it, before Init() is called. TakesConfiguration bool // If set, when the runtime loads its oslo.policy from $LIQUID_POLICY_PATH, // YAML will be supported in addition to JSON. This is an explicit dependency // injection slot to allow the caller to choose their YAML library. YAMLUnmarshal func(in []byte, out any) error // How often the runtime will call BuildServiceInfo() to refresh the // ServiceInfo of the liquid. The zero value can be used for liquids with // static ServiceInfo; no polling will be performed then. ServiceInfoRefreshInterval time.Duration // How many HTTP requests may be served concurrently. If set, the runtime // will ensure that no more than that many calls of ScanCapacity, ScanUsage // or SetQuota are ongoing at any time. The default (0) imposes no limit. MaxConcurrentRequests int // (Required.) Where the HTTP server will listen by default, e.g. ":8080". // Can be overridden at runtime by setting $LIQUID_LISTEN_ADDRESS. DefaultListenAddress string // If set, the server will be run with TLS support. Can be overridden at // runtime by setting $LIQUID_TLS_CERT_PATH and $LIQUID_TLS_KEY_PATH. DefaultTLSCertificatePath string DefaultTLSPrivateKeyPath string }
RunOpts provides configuration to func Run().
type State ¶
type State[T any] struct { // contains filtered or unexported fields }
State contains data that is guarded by an RWMutex, such that the data cannot be accessed without using the mutex. A zero-initialized State contains a zero-initialized piece of data.
This is provided here for implementations of the Logic interface that compute state during BuildServiceInfo(). See documentation on type Logic.