opts.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cni
  14. import (
  15. "fmt"
  16. "os"
  17. "sort"
  18. "strings"
  19. cnilibrary "github.com/containernetworking/cni/libcni"
  20. "github.com/containernetworking/cni/pkg/invoke"
  21. "github.com/containernetworking/cni/pkg/version"
  22. )
  23. // Opt sets options for a CNI instance
  24. type Opt func(c *libcni) error
  25. // WithInterfacePrefix sets the prefix for network interfaces
  26. // e.g. eth or wlan
  27. func WithInterfacePrefix(prefix string) Opt {
  28. return func(c *libcni) error {
  29. c.prefix = prefix
  30. return nil
  31. }
  32. }
  33. // WithPluginDir can be used to set the locations of
  34. // the cni plugin binaries
  35. func WithPluginDir(dirs []string) Opt {
  36. return func(c *libcni) error {
  37. c.pluginDirs = dirs
  38. c.cniConfig = cnilibrary.NewCNIConfig(
  39. dirs,
  40. &invoke.DefaultExec{
  41. RawExec: &invoke.RawExec{Stderr: os.Stderr},
  42. PluginDecoder: version.PluginDecoder{},
  43. },
  44. )
  45. return nil
  46. }
  47. }
  48. // WithPluginConfDir can be used to configure the
  49. // cni configuration directory.
  50. func WithPluginConfDir(dir string) Opt {
  51. return func(c *libcni) error {
  52. c.pluginConfDir = dir
  53. return nil
  54. }
  55. }
  56. // WithPluginMaxConfNum can be used to configure the
  57. // max cni plugin config file num.
  58. func WithPluginMaxConfNum(max int) Opt {
  59. return func(c *libcni) error {
  60. c.pluginMaxConfNum = max
  61. return nil
  62. }
  63. }
  64. // WithMinNetworkCount can be used to configure the
  65. // minimum networks to be configured and initialized
  66. // for the status to report success. By default its 1.
  67. func WithMinNetworkCount(count int) Opt {
  68. return func(c *libcni) error {
  69. c.networkCount = count
  70. return nil
  71. }
  72. }
  73. // WithLoNetwork can be used to load the loopback
  74. // network config.
  75. func WithLoNetwork(c *libcni) error {
  76. loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
  77. "cniVersion": "0.3.1",
  78. "name": "cni-loopback",
  79. "plugins": [{
  80. "type": "loopback"
  81. }]
  82. }`))
  83. c.networks = append(c.networks, &Network{
  84. cni: c.cniConfig,
  85. config: loConfig,
  86. ifName: "lo",
  87. })
  88. return nil
  89. }
  90. // WithConf can be used to load config directly
  91. // from byte.
  92. func WithConf(bytes []byte) Opt {
  93. return WithConfIndex(bytes, 0)
  94. }
  95. // WithConfIndex can be used to load config directly
  96. // from byte and set the interface name's index.
  97. func WithConfIndex(bytes []byte, index int) Opt {
  98. return func(c *libcni) error {
  99. conf, err := cnilibrary.ConfFromBytes(bytes)
  100. if err != nil {
  101. return err
  102. }
  103. confList, err := cnilibrary.ConfListFromConf(conf)
  104. if err != nil {
  105. return err
  106. }
  107. c.networks = append(c.networks, &Network{
  108. cni: c.cniConfig,
  109. config: confList,
  110. ifName: getIfName(c.prefix, index),
  111. })
  112. return nil
  113. }
  114. }
  115. // WithConfFile can be used to load network config
  116. // from an .conf file. Supported with absolute fileName
  117. // with path only.
  118. func WithConfFile(fileName string) Opt {
  119. return func(c *libcni) error {
  120. conf, err := cnilibrary.ConfFromFile(fileName)
  121. if err != nil {
  122. return err
  123. }
  124. // upconvert to conf list
  125. confList, err := cnilibrary.ConfListFromConf(conf)
  126. if err != nil {
  127. return err
  128. }
  129. c.networks = append(c.networks, &Network{
  130. cni: c.cniConfig,
  131. config: confList,
  132. ifName: getIfName(c.prefix, 0),
  133. })
  134. return nil
  135. }
  136. }
  137. // WithConfListBytes can be used to load network config list directly
  138. // from byte
  139. func WithConfListBytes(bytes []byte) Opt {
  140. return func(c *libcni) error {
  141. confList, err := cnilibrary.ConfListFromBytes(bytes)
  142. if err != nil {
  143. return err
  144. }
  145. i := len(c.networks)
  146. c.networks = append(c.networks, &Network{
  147. cni: c.cniConfig,
  148. config: confList,
  149. ifName: getIfName(c.prefix, i),
  150. })
  151. return nil
  152. }
  153. }
  154. // WithConfListFile can be used to load network config
  155. // from an .conflist file. Supported with absolute fileName
  156. // with path only.
  157. func WithConfListFile(fileName string) Opt {
  158. return func(c *libcni) error {
  159. confList, err := cnilibrary.ConfListFromFile(fileName)
  160. if err != nil {
  161. return err
  162. }
  163. i := len(c.networks)
  164. c.networks = append(c.networks, &Network{
  165. cni: c.cniConfig,
  166. config: confList,
  167. ifName: getIfName(c.prefix, i),
  168. })
  169. return nil
  170. }
  171. }
  172. // WithDefaultConf can be used to detect the default network
  173. // config file from the configured cni config directory and load
  174. // it.
  175. // Since the CNI spec does not specify a way to detect default networks,
  176. // the convention chosen is - the first network configuration in the sorted
  177. // list of network conf files as the default network.
  178. func WithDefaultConf(c *libcni) error {
  179. return loadFromConfDir(c, c.pluginMaxConfNum)
  180. }
  181. // WithAllConf can be used to detect all network config
  182. // files from the configured cni config directory and load
  183. // them.
  184. func WithAllConf(c *libcni) error {
  185. return loadFromConfDir(c, 0)
  186. }
  187. // loadFromConfDir detects network config files from the
  188. // configured cni config directory and load them. max is
  189. // the maximum network config to load (max i<= 0 means no limit).
  190. func loadFromConfDir(c *libcni, max int) error {
  191. files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
  192. switch {
  193. case err != nil:
  194. return fmt.Errorf("failed to read config file: %v: %w", err, ErrRead)
  195. case len(files) == 0:
  196. return fmt.Errorf("no network config found in %s: %w", c.pluginConfDir, ErrCNINotInitialized)
  197. }
  198. // files contains the network config files associated with cni network.
  199. // Use lexicographical way as a defined order for network config files.
  200. sort.Strings(files)
  201. // Since the CNI spec does not specify a way to detect default networks,
  202. // the convention chosen is - the first network configuration in the sorted
  203. // list of network conf files as the default network and choose the default
  204. // interface provided during init as the network interface for this default
  205. // network. For every other network use a generated interface id.
  206. i := 0
  207. var networks []*Network
  208. for _, confFile := range files {
  209. var confList *cnilibrary.NetworkConfigList
  210. if strings.HasSuffix(confFile, ".conflist") {
  211. confList, err = cnilibrary.ConfListFromFile(confFile)
  212. if err != nil {
  213. return fmt.Errorf("failed to load CNI config list file %s: %v: %w", confFile, err, ErrInvalidConfig)
  214. }
  215. } else {
  216. conf, err := cnilibrary.ConfFromFile(confFile)
  217. if err != nil {
  218. return fmt.Errorf("failed to load CNI config file %s: %v: %w", confFile, err, ErrInvalidConfig)
  219. }
  220. // Ensure the config has a "type" so we know what plugin to run.
  221. // Also catches the case where somebody put a conflist into a conf file.
  222. if conf.Network.Type == "" {
  223. return fmt.Errorf("network type not found in %s: %w", confFile, ErrInvalidConfig)
  224. }
  225. confList, err = cnilibrary.ConfListFromConf(conf)
  226. if err != nil {
  227. return fmt.Errorf("failed to convert CNI config file %s to CNI config list: %v: %w", confFile, err, ErrInvalidConfig)
  228. }
  229. }
  230. if len(confList.Plugins) == 0 {
  231. return fmt.Errorf("CNI config list in config file %s has no networks, skipping: %w", confFile, ErrInvalidConfig)
  232. }
  233. networks = append(networks, &Network{
  234. cni: c.cniConfig,
  235. config: confList,
  236. ifName: getIfName(c.prefix, i),
  237. })
  238. i++
  239. if i == max {
  240. break
  241. }
  242. }
  243. if len(networks) == 0 {
  244. return fmt.Errorf("no valid networks found in %s: %w", c.pluginDirs, ErrCNINotInitialized)
  245. }
  246. c.networks = append(c.networks, networks...)
  247. return nil
  248. }