123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- /*
- Copyright The containerd Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package cni
- import (
- "fmt"
- "os"
- "sort"
- "strings"
- cnilibrary "github.com/containernetworking/cni/libcni"
- "github.com/containernetworking/cni/pkg/invoke"
- "github.com/containernetworking/cni/pkg/version"
- )
- // Opt sets options for a CNI instance
- type Opt func(c *libcni) error
- // WithInterfacePrefix sets the prefix for network interfaces
- // e.g. eth or wlan
- func WithInterfacePrefix(prefix string) Opt {
- return func(c *libcni) error {
- c.prefix = prefix
- return nil
- }
- }
- // WithPluginDir can be used to set the locations of
- // the cni plugin binaries
- func WithPluginDir(dirs []string) Opt {
- return func(c *libcni) error {
- c.pluginDirs = dirs
- c.cniConfig = cnilibrary.NewCNIConfig(
- dirs,
- &invoke.DefaultExec{
- RawExec: &invoke.RawExec{Stderr: os.Stderr},
- PluginDecoder: version.PluginDecoder{},
- },
- )
- return nil
- }
- }
- // WithPluginConfDir can be used to configure the
- // cni configuration directory.
- func WithPluginConfDir(dir string) Opt {
- return func(c *libcni) error {
- c.pluginConfDir = dir
- return nil
- }
- }
- // WithPluginMaxConfNum can be used to configure the
- // max cni plugin config file num.
- func WithPluginMaxConfNum(max int) Opt {
- return func(c *libcni) error {
- c.pluginMaxConfNum = max
- return nil
- }
- }
- // WithMinNetworkCount can be used to configure the
- // minimum networks to be configured and initialized
- // for the status to report success. By default its 1.
- func WithMinNetworkCount(count int) Opt {
- return func(c *libcni) error {
- c.networkCount = count
- return nil
- }
- }
- // WithLoNetwork can be used to load the loopback
- // network config.
- func WithLoNetwork(c *libcni) error {
- loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
- "cniVersion": "0.3.1",
- "name": "cni-loopback",
- "plugins": [{
- "type": "loopback"
- }]
- }`))
- c.networks = append(c.networks, &Network{
- cni: c.cniConfig,
- config: loConfig,
- ifName: "lo",
- })
- return nil
- }
- // WithConf can be used to load config directly
- // from byte.
- func WithConf(bytes []byte) Opt {
- return WithConfIndex(bytes, 0)
- }
- // WithConfIndex can be used to load config directly
- // from byte and set the interface name's index.
- func WithConfIndex(bytes []byte, index int) Opt {
- return func(c *libcni) error {
- conf, err := cnilibrary.ConfFromBytes(bytes)
- if err != nil {
- return err
- }
- confList, err := cnilibrary.ConfListFromConf(conf)
- if err != nil {
- return err
- }
- c.networks = append(c.networks, &Network{
- cni: c.cniConfig,
- config: confList,
- ifName: getIfName(c.prefix, index),
- })
- return nil
- }
- }
- // WithConfFile can be used to load network config
- // from an .conf file. Supported with absolute fileName
- // with path only.
- func WithConfFile(fileName string) Opt {
- return func(c *libcni) error {
- conf, err := cnilibrary.ConfFromFile(fileName)
- if err != nil {
- return err
- }
- // upconvert to conf list
- confList, err := cnilibrary.ConfListFromConf(conf)
- if err != nil {
- return err
- }
- c.networks = append(c.networks, &Network{
- cni: c.cniConfig,
- config: confList,
- ifName: getIfName(c.prefix, 0),
- })
- return nil
- }
- }
- // WithConfListBytes can be used to load network config list directly
- // from byte
- func WithConfListBytes(bytes []byte) Opt {
- return func(c *libcni) error {
- confList, err := cnilibrary.ConfListFromBytes(bytes)
- if err != nil {
- return err
- }
- i := len(c.networks)
- c.networks = append(c.networks, &Network{
- cni: c.cniConfig,
- config: confList,
- ifName: getIfName(c.prefix, i),
- })
- return nil
- }
- }
- // WithConfListFile can be used to load network config
- // from an .conflist file. Supported with absolute fileName
- // with path only.
- func WithConfListFile(fileName string) Opt {
- return func(c *libcni) error {
- confList, err := cnilibrary.ConfListFromFile(fileName)
- if err != nil {
- return err
- }
- i := len(c.networks)
- c.networks = append(c.networks, &Network{
- cni: c.cniConfig,
- config: confList,
- ifName: getIfName(c.prefix, i),
- })
- return nil
- }
- }
- // WithDefaultConf can be used to detect the default network
- // config file from the configured cni config directory and load
- // it.
- // Since the CNI spec does not specify a way to detect default networks,
- // the convention chosen is - the first network configuration in the sorted
- // list of network conf files as the default network.
- func WithDefaultConf(c *libcni) error {
- return loadFromConfDir(c, c.pluginMaxConfNum)
- }
- // WithAllConf can be used to detect all network config
- // files from the configured cni config directory and load
- // them.
- func WithAllConf(c *libcni) error {
- return loadFromConfDir(c, 0)
- }
- // loadFromConfDir detects network config files from the
- // configured cni config directory and load them. max is
- // the maximum network config to load (max i<= 0 means no limit).
- func loadFromConfDir(c *libcni, max int) error {
- files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
- switch {
- case err != nil:
- return fmt.Errorf("failed to read config file: %v: %w", err, ErrRead)
- case len(files) == 0:
- return fmt.Errorf("no network config found in %s: %w", c.pluginConfDir, ErrCNINotInitialized)
- }
- // files contains the network config files associated with cni network.
- // Use lexicographical way as a defined order for network config files.
- sort.Strings(files)
- // Since the CNI spec does not specify a way to detect default networks,
- // the convention chosen is - the first network configuration in the sorted
- // list of network conf files as the default network and choose the default
- // interface provided during init as the network interface for this default
- // network. For every other network use a generated interface id.
- i := 0
- var networks []*Network
- for _, confFile := range files {
- var confList *cnilibrary.NetworkConfigList
- if strings.HasSuffix(confFile, ".conflist") {
- confList, err = cnilibrary.ConfListFromFile(confFile)
- if err != nil {
- return fmt.Errorf("failed to load CNI config list file %s: %v: %w", confFile, err, ErrInvalidConfig)
- }
- } else {
- conf, err := cnilibrary.ConfFromFile(confFile)
- if err != nil {
- return fmt.Errorf("failed to load CNI config file %s: %v: %w", confFile, err, ErrInvalidConfig)
- }
- // Ensure the config has a "type" so we know what plugin to run.
- // Also catches the case where somebody put a conflist into a conf file.
- if conf.Network.Type == "" {
- return fmt.Errorf("network type not found in %s: %w", confFile, ErrInvalidConfig)
- }
- confList, err = cnilibrary.ConfListFromConf(conf)
- if err != nil {
- return fmt.Errorf("failed to convert CNI config file %s to CNI config list: %v: %w", confFile, err, ErrInvalidConfig)
- }
- }
- if len(confList.Plugins) == 0 {
- return fmt.Errorf("CNI config list in config file %s has no networks, skipping: %w", confFile, ErrInvalidConfig)
- }
- networks = append(networks, &Network{
- cni: c.cniConfig,
- config: confList,
- ifName: getIfName(c.prefix, i),
- })
- i++
- if i == max {
- break
- }
- }
- if len(networks) == 0 {
- return fmt.Errorf("no valid networks found in %s: %w", c.pluginDirs, ErrCNINotInitialized)
- }
- c.networks = append(c.networks, networks...)
- return nil
- }
|