catalog

package
v0.0.0-...-fa81656 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package catalog is the shared price/availability catalog for providers whose APIs do not expose a rate card (Modal, and most NeoClouds). It follows SkyPilot's community-catalog pattern: prices live in checked-in, git-tracked CSV files (pkg/provider/catalog/data/<provider>.csv) rather than hardcoded in Go, so a price change is a reviewable data diff.

Two-tier load, override-first:

  1. If an override directory is set (NEBULA_CATALOG_DIR, or LoadFrom(dir)), CSVs there win. This is how the deployed controller consumes a ConfigMap: the CSVs are rendered into a ConfigMap and mounted, so ops can edit prices live (kubectl edit configmap) without rebuilding the image.
  2. Otherwise the CSVs embedded at build time are used as the default, so the binary always has a working catalog even with nothing mounted.

A provider's Offerings() becomes a lookup into this catalog instead of a hardcoded table.

Index

Constants

View Source
const OverrideDirEnv = "NEBULA_CATALOG_DIR"

OverrideDirEnv is the env var pointing at a directory of provider CSVs that override the embedded defaults. Set by the manager Deployment to the ConfigMap mount path.

Variables

This section is empty.

Functions

This section is empty.

Types

type Base

type Base struct {
	// ProviderName is this provider's stable identifier (e.g. "modal"), used both
	// as Name() and as the key into the catalog.
	ProviderName string
	// Catalog is the shared price/availability lookup.
	Catalog Lookup
}

Base supplies the parts of provider.Provider that are identical for every adapter whose price/availability comes from a catalog: Name, Offerings, and a default identity MapAccelerator. Adapters embed it so those three methods are not re-implemented per provider:

type Provider struct {
    catalog.Base
    client Client
}

Name and Offerings are fully generic. MapAccelerator is generic only while a provider names its accelerators like Nebula's canonical names (Modal does); one whose identifiers diverge overrides just that method and still reuses the rest.

Lifecycle, Capabilities and ClassifyProvisionError are genuinely provider-specific and are not provided here.

func (Base) ExpandRegions

func (b Base) ExpandRegions(declared []string) []string

ExpandRegions passes the declared regions through unchanged: one candidate each, tokens used verbatim as region names. Right for a provider whose own vocabulary already spans both levels the pool speaks AND whose provision reports capacity failures synchronously, so walking candidates actually buys a retry in the next region. nil stays nil, which every adapter reads as "unconstrained".

Both halves have real overriders, in opposite directions: AWS expands a group token into many candidates ("us" is not a callable region), while Modal collapses everything into ONE candidate because its create cannot fail over. Check which a new provider resembles before inheriting this.

func (Base) MapAccelerator

func (b Base) MapAccelerator(canonical string, count int32) (providerAcceleratorIDs []string, ok bool)

MapAccelerator translates a canonical accelerator request (type + count) into this provider's own ids, using the catalog as the mapping table: matching rows contribute their AcceleratorIDs in catalog order — PRIMARY first, then interchangeable alternates, deduped. A blank AcceleratorID falls back to the canonical name, so an identity-mapped provider needs no per-name data. Since the mapping is all in the CSV, a provider whose ids diverge just fills in accelerator_id/gpu_count instead of overriding this.

Count matching honours both catalog shapes (see Offering.GPUCount): a provider that bakes the count in (AWS: T4x1=g4dn.xlarge, T4x8=g4dn.metal) emits one row per count, which is what keeps (L4, 1) and (L4, 8) on DISTINCT primaries so one's block cannot exclude the other. A provider taking count as a parameter (Modal) leaves GPUCount 0, and a 0 row matches any count.

Dedup collapses the per-capacity-type/per-region duplicates (an instance type is the same object whether the row prices Spot or OnDemand). ids[0] is what failover blocks on; alternates broaden a launch but never the blocklist. ok=false when no row matches.

Availability gates the mapping: a row with Available false contributes no id, so a (type, count) with no available row maps to ok=false. This is the one seam placement, AWS and Modal all consult, so flipping a CSV row off removes it from scheduling everywhere without touching Go.

func (Base) Name

func (b Base) Name() string

Name returns the provider's stable identifier.

func (Base) Offerings

func (b Base) Offerings(context.Context) ([]provider.Offering, error)

Offerings returns this provider's rows from the catalog. The error is always nil today (the catalog is in-memory); the signature matches provider.Provider so an adapter that later combines the static catalog with a live availability probe can return a real error without a signature change.

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog holds parsed offerings keyed by provider name.

func Load

func Load() (*Catalog, error)

Load builds a Catalog, preferring CSVs in the override dir named by NEBULA_CATALOG_DIR when that env var is set and the dir exists, otherwise falling back to the embedded defaults.

func LoadFrom

func LoadFrom(dir string) (*Catalog, error)

LoadFrom builds a Catalog from CSV files in dir (used for the ConfigMap mount and in tests). Each file must be named "<provider>.csv".

func (*Catalog) Offerings

func (c *Catalog) Offerings(providerName string) []provider.Offering

Offerings returns the catalog rows for providerName, or nil if the provider has no catalog file. The returned slice is a copy, safe for the caller to annotate (e.g. a live availability probe) without mutating the catalog.

type Lookup

type Lookup interface {
	// Offerings returns providerName's rows, or nil if the provider has no
	// catalog entry. Implementations return a copy the caller may safely annotate.
	Offerings(providerName string) []provider.Offering
}

Lookup is the price/availability seam a provider adapter depends on: given a provider name it returns that provider's offering rows. The concrete *Catalog satisfies it; tests inject a trivial fake so an adapter can be unit-tested without embedding CSVs. It lives in this package (not in provider) so all catalog-shaped types share one home and there is no provider.Catalog / catalog.Catalog name clash.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL