Documentation
¶
Index ¶
- Constants
- Variables
- func CheckCommitmentsDryRun(ctx context.Context, config E2EChecksConfig)
- func CheckCommitmentsInfoEndpoint(ctx context.Context, config E2EChecksConfig)
- func CheckCommitmentsMultiFlavorGroupBatch(ctx context.Context, config E2EChecksConfig)
- func CheckCommitmentsRoundTrip(ctx context.Context, config E2EChecksConfig)
- func DeleteChildReservations(ctx context.Context, k8sClient client.Client, cr *v1alpha1.CommittedResource) error
- func GetFlavorGroupAndTypeFromResource(resourceName string) (string, v1alpha1.CommittedResourceType, error)
- func GetFlavorGroupNameFromResource(resourceName string) (string, error)
- func GetMaxSlotIndex(reservations []v1alpha1.Reservation) int
- func GetNextSlotIndex(reservations []v1alpha1.Reservation) int
- func LogFlavorGroupResourceConfig(log logr.Logger, cfg map[string]FlavorGroupResourcesConfig)
- func LoggerFromContext(ctx context.Context) logr.Logger
- func ResourceNameCores(flavorGroup string) string
- func ResourceNameInstances(flavorGroup string) string
- func ResourceNameRAM(flavorGroup string) string
- func RunCommitmentsE2EChecks(ctx context.Context, config E2EChecksConfig)
- func ScanAZForPaygCandidates(ctx context.Context, k8sClient client.Client, vmSource reservations.VMSource, ...) (map[string][]PAYGCandidate, error)
- func WithGlobalRequestID(ctx context.Context, greq string) context.Context
- func WithNewGlobalRequestID(ctx context.Context) context.Context
- type APIConfig
- type ApplyResult
- type CRControllerMonitor
- type CapacityCalculator
- type Commitment
- type CommitmentReservationController
- func (r *CommitmentReservationController) Init(ctx context.Context, conf ReservationControllerConfig) error
- func (r *CommitmentReservationController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)
- func (r *CommitmentReservationController) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error
- type CommitmentState
- func FromChangeCommitmentTargetState(commitment liquid.Commitment, projectID string, domainID string, ...) (*CommitmentState, error)
- func FromCommitment(commitment Commitment, ramUnitMiB uint64) (*CommitmentState, error)
- func FromCommittedResource(cr v1alpha1.CommittedResource) (*CommitmentState, error)
- func FromReservations(reservations []v1alpha1.Reservation) (*CommitmentState, error)
- type CommitmentStateWithUsage
- type CommitmentsClient
- type CommittedResourceController
- type CommittedResourceControllerConfig
- type Config
- type DomainResolver
- type E2EChecksConfig
- type E2ERoundTripConfigdeprecated
- type Flavor
- type FlavorGroupResourcesConfig
- type PAYGCandidate
- type Project
- type RAMResourceTypeConfig
- type ReservationControllerConfig
- type ReservationManager
- type ReservationManagerConfig
- type ResourceTypeConfig
- type Server
- type SlotLimitExceededError
- type Syncer
- type SyncerConfig
- type SyncerMonitor
- func (m *SyncerMonitor) Collect(ch chan<- prometheus.Metric)
- func (m *SyncerMonitor) Describe(ch chan<- *prometheus.Desc)
- func (m *SyncerMonitor) RecordCRCreates(count int)
- func (m *SyncerMonitor) RecordCRDeletes(count int)
- func (m *SyncerMonitor) RecordCRUpdates(count int)
- func (m *SyncerMonitor) RecordCommitmentSkipped(reason string)
- func (m *SyncerMonitor) RecordDuration(seconds float64)
- func (m *SyncerMonitor) RecordError()
- func (m *SyncerMonitor) RecordStaleCRs(count int)
- func (m *SyncerMonitor) SetLimesCommitmentsActive(count int)
- type UsageCalculator
- type UsageReconciler
- type UsageReconcilerConfig
- type UsageReconcilerMonitor
- type VMUsageInfo
Constants ¶
const ( // Resource type suffixes ResourceSuffixRAM = "_ram" ResourceSuffixCores = "_cores" ResourceSuffixInstances = "_instances" )
Limes LIQUID resource naming convention: hw_version_<flavorgroup>_<resourcetype> Supported resource types: _ram, _cores, _instances
const ( SkipReasonUnitMismatch = "unit_mismatch" SkipReasonUnknownFlavorGroup = "unknown_flavor_group" SkipReasonInvalidResource = "invalid_resource_name" SkipReasonEmptyUUID = "empty_uuid" SkipReasonNonCompute = "non_compute" )
Skip reason labels for commitment processing
Variables ¶
var (
// CreatorValue identifies reservations created by this syncer.
CreatorValue = "commitments-syncer"
)
Functions ¶
func CheckCommitmentsDryRun ¶
func CheckCommitmentsDryRun(ctx context.Context, config E2EChecksConfig)
CheckCommitmentsDryRun verifies that the dry-run path of change-commitments works end-to-end for each (AZ, HandlesCommitments resource) pair. A dry-run request is sent with amount=2; both accepted and capacity-rejected responses are valid outcomes. No cleanup is needed because dry-run does not persist any state.
func CheckCommitmentsInfoEndpoint ¶
func CheckCommitmentsInfoEndpoint(ctx context.Context, config E2EChecksConfig)
CheckCommitmentsInfoEndpoint verifies that GET /commitments/v1/info returns 200 with a valid ServiceInfo.
func CheckCommitmentsMultiFlavorGroupBatch ¶
func CheckCommitmentsMultiFlavorGroupBatch(ctx context.Context, config E2EChecksConfig)
CheckCommitmentsMultiFlavorGroupBatch exercises the pending→batch-confirm flow for each (AZ, resource) pair. For each pair it:
- Creates a pending commitment (UUID:A, amount=1, ExpiresAt=10min) — non-confirming, always accepted
- Sends an atomic batch: UUID:A pending→confirmed amount=3, UUID:B new confirmed amount=1
- If the batch is rejected (no capacity): cleans up the pending UUID:A and continues
- If the batch is accepted: parses and logs the usage report; deletes unless NoCleanup=true
This exercises the all-or-nothing semantics of change-commitments: if capacity for the full batch (4 units) is unavailable, neither UUID:A nor UUID:B is confirmed.
func CheckCommitmentsRoundTrip ¶
func CheckCommitmentsRoundTrip(ctx context.Context, config E2EChecksConfig)
CheckCommitmentsRoundTrip iterates all HandlesCommitments resources from /info and for each (AZ, resource) pair:
- Creates a confirmed test commitment (amount=2, expires in 5 minutes)
- If accepted: calls the usage API to verify it returns a valid response, then deletes the commitment
- If rejected: logs the reason and continues — capacity rejection is not an error
Panics on infrastructure failures (non-200 from the API, deletion failure after acceptance).
func DeleteChildReservations ¶
func DeleteChildReservations(ctx context.Context, k8sClient client.Client, cr *v1alpha1.CommittedResource) error
DeleteChildReservations deletes all Reservation CRDs belonging to cr, matched by CommitmentUUID. Called both by the controller on inactive/rollback transitions and by the API handler on CR deletion.
func GetFlavorGroupAndTypeFromResource ¶
func GetFlavorGroupAndTypeFromResource(resourceName string) (string, v1alpha1.CommittedResourceType, error)
GetFlavorGroupAndTypeFromResource extracts the flavor group name and resource type from a LIQUID resource name. Accepts _ram (memory) and _cores (CPU) suffixes. _instances resources are not supported for commitments.
func GetFlavorGroupNameFromResource ¶
GetFlavorGroupNameFromResource extracts the flavor group name from a LIQUID resource name. Only accepts _ram resources since CommitmentState is RAM-based. Callers handling _cores or _instances must use a different approach.
func GetMaxSlotIndex ¶
func GetMaxSlotIndex(reservations []v1alpha1.Reservation) int
func GetNextSlotIndex ¶
func GetNextSlotIndex(reservations []v1alpha1.Reservation) int
Always continue counting slots from max, instead of filling gaps.
func LogFlavorGroupResourceConfig ¶
func LogFlavorGroupResourceConfig(log logr.Logger, cfg map[string]FlavorGroupResourcesConfig)
LogFlavorGroupResourceConfig logs the effective RAM unit for each configured flavor group. It warns when RAMUnitGiB is 0, which silently defaults to 1 GiB.
func LoggerFromContext ¶
LoggerFromContext returns a logger with greq and req values from the context. This creates a child logger with the request tracking values pre-attached, so you don't need to repeat them in every log call.
func ResourceNameCores ¶
ResourceNameCores creates a LIQUID resource name for CPU cores from a flavor group name. Format: hw_version_<flavorgroup>_cores
func ResourceNameInstances ¶
ResourceNameInstances creates a LIQUID resource name for instance count from a flavor group name. Format: hw_version_<flavorgroup>_instances
func ResourceNameRAM ¶
ResourceNameRAM creates a LIQUID resource name for RAM from a flavor group name. Format: hw_version_<flavorgroup>_ram
func RunCommitmentsE2EChecks ¶
func RunCommitmentsE2EChecks(ctx context.Context, config E2EChecksConfig)
RunCommitmentsE2EChecks runs all e2e checks for the commitments API.
func ScanAZForPaygCandidates ¶
func ScanAZForPaygCandidates( ctx context.Context, k8sClient client.Client, vmSource reservations.VMSource, az string, projectID string, flavorGroup compute.FlavorGroupFeature, ) (map[string][]PAYGCandidate, error)
ScanAZForPaygCandidates returns unallocated PAYG VMs across all HVs in az, grouped by HV name. Makes exactly two calls regardless of HV or VM count:
- List all CR Reservations → build allocated VM UUID set from Spec.Allocations
- VMSource.ListVMsByProject → enriched VM data for the project
HVs are identified by the label "topology.kubernetes.io/zone".
func WithGlobalRequestID ¶
WithGlobalRequestID creates a new context with the specified global request ID. This is used to propagate existing request IDs (e.g., from the creator annotation).
Types ¶
type APIConfig ¶
type APIConfig struct {
// EnableChangeCommitments controls whether the change-commitments endpoint is active.
// When false the endpoint returns HTTP 503; the info endpoint remains available.
EnableChangeCommitments bool `json:"enableChangeCommitments"`
// EnableReportUsage controls whether the report-usage endpoint is active.
EnableReportUsage bool `json:"enableReportUsage"`
// EnableReportCapacity controls whether the report-capacity endpoint is active.
EnableReportCapacity bool `json:"enableReportCapacity"`
// EnableQuotaAPI controls whether the quota API endpoint is active.
// When false, the endpoint will return HTTP 503 Service Unavailable.
EnableQuotaAPI bool `json:"enableQuota"`
// WatchTimeout is how long the change-commitments handler polls CommittedResource
// CRD conditions before giving up and rolling back.
WatchTimeout metav1.Duration `json:"watchTimeout"`
// WatchPollInterval is how frequently the change-commitments handler polls
// CommittedResource CRD conditions while waiting for the controller outcome.
WatchPollInterval metav1.Duration `json:"watchPollInterval"`
// FlavorGroupResourceConfig maps flavor group IDs to resource flag configs; "*" acts as catch-all.
FlavorGroupResourceConfig map[string]FlavorGroupResourcesConfig `json:"flavorGroupResourceConfig,omitempty"`
// QuotaServedAvailabilityZones restricts quota handling to these AZs.
// Quota received for AZs not in this list is silently skipped.
// If empty/nil, no pre-filtering is applied (relies on error-based fallback).
QuotaServedAvailabilityZones []string `json:"quotaServedAvailabilityZones,omitempty"`
}
APIConfig holds configuration for the LIQUID commitment HTTP endpoints.
func DefaultAPIConfig ¶
func DefaultAPIConfig() APIConfig
func (APIConfig) ResourceConfigForGroup ¶
func (c APIConfig) ResourceConfigForGroup(groupName string) FlavorGroupResourcesConfig
ResourceConfigForGroup returns the resource config for the given flavor group name, falling back to the "*" catch-all if no exact match exists.
type ApplyResult ¶
type ApplyResult struct {
// Created is the number of reservations created
Created int
// Deleted is the number of reservations deleted
Deleted int
// Repaired is the number of reservations repaired (metadata sync or recreated due to wrong config)
Repaired int
// TotalSlots is the total number of reservation slots that should exist after the apply.
// Used by the CR controller to wait for the correct number of children in the cache.
TotalSlots int
// TouchedReservations are reservations that were created or updated
TouchedReservations []v1alpha1.Reservation
// RemovedReservations are reservations that were deleted
RemovedReservations []v1alpha1.Reservation
}
ApplyResult contains the result of applying a commitment state.
type CRControllerMonitor ¶
type CRControllerMonitor struct {
// contains filtered or unexported fields
}
CRControllerMonitor reports the number of CommittedResource CRDs that are actively awaiting reservation placement (Reason=Reserving, AllowRejection=false).
This includes both CRs on their first placement attempt and those being retried after a failure. API-originated dry-run probes (AllowRejection=true) are excluded. The metric is absent for a given label set when no CRs match — absence means zero.
func NewCRControllerMonitor ¶
func NewCRControllerMonitor(c client.Client) CRControllerMonitor
func (*CRControllerMonitor) Collect ¶
func (m *CRControllerMonitor) Collect(ch chan<- prometheus.Metric)
Collect implements prometheus.Collector. Lists all CommittedResource CRDs and counts those with Ready=False/Reason=Reserving and AllowRejection=false, grouped by flavor group, resource type, and AZ.
func (*CRControllerMonitor) Describe ¶
func (m *CRControllerMonitor) Describe(ch chan<- *prometheus.Desc)
Describe implements prometheus.Collector.
func (*CRControllerMonitor) RecordSlotLimitRejection ¶
func (m *CRControllerMonitor) RecordSlotLimitRejection(flavorGroup, az string)
RecordSlotLimitRejection increments the slot-limit rejection counter for the given flavor group and AZ.
type CapacityCalculator ¶
type CapacityCalculator struct {
// contains filtered or unexported fields
}
CapacityCalculator computes capacity reports for Limes LIQUID API.
func NewCapacityCalculator ¶
func NewCapacityCalculator(client client.Client, conf APIConfig) *CapacityCalculator
func (*CapacityCalculator) CalculateCapacity ¶
func (c *CapacityCalculator) CalculateCapacity(ctx context.Context, req liquid.ServiceCapacityRequest) (liquid.ServiceCapacityReport, error)
CalculateCapacity computes per-AZ capacity for all flavor groups. For each flavor group, three resources are reported: _ram, _cores, _instances. All values are read from FlavorGroupCapacity CRDs pre-computed by the capacity controller:
- Capacity: RunningInstances + ExclusivelyFreeCapacity converted to slots.
- Usage: RunningInstances / RunningResources.
type Commitment ¶
type Commitment struct {
// A unique numerical identifier for this commitment. This API uses this
// numerical ID to refer to the commitment in other API calls.
ID int `json:"id"`
// A unique string identifier for this commitment. The next major version of
// this API will use this UUID instead of the numerical ID to refer to
// commitments in API calls.
UUID string `json:"uuid"`
// The resource for which usage is committed.
ServiceType string `json:"service_type"`
ResourceName string `json:"resource_name"`
// The availability zone in which usage is committed.
AvailabilityZone string `json:"availability_zone"`
// The amount of usage that was committed to.
Amount uint64 `json:"amount"`
// For measured resources, the unit for this resource. The value from the
// amount field is measured in this unit.
Unit string `json:"unit"`
// The requested duration of this commitment, expressed as a comma-separated
// sequence of positive integer multiples of time units like "1 year,
// 3 months". Acceptable time units include "second", "minute", "hour",
// "day", "month" and "year".
Duration string `json:"duration"`
// UNIX timestamp when this commitment was created.
CreatedAt uint64 `json:"created_at"`
// UNIX timestamp when this commitment should be confirmed. Only shown if
// this was given when creating the commitment, to delay confirmation into
// the future.
ConfirmBy *uint64 `json:"confirm_by,omitempty"`
// UNIX timestamp when this commitment was confirmed. Only shown after
// confirmation.
ConfirmedAt *uint64 `json:"confirmed_at,omitempty"`
// UNIX timestamp when this commitment is set to expire. Note that the
// duration counts from confirmBy (or from createdAt for immediately-
// confirmed commitments) and is calculated at creation time, so this is
// also shown on unconfirmed commitments.
ExpiresAt uint64 `json:"expires_at"`
// Whether the commitment is marked for transfer to a different project.
// Transferable commitments do not count towards quota calculation in their
// project, but still block capacity and still count towards billing. Not
// shown if false.
Transferable bool `json:"transferable"`
// The current status of this commitment. If provided, one of "planned",
// "pending", "guaranteed", "confirmed", "superseded", or "expired".
Status string `json:"status,omitempty"`
// Whether a mail notification should be sent if a created commitment is
// confirmed. Can only be set if the commitment contains a confirmBy value.
NotifyOnConfirm bool `json:"notify_on_confirm"`
// The openstack project ID this commitment is for.
ProjectID string `json:"project_id"`
// The openstack domain ID this commitment is for.
DomainID string `json:"domain_id"`
}
Commitment model from the limes API. See: https://github.com/sapcc/limes/blob/5ea068b/docs/users/api-spec-resources.md?plain=1#L493 See: https://github.com/sapcc/go-api-declarations/blob/94ee3e5/limes/resources/commitment.go#L19
type CommitmentReservationController ¶
type CommitmentReservationController struct {
// Client for the kubernetes API.
client.Client
// Kubernetes scheme to use for the reservations.
Scheme *runtime.Scheme
// Configuration for the controller.
Conf ReservationControllerConfig
// SchedulerClient for making scheduler API calls.
SchedulerClient *reservations.SchedulerClient
// DomainResolver resolves OpenStack domain IDs to domain names so the
// domain_name scheduler hint can be populated for filter_external_customer.
// Nil when KeystoneSecretRef is not configured; hint is omitted in that case.
DomainResolver DomainResolver
}
CommitmentReservationController reconciles commitment Reservation objects
func (*CommitmentReservationController) Init ¶
func (r *CommitmentReservationController) Init(ctx context.Context, conf ReservationControllerConfig) error
Init initializes the reconciler with required clients and DB connection.
func (*CommitmentReservationController) Reconcile ¶
func (r *CommitmentReservationController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)
Reconcile is part of the main kubernetes reconciliation loop which aims to move the current state of the cluster closer to the desired state. Note: This controller only handles commitment reservations, as filtered by the predicate.
func (*CommitmentReservationController) SetupWithManager ¶
func (r *CommitmentReservationController) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error
SetupWithManager sets up the controller with the Manager.
type CommitmentState ¶
type CommitmentState struct {
// CommitmentUUID is the UUID of the commitment this state corresponds to.
CommitmentUUID string
// ProjectID is the OpenStack project this commitment belongs to
ProjectID string
// DomainID is the OpenStack domain this commitment belongs to
DomainID string
// FlavorGroupName identifies the flavor group (e.g., "hana_medium_v2")
FlavorGroupName string
// ResourceType is the kind of resource committed: memory or cores.
ResourceType v1alpha1.CommittedResourceType
// TotalMemoryBytes is the total memory in bytes across all reservation slots (memory commitments only).
TotalMemoryBytes int64
// TotalCores is the number of committed CPU cores (cores commitments only).
TotalCores int64
// AvailabilityZone specifies the availability zone for this commitment
AvailabilityZone string
// StartTime is when the commitment becomes active
StartTime *time.Time
// EndTime is when the commitment expires
EndTime *time.Time
// CreatorRequestID is the request ID that triggered this state change (for traceability)
CreatorRequestID string
// NamePrefix overrides the default "commitment-<uuid>-" reservation naming convention.
// When set (e.g. "<cr-name>-"), Reservation CRDs are named "<NamePrefix><slot-index>".
// Used by the CommittedResource controller; leave empty for the legacy syncer path.
NamePrefix string
// ParentGeneration is the Generation of the parent CommittedResource CRD. Written into
// Reservation spec so the Reservation controller can echo it back in status, letting
// the CR controller detect when all children have been processed for the current spec.
// Zero for syncer-created reservations (no parent CR).
ParentGeneration int64
// State is the lifecycle state from Limes (planned/pending/guaranteed/confirmed/superseded/expired).
State v1alpha1.CommitmentStatus
}
CommitmentState represents desired or current commitment resource allocation.
func FromChangeCommitmentTargetState ¶
func FromChangeCommitmentTargetState( commitment liquid.Commitment, projectID string, domainID string, flavorGroupName string, resourceType v1alpha1.CommittedResourceType, az string, ramUnitMiB uint64, ) (*CommitmentState, error)
FromChangeCommitmentTargetState converts LIQUID API request to CommitmentState. ramUnitMiB is the size of one external RAM unit in MiB:
- fixed-ratio flavor groups: SmallestFlavor.MemoryMB (1 unit = 1 smallest-flavor slot)
- variable-ratio flavor groups: 1024 (1 unit = 1 GiB)
func FromCommitment ¶
func FromCommitment( commitment Commitment, ramUnitMiB uint64, ) (*CommitmentState, error)
FromCommitment converts Limes commitment to CommitmentState. ramUnitMiB is the size of one external RAM unit in MiB, as returned by RAMResourceTypeConfig.RAMUnitMiB():
- fixed-ratio flavor groups: SmallestFlavor.MemoryMB (1 unit = 1 smallest-flavor slot)
- variable-ratio flavor groups: 1024 (1 unit = 1 GiB)
func FromCommittedResource ¶
func FromCommittedResource(cr v1alpha1.CommittedResource) (*CommitmentState, error)
FromCommittedResource reads CommitmentState from a CommittedResource CRD.
func FromReservations ¶
func FromReservations(reservations []v1alpha1.Reservation) (*CommitmentState, error)
FromReservations reconstructs CommitmentState from existing Reservation CRDs.
type CommitmentStateWithUsage ¶
type CommitmentStateWithUsage struct {
CommitmentState
RemainingMemoryBytes int64
AssignedInstances []string
UsedVCPUs int64
}
CommitmentStateWithUsage extends CommitmentState with usage tracking for billing calculations.
func NewCommitmentStateWithUsage ¶
func NewCommitmentStateWithUsage(state *CommitmentState) *CommitmentStateWithUsage
NewCommitmentStateWithUsage creates a CommitmentStateWithUsage from a CommitmentState.
func (*CommitmentStateWithUsage) AssignVM ¶
func (c *CommitmentStateWithUsage) AssignVM(vmUUID string, vmMemoryBytes, vCPUs int64) bool
AssignVM attempts to assign a VM to this commitment if there's enough capacity.
func (*CommitmentStateWithUsage) HasRemainingCapacity ¶
func (c *CommitmentStateWithUsage) HasRemainingCapacity() bool
HasRemainingCapacity returns true if the commitment has any remaining capacity.
type CommitmentsClient ¶
type CommitmentsClient interface {
// Init the client.
Init(ctx context.Context, client client.Client, conf SyncerConfig) error
// List all projects to resolve commitments.
ListProjects(ctx context.Context) ([]Project, error)
// List all commitments with resolved metadata (e.g. project, flavor, ...).
ListCommitmentsByID(ctx context.Context, projects ...Project) (map[string]Commitment, error)
}
Client to fetch commitments.
func NewCommitmentsClient ¶
func NewCommitmentsClient() CommitmentsClient
type CommittedResourceController ¶
type CommittedResourceController struct {
client.Client
Scheme *runtime.Scheme
Conf CommittedResourceControllerConfig
Monitor *CRControllerMonitor
// VMSource enables PAYG pre-allocation when creating reservation slots. When nil the
// PAYG scan is skipped and all slots are created via blind scheduler probes.
VMSource reservations.VMSource
}
CommittedResourceController reconciles CommittedResource CRDs and owns all child Reservation CRUD.
func (*CommittedResourceController) SetupWithManager ¶
func (r *CommittedResourceController) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error
SetupWithManager sets up the controller with the Manager.
type CommittedResourceControllerConfig ¶
type CommittedResourceControllerConfig struct {
// RequeueIntervalRetry is the base back-off interval when placement fails (AllowRejection=false path).
// The actual delay doubles with each consecutive failure: base * 2^min(failures, 6), capped at MaxRequeueInterval.
// If zero (unconfigured), backoff is disabled and the controller retries immediately on every failure.
RequeueIntervalRetry metav1.Duration `json:"requeueIntervalRetry"`
// MaxRequeueInterval caps the exponential backoff delay.
// Once this ceiling is reached, every subsequent retry fires after exactly this interval.
MaxRequeueInterval metav1.Duration `json:"maxRequeueInterval"`
// SlotCreationDelay is the pause inserted between consecutive Reservation CRD creates.
// Spreads scheduler calls over time instead of bursting them all at once.
// 0 disables the delay.
SlotCreationDelay metav1.Duration `json:"slotCreationDelay"`
// MaxSlotsPerCommitment caps the number of Reservation CRDs that may be created for a single
// CommittedResource on the AllowRejection=true (API) path. Requests that would exceed this
// limit are rejected immediately before any slots are created.
// Has no effect on the AllowRejection=false (syncer) path.
// 0 disables the cap.
MaxSlotsPerCommitment int `json:"maxSlotsPerCommitment"`
// EnablePaygPreAllocation enables scanning the AZ for existing PAYG VMs before creating
// blind reservation slots. When true, the controller absorbs matching PAYG VMs into
// pre-populated slots, consuming CR delta before falling back to the blind scheduler path.
EnablePaygPreAllocation bool `json:"enablePaygPreAllocation,omitempty"`
}
CommittedResourceControllerConfig holds tuning knobs for the CommittedResource CRD controller.
type Config ¶
type Config struct {
ReservationController ReservationControllerConfig `json:"committedResourceReservationController"`
CommittedResourceController CommittedResourceControllerConfig `json:"committedResourceController"`
UsageReconciler UsageReconcilerConfig `json:"committedResourceUsageReconciler"`
API APIConfig `json:"committedResourceAPI"`
// DatasourceName is the name of the Datasource CRD that provides database
// connection info. Used to construct the UsageDBClient for report-usage and usage reconciler.
DatasourceName string `json:"datasourceName,omitempty"`
}
Config aggregates configuration for all commitments components. Each controller and the API have their own sub-struct so that unrelated fields are never visible to the wrong component.
type DomainResolver ¶
type DomainResolver interface {
// ResolveDomainName returns the name of the domain with the given ID.
// Returns an error if the domain cannot be found or the lookup fails.
ResolveDomainName(ctx context.Context, domainID string) (string, error)
}
DomainResolver resolves OpenStack domain IDs to their human-readable names. Implementations must be safe for concurrent use.
type E2EChecksConfig ¶
type E2EChecksConfig struct {
// BaseURL for the commitments API. If empty, defaults to defaultCommitmentsAPIURL.
BaseURL string `json:"baseURL"`
// NoCleanup prevents test commitments from being deleted after a successful run.
// Useful for local inspection with Tilt. Zero value (false) means cleanup runs normally.
NoCleanup bool `json:"noCleanup,omitempty"`
// ProjectID is the OpenStack project UUID for all e2e test commitments.
// If empty, falls back to RoundTripCheck.TestProjectID, then defaultE2EProjectUUID.
ProjectID string `json:"projectID,omitempty"`
// AZs is the list of availability zones to test. If empty, falls back to
// RoundTripCheck.AZ, then uses "qa-de-1b".
AZs []string `json:"azs,omitempty"`
// RequestTimeout is the per-request HTTP timeout for change-commitments calls.
// Defaults to 30s if unset.
RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"`
// RoundTripCheck holds optional overrides for backward compatibility.
// Prefer the top-level ProjectID and AZs fields for new configurations.
RoundTripCheck *E2ERoundTripConfig `json:"roundTripCheck,omitempty"`
}
E2EChecksConfig holds the configuration for CR e2e checks.
type E2ERoundTripConfig
deprecated
type E2ERoundTripConfig struct {
// AZ is the availability zone to use (e.g. "qa-de-1d"). Defaults to "" if not set.
AZ string `json:"az"`
// TestProjectID is the OpenStack project UUID to create test commitments under.
// Defaults to defaultE2EProjectUUID if not set.
TestProjectID string `json:"testProjectID"`
}
E2ERoundTripConfig holds optional overrides for the create→delete round-trip e2e check.
Deprecated: use E2EChecksConfig.ProjectID and E2EChecksConfig.AZs instead.
type Flavor ¶
type Flavor struct {
ID string `json:"id"`
Disk int `json:"disk"` // in GB.
RAM int `json:"ram"` // in MB.
Name string `json:"name"`
RxTxFactor float64 `json:"rxtx_factor"`
VCPUs int `json:"vcpus"`
IsPublic bool `json:"os-flavor-access:is_public"`
Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"`
Description string `json:"description"`
// JSON string of extra specifications used when scheduling the flavor.
ExtraSpecs map[string]string `json:"extra_specs" db:"extra_specs"`
}
OpenStack flavor model as returned by the Nova API under /flavors/detail. See: https://docs.openstack.org/api-ref/compute/#list-flavors
type FlavorGroupResourcesConfig ¶
type FlavorGroupResourcesConfig struct {
RAM RAMResourceTypeConfig `json:"ram"`
Cores ResourceTypeConfig `json:"cores"`
Instances ResourceTypeConfig `json:"instances"`
}
FlavorGroupResourcesConfig groups resource type configs for the three resources of a flavor group.
func ResourceConfigForGroup ¶
func ResourceConfigForGroup(cfg map[string]FlavorGroupResourcesConfig, groupName string) FlavorGroupResourcesConfig
ResourceConfigForGroup returns the resource config for the given flavor group name, falling back to the "*" catch-all if no exact match exists.
type PAYGCandidate ¶
type PAYGCandidate struct {
VMID string
HVName string
MemoryMB uint64
FlavorName string // exact flavor of the VM — used directly as the slot ResourceName
}
PAYGCandidate is an unallocated PAYG VM suitable for pre-allocation into a reservation slot.
type Project ¶
type Project struct {
// DomainID is the domain ID the project belongs to.
DomainID string `json:"domain_id"`
// ID is the unique ID of the project.
ID string `json:"id"`
// Name is the name of the project.
Name string `json:"name"`
// ParentID is the parent_id of the project.
ParentID string `json:"parent_id"`
}
OpenStack project model as returned by the Keystone API under /projects. See: https://docs.openstack.org/api-ref/identity/v3/#projects
type RAMResourceTypeConfig ¶
type RAMResourceTypeConfig struct {
HandlesCommitments bool `json:"handlesCommitments"`
HandlesDryRun bool `json:"handlesDryRun"`
HasCapacity bool `json:"hasCapacity"`
HasQuota bool `json:"hasQuota"`
// RAMUnitGiB is the size of one declared LIQUID RAM unit in GiB.
// Fixed-ratio groups set this to the smallest flavor's RAM (e.g. 480 for a 480 GiB HANA slot).
// Variable-ratio groups set this to 1 (1 unit = 1 GiB).
// Defaults to 1 if unset.
RAMUnitGiB uint64 `json:"ramUnitGiB,omitempty"`
}
RAMResourceTypeConfig extends ResourceTypeConfig with RAM-specific unit configuration.
func (RAMResourceTypeConfig) DeclaredUnitsToGiB ¶
func (c RAMResourceTypeConfig) DeclaredUnitsToGiB(units int64) int64
DeclaredUnitsToGiB converts a value in declared LIQUID units to GiB.
func (RAMResourceTypeConfig) GiBToDeclaredUnits ¶
func (c RAMResourceTypeConfig) GiBToDeclaredUnits(gib int64) int64
GiBToDeclaredUnits converts a GiB value to declared LIQUID units.
func (RAMResourceTypeConfig) RAMUnitMiB ¶
func (c RAMResourceTypeConfig) RAMUnitMiB() uint64
RAMUnitMiB returns the RAM unit in MiB (RAMUnitGiB × 1024), defaulting to 1024 if unset.
type ReservationControllerConfig ¶
type ReservationControllerConfig struct {
// RequeueIntervalActive is how often to re-verify a healthy Reservation CRD.
RequeueIntervalActive metav1.Duration `json:"requeueIntervalActive"`
// RequeueIntervalRetry is the back-off interval when knowledge is unavailable.
RequeueIntervalRetry metav1.Duration `json:"requeueIntervalRetry"`
// RequeueIntervalGracePeriod is how often to re-check while a VM allocation
// is still within AllocationGracePeriod. Shorter than RequeueIntervalActive.
RequeueIntervalGracePeriod metav1.Duration `json:"requeueIntervalGracePeriod"`
// AllocationGracePeriod is the time window after a VM is allocated to a
// reservation during which it's expected to appear on the target host.
// VMs not confirmed within this period are considered stale and removed.
AllocationGracePeriod metav1.Duration `json:"allocationGracePeriod"`
// SchedulerURL is the endpoint of the nova external scheduler.
SchedulerURL string `json:"schedulerURL"`
// PipelineDefault is the fallback pipeline when no FlavorGroupPipelines entry matches.
PipelineDefault string `json:"pipelineDefault"`
// FlavorGroupPipelines maps flavor group IDs to pipeline names; "*" acts as catch-all.
FlavorGroupPipelines map[string]string `json:"flavorGroupPipelines,omitempty"`
// KeystoneSecretRef references a Kubernetes Secret that holds OpenStack credentials
// used to resolve domain IDs to domain names for the domain_name scheduler hint.
// The secret must contain the same keys as the syncer's keystoneSecretRef.
// When empty, domain name resolution is skipped and filter_external_customer will
// not enforce domain restrictions for CR reservations.
KeystoneSecretRef corev1.SecretReference `json:"keystoneSecretRef,omitempty"`
// SSOSecretRef is an optional reference to a Secret holding SSO credentials.
// Required in environments that use SSO-based Keystone authentication.
// When nil, http.DefaultClient is used, which will fail in SSO-only environments.
SSOSecretRef *corev1.SecretReference `json:"ssoSecretRef,omitempty"`
}
ReservationControllerConfig holds tuning knobs for the Reservation CRD controller.
type ReservationManager ¶
ReservationManager handles CRUD operations for Reservation CRDs.
func NewReservationManager ¶
func NewReservationManager(k8sClient client.Client, cfg ReservationManagerConfig) *ReservationManager
NewReservationManager creates a ReservationManager using the given client and config.
func (*ReservationManager) ApplyCommitmentState ¶
func (m *ReservationManager) ApplyCommitmentState( ctx context.Context, log logr.Logger, desiredState *CommitmentState, flavorGroups map[string]compute.FlavorGroupFeature, creator string, ) (*ApplyResult, error)
ApplyCommitmentState synchronizes Reservation CRDs to match the desired commitment state. This function performs CRUD operations (create/update/delete) on reservation slots to align with the capacity specified in desiredState.
Entry points:
- from Syncer - periodic sync with Limes state
- from API ChangeCommitmentsHandler - batch processing of commitment changes
The function is idempotent and handles:
- Repairing inconsistent slots (wrong flavor group/project)
- Creating new reservation slots when capacity increases
- Deleting unused/excess slots when capacity decreases
- Syncing reservation metadata for all remaining slots
Returns ApplyResult containing touched/removed reservations and counts for metrics.
type ReservationManagerConfig ¶
type ReservationManagerConfig struct {
// SlotCreationDelay adds a pause between consecutive Reservation CRD creates to spread
// scheduler load across time rather than bursting all creates at once.
SlotCreationDelay time.Duration
// MaxSlots caps the number of new blind-scheduler slots created per apply call.
// PAYG remapping slots and already-existing slots are excluded from this limit.
// Only set by the caller on the AllowRejection=true (API) path.
MaxSlots int
EnablePaygPreAllocation bool
VMSource reservations.VMSource
}
ReservationManagerConfig holds options for ReservationManager.
type ResourceTypeConfig ¶
type ResourceTypeConfig struct {
HandlesCommitments bool `json:"handlesCommitments"`
HandlesDryRun bool `json:"handlesDryRun"`
HasCapacity bool `json:"hasCapacity"`
HasQuota bool `json:"hasQuota"`
}
ResourceTypeConfig holds per-resource flags for a single resource type within a flavor group.
type Server ¶
type Server struct {
ID string `json:"id" db:"id,primarykey"`
Name string `json:"name" db:"name"`
Status string `json:"status" db:"status"`
TenantID string `json:"tenant_id" db:"tenant_id"`
UserID string `json:"user_id" db:"user_id"`
HostID string `json:"hostId" db:"host_id"`
Created string `json:"created" db:"created"`
Updated string `json:"updated" db:"updated"`
AccessIPv4 string `json:"accessIPv4" db:"access_ipv4"`
AccessIPv6 string `json:"accessIPv6" db:"access_ipv6"`
OSDCFdiskConfig string `json:"OS-DCF:diskConfig" db:"os_dcf_disk_config"`
Progress int `json:"progress" db:"progress"`
OSEXTAvailabilityZone string `json:"OS-EXT-AZ:availability_zone" db:"os_ext_az_availability_zone"`
ConfigDrive string `json:"config_drive" db:"config_drive"`
KeyName string `json:"key_name" db:"key_name"`
OSSRVUSGLaunchedAt string `json:"OS-SRV-USG:launched_at" db:"os_srv_usg_launched_at"`
OSSRVUSGTerminatedAt *string `json:"OS-SRV-USG:terminated_at" db:"os_srv_usg_terminated_at"`
OSEXTSRVATTRHost string `json:"OS-EXT-SRV-ATTR:host" db:"os_ext_srv_attr_host"`
OSEXTSRVATTRInstanceName string `json:"OS-EXT-SRV-ATTR:instance_name" db:"os_ext_srv_attr_instance_name"`
OSEXTSRVATTRHypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname" db:"os_ext_srv_attr_hypervisor_hostname"`
OSEXTSTSTaskState *string `json:"OS-EXT-STS:task_state" db:"os_ext_sts_task_state"`
OSEXTSTSVmState string `json:"OS-EXT-STS:vm_state" db:"os_ext_sts_vm_state"`
OSEXTSTSPowerState int `json:"OS-EXT-STS:power_state" db:"os_ext_sts_power_state"`
// From nested JSON
FlavorName string `json:"-" db:"flavor_name"`
}
OpenStack server model as returned by the Nova API under /servers/detail. See: https://docs.openstack.org/api-ref/compute/#list-servers-detailed
func (*Server) MarshalJSON ¶
Custom marshaler for OpenStackServer to handle nested JSON.
func (*Server) UnmarshalJSON ¶
Custom unmarshaler for OpenStackServer to handle nested JSON.
type SlotLimitExceededError ¶
SlotLimitExceededError is returned by ApplyCommitmentState when the number of new reservation slots would exceed MaxSlots. It is a distinct type so callers can detect and metric it separately from ordinary capacity failures.
func (*SlotLimitExceededError) Error ¶
func (e *SlotLimitExceededError) Error() string
type Syncer ¶
type Syncer struct {
// Client to fetch commitments from Limes
CommitmentsClient
// Kubernetes client for CRD operations
client.Client
// contains filtered or unexported fields
}
type SyncerConfig ¶
type SyncerConfig struct {
// Secret ref to keystone credentials stored in a k8s secret.
KeystoneSecretRef corev1.SecretReference `json:"keystoneSecretRef"`
// Secret ref to SSO credentials stored in a k8s secret, if applicable.
SSOSecretRef *corev1.SecretReference `json:"ssoSecretRef"`
// SyncInterval defines how often the syncer reconciles Limes commitments to Reservation CRDs.
SyncInterval metav1.Duration `json:"committedResourceSyncInterval"`
// FlavorGroupResourceConfig maps flavor group names to resource configs; "*" acts as catch-all.
// Not read from JSON — populated by the caller from the shared APIConfig.
FlavorGroupResourceConfig map[string]FlavorGroupResourcesConfig
}
func (SyncerConfig) ResourceConfigForGroup ¶
func (c SyncerConfig) ResourceConfigForGroup(groupName string) FlavorGroupResourcesConfig
ResourceConfigForGroup returns the resource config for the given flavor group name.
type SyncerMonitor ¶
type SyncerMonitor struct {
// contains filtered or unexported fields
}
SyncerMonitor provides metrics for the commitment syncer.
func NewSyncerMonitor ¶
func NewSyncerMonitor() *SyncerMonitor
NewSyncerMonitor creates a new monitor with Prometheus metrics.
func (*SyncerMonitor) Collect ¶
func (m *SyncerMonitor) Collect(ch chan<- prometheus.Metric)
Collect implements prometheus.Collector.
func (*SyncerMonitor) Describe ¶
func (m *SyncerMonitor) Describe(ch chan<- *prometheus.Desc)
Describe implements prometheus.Collector.
func (*SyncerMonitor) RecordCRCreates ¶
func (m *SyncerMonitor) RecordCRCreates(count int)
func (*SyncerMonitor) RecordCRDeletes ¶
func (m *SyncerMonitor) RecordCRDeletes(count int)
func (*SyncerMonitor) RecordCRUpdates ¶
func (m *SyncerMonitor) RecordCRUpdates(count int)
func (*SyncerMonitor) RecordCommitmentSkipped ¶
func (m *SyncerMonitor) RecordCommitmentSkipped(reason string)
func (*SyncerMonitor) RecordDuration ¶
func (m *SyncerMonitor) RecordDuration(seconds float64)
func (*SyncerMonitor) RecordError ¶
func (m *SyncerMonitor) RecordError()
func (*SyncerMonitor) RecordStaleCRs ¶
func (m *SyncerMonitor) RecordStaleCRs(count int)
func (*SyncerMonitor) SetLimesCommitmentsActive ¶
func (m *SyncerMonitor) SetLimesCommitmentsActive(count int)
type UsageCalculator ¶
type UsageCalculator struct {
// contains filtered or unexported fields
}
UsageCalculator computes usage reports for Limes LIQUID API.
func NewUsageCalculator ¶
func NewUsageCalculator(client client.Client, vmSource reservations.VMSource, config APIConfig) *UsageCalculator
NewUsageCalculator creates a new UsageCalculator instance.
func (*UsageCalculator) BuildVMAssignmentsFromStatus ¶
func (c *UsageCalculator) BuildVMAssignmentsFromStatus(ctx context.Context, projectID string) (map[string]string, error)
BuildVMAssignmentsFromStatus reads pre-computed VM-to-commitment assignments from CommittedResource CRD status. Returns a map of vmUUID → commitmentUUID (empty string = PAYG). This is the read path that replaces the inline assignment algorithm in the usage API.
func (*UsageCalculator) CalculateUsage ¶
func (c *UsageCalculator) CalculateUsage( ctx context.Context, log logr.Logger, projectID string, allAZs []liquid.AvailabilityZone, ) (liquid.ServiceUsageReport, error)
CalculateUsage computes the usage report for a specific project. VM-to-commitment assignment is read from CommittedResource CRD status (pre-computed by the UsageReconciler). If a CR has no usage status yet, its VMs appear as PAYG until the first reconcile completes (within one CooldownInterval).
type UsageReconciler ¶
type UsageReconciler struct {
client.Client
Conf UsageReconcilerConfig
VMSource reservations.VMSource
Monitor UsageReconcilerMonitor
}
UsageReconciler reconciles CommittedResource.Status usage fields (AssignedInstances, UsedResources, LastUsageReconcileAt) by running the deterministic VM-to-CR assignment periodically and on relevant change events.
func (*UsageReconciler) SetupWithManager ¶
func (r *UsageReconciler) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error
SetupWithManager registers the usage reconciler with the controller manager.
type UsageReconcilerConfig ¶
type UsageReconcilerConfig struct {
// CooldownInterval is the minimum time between usage reconcile runs for the same CommittedResource.
// If a reconcile ran within this window, the next trigger is deferred until the window expires.
// This interval also acts as the periodic fallback: every successful reconcile schedules the
// next run after this duration so that changes not caught by watches are still picked up.
CooldownInterval metav1.Duration `json:"cooldownInterval"`
}
UsageReconcilerConfig holds tuning knobs for the usage reconciler.
func DefaultUsageReconcilerConfig ¶
func DefaultUsageReconcilerConfig() UsageReconcilerConfig
func (*UsageReconcilerConfig) ApplyDefaults ¶
func (c *UsageReconcilerConfig) ApplyDefaults()
ApplyDefaults fills in zero-value fields from the defaults, leaving explicitly configured values intact.
type UsageReconcilerMonitor ¶
type UsageReconcilerMonitor struct {
// contains filtered or unexported fields
}
UsageReconcilerMonitor provides metrics for the usage reconciler.
func NewUsageReconcilerMonitor ¶
func NewUsageReconcilerMonitor() UsageReconcilerMonitor
NewUsageReconcilerMonitor creates a new monitor with Prometheus metrics.
func (UsageReconcilerMonitor) Collect ¶
func (m UsageReconcilerMonitor) Collect(ch chan<- prometheus.Metric)
Collect implements prometheus.Collector.
func (UsageReconcilerMonitor) Describe ¶
func (m UsageReconcilerMonitor) Describe(ch chan<- *prometheus.Desc)
Describe implements prometheus.Collector.
type VMUsageInfo ¶
type VMUsageInfo struct {
UUID string
Name string
FlavorName string
FlavorGroup string
Status string
MemoryMB uint64
VCPUs uint64
DiskGB uint64
VideoRAMMiB *uint64
OSType string
AZ string
Hypervisor string
CreatedAt time.Time
UsageMultiple uint64
}
VMUsageInfo contains VM information needed for usage calculation, enriched with flavor group data.
Source Files
¶
- capacity.go
- client.go
- committed_resource_controller.go
- committed_resource_controller_monitor.go
- committed_resource_summary.go
- config.go
- context.go
- domain_resolver.go
- e2e_checks.go
- field_index.go
- messages.go
- payg_candidates.go
- reservation_controller.go
- reservation_manager.go
- state.go
- syncer.go
- syncer_monitor.go
- usage.go
- usage_reconciler.go
- usage_reconciler_monitor.go
- utils.go