// Package cri inspects the container runtime configuration on the node. package cri import ( "errors" "fmt" "os" "sort" "github.com/pelletier/go-toml/v2" ) // ErrConfigNotFound is returned when the containerd config file does not // exist. var ErrConfigNotFound = errors.New("containerd config not found") // Containerd holds the CRI runtime handlers configured in containerd. type Containerd struct { path string handlers map[string]struct{} } // Load parses the containerd config at the given path and collects the CRI // runtime handler names from // [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.] (and the // legacy [plugins.cri.containerd.runtimes.] spelling). func Load(path string) (*Containerd, error) { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("%w: %s", ErrConfigNotFound, path) } return nil, err } var tree map[string]interface{} if err := toml.Unmarshal(data, &tree); err != nil { return nil, fmt.Errorf("parse %s: %w", path, err) } c := &Containerd{path: path, handlers: map[string]struct{}{}} c.collectHandlers(tree) return c, nil } func (c *Containerd) collectHandlers(tree map[string]interface{}) { plugins, _ := tree["plugins"].(map[string]interface{}) for _, criName := range []string{"io.containerd.grpc.v1.cri", "cri"} { criSection, _ := plugins[criName].(map[string]interface{}) containerdSection, _ := criSection["containerd"].(map[string]interface{}) runtimes, _ := containerdSection["runtimes"].(map[string]interface{}) for handler := range runtimes { c.handlers[handler] = struct{}{} } } } // HandlerNames returns the configured runtime handler names, sorted. func (c *Containerd) HandlerNames() []string { names := make([]string, 0, len(c.handlers)) for h := range c.handlers { names = append(names, h) } sort.Strings(names) return names } // HasHandler reports whether the named runtime handler is configured. func (c *Containerd) HasHandler(name string) bool { _, ok := c.handlers[name] return ok }