api.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. // Copyright 2015 CNI authors
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package libcni
  15. // Note this is the actual implementation of the CNI specification, which
  16. // is reflected in the https://github.com/containernetworking/cni/blob/master/SPEC.md file
  17. // it is typically bundled into runtime providers (i.e. containerd or cri-o would use this
  18. // before calling runc or hcsshim). It is also bundled into CNI providers as well, for example,
  19. // to add an IP to a container, to parse the configuration of the CNI and so on.
  20. import (
  21. "context"
  22. "encoding/json"
  23. "fmt"
  24. "io/ioutil"
  25. "os"
  26. "path/filepath"
  27. "strings"
  28. "github.com/containernetworking/cni/pkg/invoke"
  29. "github.com/containernetworking/cni/pkg/types"
  30. "github.com/containernetworking/cni/pkg/types/create"
  31. "github.com/containernetworking/cni/pkg/utils"
  32. "github.com/containernetworking/cni/pkg/version"
  33. )
  34. var (
  35. CacheDir = "/var/lib/cni"
  36. )
  37. const (
  38. CNICacheV1 = "cniCacheV1"
  39. )
  40. // A RuntimeConf holds the arguments to one invocation of a CNI plugin
  41. // excepting the network configuration, with the nested exception that
  42. // the `runtimeConfig` from the network configuration is included
  43. // here.
  44. type RuntimeConf struct {
  45. ContainerID string
  46. NetNS string
  47. IfName string
  48. Args [][2]string
  49. // A dictionary of capability-specific data passed by the runtime
  50. // to plugins as top-level keys in the 'runtimeConfig' dictionary
  51. // of the plugin's stdin data. libcni will ensure that only keys
  52. // in this map which match the capabilities of the plugin are passed
  53. // to the plugin
  54. CapabilityArgs map[string]interface{}
  55. // DEPRECATED. Will be removed in a future release.
  56. CacheDir string
  57. }
  58. type NetworkConfig struct {
  59. Network *types.NetConf
  60. Bytes []byte
  61. }
  62. type NetworkConfigList struct {
  63. Name string
  64. CNIVersion string
  65. DisableCheck bool
  66. Plugins []*NetworkConfig
  67. Bytes []byte
  68. }
  69. type CNI interface {
  70. AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
  71. CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
  72. DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
  73. GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
  74. GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
  75. AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
  76. CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
  77. DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
  78. GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
  79. GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
  80. ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
  81. ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
  82. }
  83. type CNIConfig struct {
  84. Path []string
  85. exec invoke.Exec
  86. cacheDir string
  87. }
  88. // CNIConfig implements the CNI interface
  89. var _ CNI = &CNIConfig{}
  90. // NewCNIConfig returns a new CNIConfig object that will search for plugins
  91. // in the given paths and use the given exec interface to run those plugins,
  92. // or if the exec interface is not given, will use a default exec handler.
  93. func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
  94. return NewCNIConfigWithCacheDir(path, "", exec)
  95. }
  96. // NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
  97. // in the given paths use the given exec interface to run those plugins,
  98. // or if the exec interface is not given, will use a default exec handler.
  99. // The given cache directory will be used for temporary data storage when needed.
  100. func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
  101. return &CNIConfig{
  102. Path: path,
  103. cacheDir: cacheDir,
  104. exec: exec,
  105. }
  106. }
  107. func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
  108. var err error
  109. inject := map[string]interface{}{
  110. "name": name,
  111. "cniVersion": cniVersion,
  112. }
  113. // Add previous plugin result
  114. if prevResult != nil {
  115. inject["prevResult"] = prevResult
  116. }
  117. // Ensure every config uses the same name and version
  118. orig, err = InjectConf(orig, inject)
  119. if err != nil {
  120. return nil, err
  121. }
  122. return injectRuntimeConfig(orig, rt)
  123. }
  124. // This function takes a libcni RuntimeConf structure and injects values into
  125. // a "runtimeConfig" dictionary in the CNI network configuration JSON that
  126. // will be passed to the plugin on stdin.
  127. //
  128. // Only "capabilities arguments" passed by the runtime are currently injected.
  129. // These capabilities arguments are filtered through the plugin's advertised
  130. // capabilities from its config JSON, and any keys in the CapabilityArgs
  131. // matching plugin capabilities are added to the "runtimeConfig" dictionary
  132. // sent to the plugin via JSON on stdin. For example, if the plugin's
  133. // capabilities include "portMappings", and the CapabilityArgs map includes a
  134. // "portMappings" key, that key and its value are added to the "runtimeConfig"
  135. // dictionary to be passed to the plugin's stdin.
  136. func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
  137. var err error
  138. rc := make(map[string]interface{})
  139. for capability, supported := range orig.Network.Capabilities {
  140. if !supported {
  141. continue
  142. }
  143. if data, ok := rt.CapabilityArgs[capability]; ok {
  144. rc[capability] = data
  145. }
  146. }
  147. if len(rc) > 0 {
  148. orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
  149. if err != nil {
  150. return nil, err
  151. }
  152. }
  153. return orig, nil
  154. }
  155. // ensure we have a usable exec if the CNIConfig was not given one
  156. func (c *CNIConfig) ensureExec() invoke.Exec {
  157. if c.exec == nil {
  158. c.exec = &invoke.DefaultExec{
  159. RawExec: &invoke.RawExec{Stderr: os.Stderr},
  160. PluginDecoder: version.PluginDecoder{},
  161. }
  162. }
  163. return c.exec
  164. }
  165. type cachedInfo struct {
  166. Kind string `json:"kind"`
  167. ContainerID string `json:"containerId"`
  168. Config []byte `json:"config"`
  169. IfName string `json:"ifName"`
  170. NetworkName string `json:"networkName"`
  171. CniArgs [][2]string `json:"cniArgs,omitempty"`
  172. CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
  173. RawResult map[string]interface{} `json:"result,omitempty"`
  174. Result types.Result `json:"-"`
  175. }
  176. // getCacheDir returns the cache directory in this order:
  177. // 1) global cacheDir from CNIConfig object
  178. // 2) deprecated cacheDir from RuntimeConf object
  179. // 3) fall back to default cache directory
  180. func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
  181. if c.cacheDir != "" {
  182. return c.cacheDir
  183. }
  184. if rt.CacheDir != "" {
  185. return rt.CacheDir
  186. }
  187. return CacheDir
  188. }
  189. func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
  190. if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
  191. return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
  192. }
  193. return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
  194. }
  195. func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
  196. cached := cachedInfo{
  197. Kind: CNICacheV1,
  198. ContainerID: rt.ContainerID,
  199. Config: config,
  200. IfName: rt.IfName,
  201. NetworkName: netName,
  202. CniArgs: rt.Args,
  203. CapabilityArgs: rt.CapabilityArgs,
  204. }
  205. // We need to get type.Result into cachedInfo as JSON map
  206. // Marshal to []byte, then Unmarshal into cached.RawResult
  207. data, err := json.Marshal(result)
  208. if err != nil {
  209. return err
  210. }
  211. err = json.Unmarshal(data, &cached.RawResult)
  212. if err != nil {
  213. return err
  214. }
  215. newBytes, err := json.Marshal(&cached)
  216. if err != nil {
  217. return err
  218. }
  219. fname, err := c.getCacheFilePath(netName, rt)
  220. if err != nil {
  221. return err
  222. }
  223. if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
  224. return err
  225. }
  226. return ioutil.WriteFile(fname, newBytes, 0600)
  227. }
  228. func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
  229. fname, err := c.getCacheFilePath(netName, rt)
  230. if err != nil {
  231. // Ignore error
  232. return nil
  233. }
  234. return os.Remove(fname)
  235. }
  236. func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
  237. var bytes []byte
  238. fname, err := c.getCacheFilePath(netName, rt)
  239. if err != nil {
  240. return nil, nil, err
  241. }
  242. bytes, err = ioutil.ReadFile(fname)
  243. if err != nil {
  244. // Ignore read errors; the cached result may not exist on-disk
  245. return nil, nil, nil
  246. }
  247. unmarshaled := cachedInfo{}
  248. if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
  249. return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %w", netName, err)
  250. }
  251. if unmarshaled.Kind != CNICacheV1 {
  252. return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
  253. }
  254. newRt := *rt
  255. if unmarshaled.CniArgs != nil {
  256. newRt.Args = unmarshaled.CniArgs
  257. }
  258. newRt.CapabilityArgs = unmarshaled.CapabilityArgs
  259. return unmarshaled.Config, &newRt, nil
  260. }
  261. func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
  262. fname, err := c.getCacheFilePath(netName, rt)
  263. if err != nil {
  264. return nil, err
  265. }
  266. data, err := ioutil.ReadFile(fname)
  267. if err != nil {
  268. // Ignore read errors; the cached result may not exist on-disk
  269. return nil, nil
  270. }
  271. // Load the cached result
  272. result, err := create.CreateFromBytes(data)
  273. if err != nil {
  274. return nil, err
  275. }
  276. // Convert to the config version to ensure plugins get prevResult
  277. // in the same version as the config. The cached result version
  278. // should match the config version unless the config was changed
  279. // while the container was running.
  280. result, err = result.GetAsVersion(cniVersion)
  281. if err != nil {
  282. return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
  283. }
  284. return result, nil
  285. }
  286. func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
  287. fname, err := c.getCacheFilePath(netName, rt)
  288. if err != nil {
  289. return nil, err
  290. }
  291. fdata, err := ioutil.ReadFile(fname)
  292. if err != nil {
  293. // Ignore read errors; the cached result may not exist on-disk
  294. return nil, nil
  295. }
  296. cachedInfo := cachedInfo{}
  297. if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
  298. return c.getLegacyCachedResult(netName, cniVersion, rt)
  299. }
  300. newBytes, err := json.Marshal(&cachedInfo.RawResult)
  301. if err != nil {
  302. return nil, fmt.Errorf("failed to marshal cached network %q config: %w", netName, err)
  303. }
  304. // Load the cached result
  305. result, err := create.CreateFromBytes(newBytes)
  306. if err != nil {
  307. return nil, err
  308. }
  309. // Convert to the config version to ensure plugins get prevResult
  310. // in the same version as the config. The cached result version
  311. // should match the config version unless the config was changed
  312. // while the container was running.
  313. result, err = result.GetAsVersion(cniVersion)
  314. if err != nil {
  315. return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
  316. }
  317. return result, nil
  318. }
  319. // GetNetworkListCachedResult returns the cached Result of the previous
  320. // AddNetworkList() operation for a network list, or an error.
  321. func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
  322. return c.getCachedResult(list.Name, list.CNIVersion, rt)
  323. }
  324. // GetNetworkCachedResult returns the cached Result of the previous
  325. // AddNetwork() operation for a network, or an error.
  326. func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
  327. return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
  328. }
  329. // GetNetworkListCachedConfig copies the input RuntimeConf to output
  330. // RuntimeConf with fields updated with info from the cached Config.
  331. func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
  332. return c.getCachedConfig(list.Name, rt)
  333. }
  334. // GetNetworkCachedConfig copies the input RuntimeConf to output
  335. // RuntimeConf with fields updated with info from the cached Config.
  336. func (c *CNIConfig) GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
  337. return c.getCachedConfig(net.Network.Name, rt)
  338. }
  339. func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
  340. c.ensureExec()
  341. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  342. if err != nil {
  343. return nil, err
  344. }
  345. if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
  346. return nil, err
  347. }
  348. if err := utils.ValidateNetworkName(name); err != nil {
  349. return nil, err
  350. }
  351. if err := utils.ValidateInterfaceName(rt.IfName); err != nil {
  352. return nil, err
  353. }
  354. newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
  355. if err != nil {
  356. return nil, err
  357. }
  358. return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
  359. }
  360. // AddNetworkList executes a sequence of plugins with the ADD command
  361. func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
  362. var err error
  363. var result types.Result
  364. for _, net := range list.Plugins {
  365. result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
  366. if err != nil {
  367. return nil, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err)
  368. }
  369. }
  370. if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
  371. return nil, fmt.Errorf("failed to set network %q cached result: %w", list.Name, err)
  372. }
  373. return result, nil
  374. }
  375. func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
  376. c.ensureExec()
  377. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  378. if err != nil {
  379. return err
  380. }
  381. newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
  382. if err != nil {
  383. return err
  384. }
  385. return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
  386. }
  387. // CheckNetworkList executes a sequence of plugins with the CHECK command
  388. func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
  389. // CHECK was added in CNI spec version 0.4.0 and higher
  390. if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
  391. return err
  392. } else if !gtet {
  393. return fmt.Errorf("configuration version %q does not support the CHECK command", list.CNIVersion)
  394. }
  395. if list.DisableCheck {
  396. return nil
  397. }
  398. cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
  399. if err != nil {
  400. return fmt.Errorf("failed to get network %q cached result: %w", list.Name, err)
  401. }
  402. for _, net := range list.Plugins {
  403. if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
  404. return err
  405. }
  406. }
  407. return nil
  408. }
  409. func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
  410. c.ensureExec()
  411. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  412. if err != nil {
  413. return err
  414. }
  415. newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
  416. if err != nil {
  417. return err
  418. }
  419. return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)
  420. }
  421. // DelNetworkList executes a sequence of plugins with the DEL command
  422. func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
  423. var cachedResult types.Result
  424. // Cached result on DEL was added in CNI spec version 0.4.0 and higher
  425. if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
  426. return err
  427. } else if gtet {
  428. cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt)
  429. if err != nil {
  430. return fmt.Errorf("failed to get network %q cached result: %w", list.Name, err)
  431. }
  432. }
  433. for i := len(list.Plugins) - 1; i >= 0; i-- {
  434. net := list.Plugins[i]
  435. if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
  436. return fmt.Errorf("plugin %s failed (delete): %w", pluginDescription(net.Network), err)
  437. }
  438. }
  439. _ = c.cacheDel(list.Name, rt)
  440. return nil
  441. }
  442. func pluginDescription(net *types.NetConf) string {
  443. if net == nil {
  444. return "<missing>"
  445. }
  446. pluginType := net.Type
  447. out := fmt.Sprintf("type=%q", pluginType)
  448. name := net.Name
  449. if name != "" {
  450. out += fmt.Sprintf(" name=%q", name)
  451. }
  452. return out
  453. }
  454. // AddNetwork executes the plugin with the ADD command
  455. func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
  456. result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
  457. if err != nil {
  458. return nil, err
  459. }
  460. if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
  461. return nil, fmt.Errorf("failed to set network %q cached result: %w", net.Network.Name, err)
  462. }
  463. return result, nil
  464. }
  465. // CheckNetwork executes the plugin with the CHECK command
  466. func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
  467. // CHECK was added in CNI spec version 0.4.0 and higher
  468. if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
  469. return err
  470. } else if !gtet {
  471. return fmt.Errorf("configuration version %q does not support the CHECK command", net.Network.CNIVersion)
  472. }
  473. cachedResult, err := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
  474. if err != nil {
  475. return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
  476. }
  477. return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
  478. }
  479. // DelNetwork executes the plugin with the DEL command
  480. func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
  481. var cachedResult types.Result
  482. // Cached result on DEL was added in CNI spec version 0.4.0 and higher
  483. if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
  484. return err
  485. } else if gtet {
  486. cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
  487. if err != nil {
  488. return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
  489. }
  490. }
  491. if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
  492. return err
  493. }
  494. _ = c.cacheDel(net.Network.Name, rt)
  495. return nil
  496. }
  497. // ValidateNetworkList checks that a configuration is reasonably valid.
  498. // - all the specified plugins exist on disk
  499. // - every plugin supports the desired version.
  500. //
  501. // Returns a list of all capabilities supported by the configuration, or error
  502. func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
  503. version := list.CNIVersion
  504. // holding map for seen caps (in case of duplicates)
  505. caps := map[string]interface{}{}
  506. errs := []error{}
  507. for _, net := range list.Plugins {
  508. if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil {
  509. errs = append(errs, err)
  510. }
  511. for c, enabled := range net.Network.Capabilities {
  512. if !enabled {
  513. continue
  514. }
  515. caps[c] = struct{}{}
  516. }
  517. }
  518. if len(errs) > 0 {
  519. return nil, fmt.Errorf("%v", errs)
  520. }
  521. // make caps list
  522. cc := make([]string, 0, len(caps))
  523. for c := range caps {
  524. cc = append(cc, c)
  525. }
  526. return cc, nil
  527. }
  528. // ValidateNetwork checks that a configuration is reasonably valid.
  529. // It uses the same logic as ValidateNetworkList)
  530. // Returns a list of capabilities
  531. func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) {
  532. caps := []string{}
  533. for c, ok := range net.Network.Capabilities {
  534. if ok {
  535. caps = append(caps, c)
  536. }
  537. }
  538. if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil {
  539. return nil, err
  540. }
  541. return caps, nil
  542. }
  543. // validatePlugin checks that an individual plugin's configuration is sane
  544. func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
  545. c.ensureExec()
  546. pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
  547. if err != nil {
  548. return err
  549. }
  550. if expectedVersion == "" {
  551. expectedVersion = "0.1.0"
  552. }
  553. vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
  554. if err != nil {
  555. return err
  556. }
  557. for _, vers := range vi.SupportedVersions() {
  558. if vers == expectedVersion {
  559. return nil
  560. }
  561. }
  562. return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion)
  563. }
  564. // GetVersionInfo reports which versions of the CNI spec are supported by
  565. // the given plugin.
  566. func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) {
  567. c.ensureExec()
  568. pluginPath, err := c.exec.FindInPath(pluginType, c.Path)
  569. if err != nil {
  570. return nil, err
  571. }
  572. return invoke.GetVersionInfo(ctx, pluginPath, c.exec)
  573. }
  574. // =====
  575. func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
  576. return &invoke.Args{
  577. Command: action,
  578. ContainerID: rt.ContainerID,
  579. NetNS: rt.NetNS,
  580. PluginArgs: rt.Args,
  581. IfName: rt.IfName,
  582. Path: strings.Join(c.Path, string(os.PathListSeparator)),
  583. }
  584. }