daemon

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 42 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// CDIHookCommand is the argv[1] the CDI spec invokes the daemon with.
	CDIHookCommand = "cdi-hook"

	// ThunderGuestDir is where the client lives inside a container. It is not
	// configurable: LD_PRELOAD names this path.
	ThunderGuestDir = "/etc/thunder"
)
View Source
const (
	EnvNode                = "NODE"
	EnvZone                = "ZONE"
	EnvAdvertisedIP        = "ADVERTISED_IP"
	EnvMinNVDriverVersion  = "MIN_DRIVER_VERSION"
	EnvThunderAPIURL       = "THUNDER_API_URL"
	EnvThunderAPIToken     = "THUNDER_API_TOKEN"
	EnvHostRoot            = "HOST_ROOT"
	EnvLibCUDAPath         = "LIBCUDA_PATH"
	EnvLibNVMLPath         = "LIBNVIDIA_ML_PATH"
	EnvNVSMIPath           = "NVIDIA_SMI_PATH"
	EnvZoneLabel           = "NODE_ZONE_LABEL"
	EnvAdvertisedIPLabel   = "NODE_ADVERTISED_IP_LABEL"
	EnvHostTargetPID       = "HOST_TARGET_PID"
	EnvDRAEnabled          = "DRA_ENABLED"
	EnvDRADriverName       = "DRA_DRIVER_NAME"
	EnvCDISpecDir          = "CDI_SPEC_DIR"
	EnvThunderClientNS     = "THUNDER_CLIENT_NAMESPACE"
	EnvKubeletPluginDir    = "KUBELET_PLUGIN_DIR"
	EnvKubeletRegistrarDir = "KUBELET_REGISTRAR_DIR"
	EnvThunderInstallURL   = "THUNDER_INSTALL_URL"
	EnvThunderTelemetryURL = "THUNDER_TELEMETRY_URL"
	EnvArtifactBaseURL     = "THUNDER_ARTIFACT_BASE_URL"
	EnvLibthunderURL       = "LIBTHUNDER_URL"
	EnvLibthunderSHA256    = "LIBTHUNDER_SHA256"
	EnvCABundlePath        = "CA_BUNDLE_PATH"
)
View Source
const (
	DefaultHostRoot            = "/host"
	DefaultLibCUDAPath         = "/usr/lib/x86_64-linux-gnu/libcuda.so.1"
	DefaultLibNVMLPath         = "/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1"
	DefaultNVSMIPath           = "/usr/bin/nvidia-smi"
	DefaultZoneLabel           = "topology.kubernetes.io/zone"
	DefaultAdvertisedIPLabel   = "thundercompute.com/advertised-ip"
	DefaultHostTargetPID       = "1"
	DefaultDRAEnabled          = true
	DefaultDRADriverName       = DefaultDriverName
	DefaultCDISpecDir          = "/var/run/cdi"
	DefaultThunderClientNS     = DefaultThunderClientNamespace
	DefaultKubeletPluginDir    = "/var/lib/kubelet/plugins/" + DefaultDriverName
	DefaultKubeletRegistrarDir = "/var/lib/kubelet/plugins_registry"
	// DefaultThunderInstallURL is the installer the CDI hook reads the
	// pinned libthunder.so digest out of. It is never executed.
	DefaultThunderInstallURL   = "https://get.thundercompute.com/install.sh"
	DefaultThunderTelemetryURL = "https://telemetry.thundercompute.com:2096"
	// DefaultCABundlePath is the node trust store staged into containers
	// that ship none of their own.
	DefaultCABundlePath = "/etc/ssl/certs/ca-certificates.crt"
)
View Source
const (
	DefaultDriverName             = thunderDomain
	DefaultThunderClientNamespace = "thunder-system"
	DefaultCDIKind                = thunderDomain + "/gpu"

	GPUTypeAttributeName = thunderDomain + "/gpu_type"
	ZoneAttributeName    = thunderDomain + "/zone"
)
View Source
const (
	ServiceAccountToken = "/var/run/secrets/kubernetes.io/serviceaccount/token"
	ServiceAccountCA    = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)
View Source
const (
	// ThunderReconcileInterval is how often the daemon re-checks that the node
	// is in the state it should be in.
	ThunderReconcileInterval = 10 * time.Second

	// ThunderReconcileMaxBackoff caps the wait between passes after repeated
	// failures. A node that cannot enroll keeps retrying, but without hammering
	// the Thunder API or re-running the installer in a tight loop.
	ThunderReconcileMaxBackoff = 5 * time.Minute
)
View Source
const (
	ThunderGuestInstallScriptKey = "install-thunder-client.sh"
	ThunderGuestSecretTokenKey   = "enrollment-token"
	ThunderGuestTokenPath        = "/mnt/thunder-setup/enrollment-token"
)
View Source
const (
	ThunderEnrollmentTokenEnv      = "THUNDER_ENROLLMENT_TOKEN"
	ThunderClientInstallCommandEnv = "THUNDER_CLIENT_INSTALL_COMMAND"
)

Variables

View Source
var ErrNotFound = errors.New("not found")
View Source
var ThunderClientGVR = thunderclient.GVR

ThunderClientGVR addresses the per-claim ThunderClient resource.

Functions

func IsNotFoundError

func IsNotFoundError(err error) bool

func Quantity

func Quantity(value int64) apiresource.Quantity

func Run

func Run(ctx context.Context, cfg Config) error

func RunCDIHook

func RunCDIHook(ctx context.Context, opts CDIHookOptions, stdin io.Reader) error

RunCDIHook stages the Thunder client into a container that is being created.

It runs on the host, once per container, with the container filesystem at <bundle>/rootfs. Everything the workload needs is written there: the library LD_PRELOAD names, and the config.json identifying the client. Nothing is assumed about the image — it needs no shell, no curl, and no root.

func StageHookBinary

func StageHookBinary(pluginDir string) (string, error)

StageHookBinary copies the running daemon to a host path the container runtime can execute, and returns that path.

The hook runs as a bare process on the node, so it cannot be the binary inside this container. The kubelet plugin directory is a hostPath mounted at the same path on both sides, which makes a copy written here valid in the CDI spec the runtime reads. This is the model nvidia-ctk uses.

func StartNodePlugin

func StartNodePlugin(ctx context.Context, driver *Driver, kube kubernetes.Interface, cfg PluginConfig) (*kubeletplugin.Helper, error)

func ThunderClientName

func ThunderClientName(claimUID types.UID) string

func ThunderGuestSetupSecretName

func ThunderGuestSetupSecretName(claimName string) string

ThunderGuestSetupSecretName is the Secret a VM mounts to set itself up as a Thunder client.

Types

type AllocatedDevice

type AllocatedDevice struct {
	RequestName string
	PoolName    string
	DeviceName  string
	ShareID     *types.UID
}

AllocatedDevice is one GPU the scheduler assigned to a claim.

type Allocation

type Allocation struct {
	ClaimUID       types.UID
	ClaimNamespace string
	ClaimName      string

	// Devices holds every device the claim was allocated from this driver.
	// The operator publishes one device per GPU, so a multi-GPU claim has one
	// entry per GPU and GPUCount is simply how many there are.
	Devices []AllocatedDevice

	RequestName string
	PoolName    string
	DeviceName  string
	ShareID     *types.UID

	Consumer ResourceConsumer
	NodeName string
	Zone     string
	GPUType  string
	GPUCount int64
}

type CDIDeviceStore

type CDIDeviceStore interface {
	Create(ctx context.Context, allocation Allocation, token string) (qualifiedName string, err error)
	Remove(ctx context.Context, qualifiedName string) error
	// StagedClientID reports the Thunder client the CDI hook enrolled for this
	// device, or "" if no container ever started and none was created.
	StagedClientID(qualifiedName string) string
}

type CDIHookOptions

type CDIHookOptions struct {
	// StateDir is the per-claim directory the daemon staged the enrollment
	// token into, and where the exchanged config is cached.
	StateDir string
	// CentralURL and TelemetryURL configure the client being enrolled.
	CentralURL   string
	TelemetryURL string
	// InstallURL, ArtifactBaseURL, LibthunderURL and LibthunderSHA256 locate
	// the client library.
	InstallURL       string
	ArtifactBaseURL  string
	LibthunderURL    string
	LibthunderSHA256 string
	// CacheDir holds this node's shared copy of the library.
	CacheDir string
	// CABundlePath is the node's trust store, staged into containers that
	// have none of their own.
	CABundlePath string
	// ClientName is what this client is called in Thunder. It is the pod name,
	// so an enrollment can be traced back to the workload holding it.
	ClientName string
}

CDIHookOptions is what the CDI spec encodes into the hook's argv.

func ParseCDIHookArgs

func ParseCDIHookArgs(args []string) (CDIHookOptions, error)

ParseCDIHookArgs reads the flags the CDI spec encodes into the hook argv.

type Config

type Config struct {
	Node string
	Zone string
	// AdvertisedIP is the address Thunder clients use to reach this node.
	// When empty it is resolved from AdvertisedIPLabel and then from the
	// node's own IP. See resolveNodeAttributes.
	AdvertisedIP        string
	MinDriverVersion    string
	ThunderAPIURL       string
	ThunderAPIToken     string
	HostRoot            string
	LibCUDAPath         string
	LibNVMLPath         string
	NVSMIPath           string
	ZoneLabel           string
	AdvertisedIPLabel   string
	HostTargetPID       string
	DRAEnabled          bool
	DRADriverName       string
	CDISpecDir          string
	ThunderClientNS     string
	KubeletPluginDir    string
	KubeletRegistrarDir string
	// ThunderInstallURL is read, not run: the CDI hook takes the pinned
	// libthunder.so digest from it so a node stages the same build the
	// installer would have.
	ThunderInstallURL   string
	ThunderTelemetryURL string
	ArtifactBaseURL     string
	// LibthunderURL and LibthunderSHA256 pin the library explicitly and
	// skip the installer entirely.
	LibthunderURL    string
	LibthunderSHA256 string
	CABundlePath     string
}

func ConfigFromEnv

func ConfigFromEnv() (Config, error)

type Driver

type Driver struct {
	DriverName string
	NodeName   string
	Kube       kubernetes.Interface
	Tokens     TokenIssuer
	Clients    ThunderClientStore
	CDI        CDIDeviceStore
	Guest      GuestConfigStore
	Logger     *slog.Logger
}

func (*Driver) CDIInstallCommand

func (d *Driver) CDIInstallCommand() string

func (*Driver) HandleError

func (d *Driver) HandleError(ctx context.Context, err error, msg string)

func (*Driver) PrepareResourceClaims

func (d *Driver) PrepareResourceClaims(ctx context.Context, claims []*resourcev1.ResourceClaim) (map[types.UID]kubeletplugin.PrepareResult, error)

func (*Driver) UnprepareResourceClaims

func (d *Driver) UnprepareResourceClaims(ctx context.Context, claims []kubeletplugin.NamespacedObject) (map[types.UID]error, error)

type FileCDIDeviceStore

type FileCDIDeviceStore struct {
	SpecDir  string
	StateDir string
	Kind     string

	LibCUDAPath          string
	LibNVMLPath          string
	NVSMIPath            string
	ClientInstallCommand string

	// HookPath is the host executable the container runtime runs while
	// creating a container. Empty disables the hook, which leaves the
	// container to supply its own Thunder client.
	HookPath string
	// CacheDir is where the hook keeps this node's copy of libthunder.so.
	CacheDir string
	// The hook is told where to enrol and where to fetch the library, since
	// it runs as a bare process on the host with none of the daemon's config.
	CentralURL       string
	TelemetryURL     string
	InstallURL       string
	ArtifactBaseURL  string
	LibthunderURL    string
	LibthunderSHA256 string
	CABundlePath     string
}

func NewFileCDIDeviceStore

func NewFileCDIDeviceStore(specDir string) *FileCDIDeviceStore

func (*FileCDIDeviceStore) Create

func (s *FileCDIDeviceStore) Create(ctx context.Context, allocation Allocation, token string) (string, error)

func (*FileCDIDeviceStore) Remove

func (s *FileCDIDeviceStore) Remove(ctx context.Context, qualifiedName string) error

func (*FileCDIDeviceStore) StagedClientID

func (s *FileCDIDeviceStore) StagedClientID(qualifiedName string) string

StagedClientID reports the Thunder client the CDI hook enrolled for this device. The hook caches the exchanged config in the claim's state dir, and the client ID in it is the only record of the enrollment the daemon has: the exchange happens in the hook, on container create, long after prepare.

An empty string means no container ever started for this claim, so no client was created and there is nothing to revoke.

type GuestArtifacts

type GuestArtifacts struct {
	Namespace  string
	SecretName string
}

type GuestConfigStore

type GuestConfigStore interface {
	Create(ctx context.Context, allocation Allocation, token string, installCommand string) (GuestArtifacts, error)
	Remove(ctx context.Context, artifacts GuestArtifacts) error
}

type KubernetesGuestConfigStore

type KubernetesGuestConfigStore struct {
	Client kubernetes.Interface
}

func NewKubernetesGuestConfigStore

func NewKubernetesGuestConfigStore(client kubernetes.Interface) *KubernetesGuestConfigStore

func (*KubernetesGuestConfigStore) Create

func (s *KubernetesGuestConfigStore) Create(ctx context.Context, allocation Allocation, token string, installCommand string) (GuestArtifacts, error)

func (*KubernetesGuestConfigStore) Remove

type KubernetesThunderClientStore

type KubernetesThunderClientStore struct {
	Client    dynamic.Interface
	Namespace string
}

func NewKubernetesThunderClientStore

func NewKubernetesThunderClientStore(client dynamic.Interface, namespace string) *KubernetesThunderClientStore

func (*KubernetesThunderClientStore) Delete

func (s *KubernetesThunderClientStore) Delete(ctx context.Context, claimUID types.UID) error

Delete releases the finalizer and removes the resource. The caller has already revoked the Thunder enrollment by this point, so holding the resource any longer would serve no purpose.

func (*KubernetesThunderClientStore) Get

func (*KubernetesThunderClientStore) Upsert

type LibthunderArtifact

type LibthunderArtifact struct {
	URL    string
	SHA256 string
}

LibthunderArtifact identifies one build of the client library.

type LibthunderCache

type LibthunderCache struct {
	// Dir is the cache root. It must be readable by the CDI hook, which means
	// somewhere under the kubelet plugin directory.
	Dir string
	// InstallURL is the installer whose pin is authoritative. Ignored when
	// both URL and SHA256 are set.
	InstallURL string
	// URL and SHA256 pin the library explicitly, skipping the installer.
	URL    string
	SHA256 string
	// ArtifactBaseURL overrides only the host, keeping the installer's digest.
	ArtifactBaseURL string
	// HTTP is used for both fetches. Nil means http.DefaultClient.
	HTTP *http.Client
	// contains filtered or unexported fields
}

LibthunderCache resolves and caches libthunder.so on a node.

func (*LibthunderCache) Ensure

func (c *LibthunderCache) Ensure(ctx context.Context) (string, error)

Ensure downloads libthunder.so unless the node already has that exact build, and returns the path to it. The digest is verified before the file is published, so a truncated or tampered download never becomes the library a workload preloads.

func (*LibthunderCache) Path

func (c *LibthunderCache) Path(artifact LibthunderArtifact) string

Path is where a build is cached, whether or not it has been downloaded. The digest is in the name, so a new release lands as a new file rather than silently replacing the one running containers were staged from.

func (*LibthunderCache) Resolve

Resolve reports which build of libthunder.so this node should stage. The answer is memoised, since it only changes when Thunder ships a release.

type NodeInfo

type NodeInfo struct {
	Labels map[string]string
	// InternalIP and ExternalIP come from status.addresses and are the
	// defaults for the advertised IP, in that order.
	InternalIP string
	ExternalIP string
}

NodeInfo is the subset of a Kubernetes Node the daemon needs before it can build in-cluster clients.

func (NodeInfo) NodeIP

func (n NodeInfo) NodeIP() string

NodeIP is the address the daemon advertises when neither the environment nor a node label pins one.

type PluginConfig

type PluginConfig struct {
	DriverName       string
	NodeName         string
	KubeletPluginDir string
	RegistrarDir     string
}

type ResourceConsumer

type ResourceConsumer struct {
	APIGroup  string
	Resource  string
	Namespace string
	Name      string
	UID       types.UID
}

type ThunderClient

type ThunderClient struct {
	ClaimUID       types.UID
	ClaimNamespace string
	ClaimName      string

	GPUType  string
	GPUCount int64
	Zone     string
	NodeName string

	RequestName string
	PoolName    string
	DeviceName  string
	ShareID     *types.UID
	Consumer    ResourceConsumer

	CDIName           string
	EnrollmentTokenID string
	GuestNamespace    string
	GuestSecret       string
	CreatedAt         time.Time
	UpdatedAt         time.Time
}

type ThunderClientConfig

type ThunderClientConfig struct {
	DeviceID                 string `json:"deviceId"`
	ClientID                 string `json:"clientId"`
	OrgID                    string `json:"orgId"`
	GPUType                  string `json:"gpuType"`
	GPUCount                 int    `json:"gpuCount"`
	CentralAPIURL            string `json:"centralApiUrl"`
	AuthToken                string `json:"authToken"`
	Claims                   string `json:"claims"`
	EnableGRPCTLS            bool   `json:"enableGrpcTls"`
	ThunderdDiscoveryEnabled bool   `json:"thunderdDiscoveryEnabled"`
	TelemetryCollector       string `json:"telemetryCollector"`
}

ThunderClientConfig is /etc/thunder/config.json, the file libthunder.so reads to learn which client it is and which control plane to talk to. The field set and JSON shape mirror what the Thunder installer writes, so a container staged by the CDI hook is indistinguishable from one set up by `curl | sh`.

func ExchangeClientEnrollment

func ExchangeClientEnrollment(ctx context.Context, httpClient *http.Client, centralURL, telemetryURL, enrollmentToken, hostname string) (ThunderClientConfig, error)

ExchangeClientEnrollment spends a single-use client enrollment token and returns the config the container needs. The token is consumed by this call, which is why it happens once per claim and is cached afterwards.

type ThunderClientStore

type ThunderClientStore interface {
	Get(ctx context.Context, claimUID types.UID) (*ThunderClient, error)
	Upsert(ctx context.Context, client ThunderClient) error
	Delete(ctx context.Context, claimUID types.UID) error
}

type ThunderTokenIssuer

type ThunderTokenIssuer struct {
	Client *thunder.Client
	ZoneID string
}

ThunderTokenIssuer adapts the Thunder SDK to the daemon DRA plugin.

func (ThunderTokenIssuer) Mint

func (i ThunderTokenIssuer) Mint(ctx context.Context, allocation Allocation) (string, string, time.Time, error)

func (ThunderTokenIssuer) Revoke

func (i ThunderTokenIssuer) Revoke(ctx context.Context, tokenID string) error

func (ThunderTokenIssuer) RevokeClient

func (i ThunderTokenIssuer) RevokeClient(ctx context.Context, clientID string) error

RevokeClient revokes a client that was enrolled by exchanging a token.

type TokenIssuer

type TokenIssuer interface {
	Mint(ctx context.Context, allocation Allocation) (tokenID string, token string, expiresAt time.Time, err error)
	Revoke(ctx context.Context, tokenID string) error
	// RevokeClient revokes the client an enrollment token was exchanged for.
	// Revoking the token alone does not: once spent, the token and the client
	// it produced are separate objects in Thunder.
	RevokeClient(ctx context.Context, clientID string) error
}

Jump to

Keyboard shortcuts

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