api.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  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 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. "errors"
  24. "fmt"
  25. "os"
  26. "path/filepath"
  27. "sort"
  28. "strings"
  29. "github.com/containernetworking/cni/pkg/invoke"
  30. "github.com/containernetworking/cni/pkg/types"
  31. "github.com/containernetworking/cni/pkg/types/create"
  32. "github.com/containernetworking/cni/pkg/utils"
  33. "github.com/containernetworking/cni/pkg/version"
  34. )
  35. var (
  36. CacheDir = "/var/lib/cni"
  37. // slightly awkward wording to preserve anyone matching on error strings
  38. ErrorCheckNotSupp = fmt.Errorf("does not support the CHECK command")
  39. )
  40. const (
  41. CNICacheV1 = "cniCacheV1"
  42. )
  43. // A RuntimeConf holds the arguments to one invocation of a CNI plugin
  44. // excepting the network configuration, with the nested exception that
  45. // the `runtimeConfig` from the network configuration is included
  46. // here.
  47. type RuntimeConf struct {
  48. ContainerID string
  49. NetNS string
  50. IfName string
  51. Args [][2]string
  52. // A dictionary of capability-specific data passed by the runtime
  53. // to plugins as top-level keys in the 'runtimeConfig' dictionary
  54. // of the plugin's stdin data. libcni will ensure that only keys
  55. // in this map which match the capabilities of the plugin are passed
  56. // to the plugin
  57. CapabilityArgs map[string]interface{}
  58. // DEPRECATED. Will be removed in a future release.
  59. CacheDir string
  60. }
  61. // Use PluginConfig instead of NetworkConfig, the NetworkConfig
  62. // backwards-compat alias will be removed in a future release.
  63. type NetworkConfig = PluginConfig
  64. type PluginConfig struct {
  65. Network *types.PluginConf
  66. Bytes []byte
  67. }
  68. type NetworkConfigList struct {
  69. Name string
  70. CNIVersion string
  71. DisableCheck bool
  72. DisableGC bool
  73. LoadOnlyInlinedPlugins bool
  74. Plugins []*PluginConfig
  75. Bytes []byte
  76. }
  77. type NetworkAttachment struct {
  78. ContainerID string
  79. Network string
  80. IfName string
  81. Config []byte
  82. NetNS string
  83. CniArgs [][2]string
  84. CapabilityArgs map[string]interface{}
  85. }
  86. type GCArgs struct {
  87. ValidAttachments []types.GCAttachment
  88. }
  89. type CNI interface {
  90. AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
  91. CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
  92. DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
  93. GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
  94. GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
  95. AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error)
  96. CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
  97. DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
  98. GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error)
  99. GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
  100. ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
  101. ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error)
  102. GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
  103. GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error
  104. GetCachedAttachments(containerID string) ([]*NetworkAttachment, error)
  105. GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error)
  106. }
  107. type CNIConfig struct {
  108. Path []string
  109. exec invoke.Exec
  110. cacheDir string
  111. }
  112. // CNIConfig implements the CNI interface
  113. var _ CNI = &CNIConfig{}
  114. // NewCNIConfig returns a new CNIConfig object that will search for plugins
  115. // in the given paths and use the given exec interface to run those plugins,
  116. // or if the exec interface is not given, will use a default exec handler.
  117. func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
  118. return NewCNIConfigWithCacheDir(path, "", exec)
  119. }
  120. // NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
  121. // in the given paths use the given exec interface to run those plugins,
  122. // or if the exec interface is not given, will use a default exec handler.
  123. // The given cache directory will be used for temporary data storage when needed.
  124. func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
  125. return &CNIConfig{
  126. Path: path,
  127. cacheDir: cacheDir,
  128. exec: exec,
  129. }
  130. }
  131. func buildOneConfig(name, cniVersion string, orig *PluginConfig, prevResult types.Result, rt *RuntimeConf) (*PluginConfig, error) {
  132. var err error
  133. inject := map[string]interface{}{
  134. "name": name,
  135. "cniVersion": cniVersion,
  136. }
  137. // Add previous plugin result
  138. if prevResult != nil {
  139. inject["prevResult"] = prevResult
  140. }
  141. // Ensure every config uses the same name and version
  142. orig, err = InjectConf(orig, inject)
  143. if err != nil {
  144. return nil, err
  145. }
  146. if rt != nil {
  147. return injectRuntimeConfig(orig, rt)
  148. }
  149. return orig, nil
  150. }
  151. // This function takes a libcni RuntimeConf structure and injects values into
  152. // a "runtimeConfig" dictionary in the CNI network configuration JSON that
  153. // will be passed to the plugin on stdin.
  154. //
  155. // Only "capabilities arguments" passed by the runtime are currently injected.
  156. // These capabilities arguments are filtered through the plugin's advertised
  157. // capabilities from its config JSON, and any keys in the CapabilityArgs
  158. // matching plugin capabilities are added to the "runtimeConfig" dictionary
  159. // sent to the plugin via JSON on stdin. For example, if the plugin's
  160. // capabilities include "portMappings", and the CapabilityArgs map includes a
  161. // "portMappings" key, that key and its value are added to the "runtimeConfig"
  162. // dictionary to be passed to the plugin's stdin.
  163. func injectRuntimeConfig(orig *PluginConfig, rt *RuntimeConf) (*PluginConfig, error) {
  164. var err error
  165. rc := make(map[string]interface{})
  166. for capability, supported := range orig.Network.Capabilities {
  167. if !supported {
  168. continue
  169. }
  170. if data, ok := rt.CapabilityArgs[capability]; ok {
  171. rc[capability] = data
  172. }
  173. }
  174. if len(rc) > 0 {
  175. orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
  176. if err != nil {
  177. return nil, err
  178. }
  179. }
  180. return orig, nil
  181. }
  182. // ensure we have a usable exec if the CNIConfig was not given one
  183. func (c *CNIConfig) ensureExec() invoke.Exec {
  184. if c.exec == nil {
  185. c.exec = &invoke.DefaultExec{
  186. RawExec: &invoke.RawExec{Stderr: os.Stderr},
  187. PluginDecoder: version.PluginDecoder{},
  188. }
  189. }
  190. return c.exec
  191. }
  192. type cachedInfo struct {
  193. Kind string `json:"kind"`
  194. ContainerID string `json:"containerId"`
  195. Config []byte `json:"config"`
  196. IfName string `json:"ifName"`
  197. NetworkName string `json:"networkName"`
  198. NetNS string `json:"netns,omitempty"`
  199. CniArgs [][2]string `json:"cniArgs,omitempty"`
  200. CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
  201. RawResult map[string]interface{} `json:"result,omitempty"`
  202. Result types.Result `json:"-"`
  203. }
  204. // getCacheDir returns the cache directory in this order:
  205. // 1) global cacheDir from CNIConfig object
  206. // 2) deprecated cacheDir from RuntimeConf object
  207. // 3) fall back to default cache directory
  208. func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
  209. if c.cacheDir != "" {
  210. return c.cacheDir
  211. }
  212. if rt.CacheDir != "" {
  213. return rt.CacheDir
  214. }
  215. return CacheDir
  216. }
  217. func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
  218. if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
  219. return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
  220. }
  221. return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
  222. }
  223. func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
  224. cached := cachedInfo{
  225. Kind: CNICacheV1,
  226. ContainerID: rt.ContainerID,
  227. Config: config,
  228. IfName: rt.IfName,
  229. NetworkName: netName,
  230. NetNS: rt.NetNS,
  231. CniArgs: rt.Args,
  232. CapabilityArgs: rt.CapabilityArgs,
  233. }
  234. // We need to get type.Result into cachedInfo as JSON map
  235. // Marshal to []byte, then Unmarshal into cached.RawResult
  236. data, err := json.Marshal(result)
  237. if err != nil {
  238. return err
  239. }
  240. err = json.Unmarshal(data, &cached.RawResult)
  241. if err != nil {
  242. return err
  243. }
  244. newBytes, err := json.Marshal(&cached)
  245. if err != nil {
  246. return err
  247. }
  248. fname, err := c.getCacheFilePath(netName, rt)
  249. if err != nil {
  250. return err
  251. }
  252. if err := os.MkdirAll(filepath.Dir(fname), 0o700); err != nil {
  253. return err
  254. }
  255. return os.WriteFile(fname, newBytes, 0o600)
  256. }
  257. func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
  258. fname, err := c.getCacheFilePath(netName, rt)
  259. if err != nil {
  260. // Ignore error
  261. return nil
  262. }
  263. return os.Remove(fname)
  264. }
  265. func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
  266. var bytes []byte
  267. fname, err := c.getCacheFilePath(netName, rt)
  268. if err != nil {
  269. return nil, nil, err
  270. }
  271. bytes, err = os.ReadFile(fname)
  272. if err != nil {
  273. // Ignore read errors; the cached result may not exist on-disk
  274. return nil, nil, nil
  275. }
  276. unmarshaled := cachedInfo{}
  277. if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
  278. return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %w", netName, err)
  279. }
  280. if unmarshaled.Kind != CNICacheV1 {
  281. return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
  282. }
  283. newRt := *rt
  284. if unmarshaled.CniArgs != nil {
  285. newRt.Args = unmarshaled.CniArgs
  286. }
  287. newRt.CapabilityArgs = unmarshaled.CapabilityArgs
  288. return unmarshaled.Config, &newRt, nil
  289. }
  290. func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
  291. fname, err := c.getCacheFilePath(netName, rt)
  292. if err != nil {
  293. return nil, err
  294. }
  295. data, err := os.ReadFile(fname)
  296. if err != nil {
  297. // Ignore read errors; the cached result may not exist on-disk
  298. return nil, nil
  299. }
  300. // Load the cached result
  301. result, err := create.CreateFromBytes(data)
  302. if err != nil {
  303. return nil, err
  304. }
  305. // Convert to the config version to ensure plugins get prevResult
  306. // in the same version as the config. The cached result version
  307. // should match the config version unless the config was changed
  308. // while the container was running.
  309. result, err = result.GetAsVersion(cniVersion)
  310. if err != nil {
  311. return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
  312. }
  313. return result, nil
  314. }
  315. func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
  316. fname, err := c.getCacheFilePath(netName, rt)
  317. if err != nil {
  318. return nil, err
  319. }
  320. fdata, err := os.ReadFile(fname)
  321. if err != nil {
  322. // Ignore read errors; the cached result may not exist on-disk
  323. return nil, nil
  324. }
  325. cachedInfo := cachedInfo{}
  326. if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
  327. return c.getLegacyCachedResult(netName, cniVersion, rt)
  328. }
  329. newBytes, err := json.Marshal(&cachedInfo.RawResult)
  330. if err != nil {
  331. return nil, fmt.Errorf("failed to marshal cached network %q config: %w", netName, err)
  332. }
  333. // Load the cached result
  334. result, err := create.CreateFromBytes(newBytes)
  335. if err != nil {
  336. return nil, err
  337. }
  338. // Convert to the config version to ensure plugins get prevResult
  339. // in the same version as the config. The cached result version
  340. // should match the config version unless the config was changed
  341. // while the container was running.
  342. result, err = result.GetAsVersion(cniVersion)
  343. if err != nil {
  344. return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err)
  345. }
  346. return result, nil
  347. }
  348. // GetNetworkListCachedResult returns the cached Result of the previous
  349. // AddNetworkList() operation for a network list, or an error.
  350. func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
  351. return c.getCachedResult(list.Name, list.CNIVersion, rt)
  352. }
  353. // GetNetworkCachedResult returns the cached Result of the previous
  354. // AddNetwork() operation for a network, or an error.
  355. func (c *CNIConfig) GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
  356. return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
  357. }
  358. // GetNetworkListCachedConfig copies the input RuntimeConf to output
  359. // RuntimeConf with fields updated with info from the cached Config.
  360. func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
  361. return c.getCachedConfig(list.Name, rt)
  362. }
  363. // GetNetworkCachedConfig copies the input RuntimeConf to output
  364. // RuntimeConf with fields updated with info from the cached Config.
  365. func (c *CNIConfig) GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
  366. return c.getCachedConfig(net.Network.Name, rt)
  367. }
  368. // GetCachedAttachments returns a list of network attachments from the cache.
  369. // The returned list will be filtered by the containerID if the value is not empty.
  370. func (c *CNIConfig) GetCachedAttachments(containerID string) ([]*NetworkAttachment, error) {
  371. dirPath := filepath.Join(c.getCacheDir(&RuntimeConf{}), "results")
  372. entries, err := os.ReadDir(dirPath)
  373. if err != nil {
  374. if os.IsNotExist(err) {
  375. return nil, nil
  376. }
  377. return nil, err
  378. }
  379. fileNames := make([]string, 0, len(entries))
  380. for _, e := range entries {
  381. fileNames = append(fileNames, e.Name())
  382. }
  383. sort.Strings(fileNames)
  384. attachments := []*NetworkAttachment{}
  385. for _, fname := range fileNames {
  386. if len(containerID) > 0 {
  387. part := fmt.Sprintf("-%s-", containerID)
  388. pos := strings.Index(fname, part)
  389. if pos <= 0 || pos+len(part) >= len(fname) {
  390. continue
  391. }
  392. }
  393. cacheFile := filepath.Join(dirPath, fname)
  394. bytes, err := os.ReadFile(cacheFile)
  395. if err != nil {
  396. continue
  397. }
  398. cachedInfo := cachedInfo{}
  399. if err := json.Unmarshal(bytes, &cachedInfo); err != nil {
  400. continue
  401. }
  402. if cachedInfo.Kind != CNICacheV1 {
  403. continue
  404. }
  405. if len(containerID) > 0 && cachedInfo.ContainerID != containerID {
  406. continue
  407. }
  408. if cachedInfo.IfName == "" || cachedInfo.NetworkName == "" {
  409. continue
  410. }
  411. attachments = append(attachments, &NetworkAttachment{
  412. ContainerID: cachedInfo.ContainerID,
  413. Network: cachedInfo.NetworkName,
  414. IfName: cachedInfo.IfName,
  415. Config: cachedInfo.Config,
  416. NetNS: cachedInfo.NetNS,
  417. CniArgs: cachedInfo.CniArgs,
  418. CapabilityArgs: cachedInfo.CapabilityArgs,
  419. })
  420. }
  421. return attachments, nil
  422. }
  423. func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
  424. c.ensureExec()
  425. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  426. if err != nil {
  427. return nil, err
  428. }
  429. if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
  430. return nil, err
  431. }
  432. if err := utils.ValidateNetworkName(name); err != nil {
  433. return nil, err
  434. }
  435. if err := utils.ValidateInterfaceName(rt.IfName); err != nil {
  436. return nil, err
  437. }
  438. newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
  439. if err != nil {
  440. return nil, err
  441. }
  442. return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
  443. }
  444. // AddNetworkList executes a sequence of plugins with the ADD command
  445. func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
  446. var err error
  447. var result types.Result
  448. for _, net := range list.Plugins {
  449. result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
  450. if err != nil {
  451. return nil, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err)
  452. }
  453. }
  454. if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
  455. return nil, fmt.Errorf("failed to set network %q cached result: %w", list.Name, err)
  456. }
  457. return result, nil
  458. }
  459. func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
  460. c.ensureExec()
  461. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  462. if err != nil {
  463. return err
  464. }
  465. newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
  466. if err != nil {
  467. return err
  468. }
  469. return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
  470. }
  471. // CheckNetworkList executes a sequence of plugins with the CHECK command
  472. func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
  473. // CHECK was added in CNI spec version 0.4.0 and higher
  474. if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
  475. return err
  476. } else if !gtet {
  477. return fmt.Errorf("configuration version %q %w", list.CNIVersion, ErrorCheckNotSupp)
  478. }
  479. if list.DisableCheck {
  480. return nil
  481. }
  482. cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
  483. if err != nil {
  484. return fmt.Errorf("failed to get network %q cached result: %w", list.Name, err)
  485. }
  486. for _, net := range list.Plugins {
  487. if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
  488. return err
  489. }
  490. }
  491. return nil
  492. }
  493. func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
  494. c.ensureExec()
  495. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  496. if err != nil {
  497. return err
  498. }
  499. newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
  500. if err != nil {
  501. return err
  502. }
  503. return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)
  504. }
  505. // DelNetworkList executes a sequence of plugins with the DEL command
  506. func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
  507. var cachedResult types.Result
  508. // Cached result on DEL was added in CNI spec version 0.4.0 and higher
  509. if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
  510. return err
  511. } else if gtet {
  512. if cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt); err != nil {
  513. _ = c.cacheDel(list.Name, rt)
  514. cachedResult = nil
  515. }
  516. }
  517. for i := len(list.Plugins) - 1; i >= 0; i-- {
  518. net := list.Plugins[i]
  519. if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
  520. return fmt.Errorf("plugin %s failed (delete): %w", pluginDescription(net.Network), err)
  521. }
  522. }
  523. _ = c.cacheDel(list.Name, rt)
  524. return nil
  525. }
  526. func pluginDescription(net *types.PluginConf) string {
  527. if net == nil {
  528. return "<missing>"
  529. }
  530. pluginType := net.Type
  531. out := fmt.Sprintf("type=%q", pluginType)
  532. name := net.Name
  533. if name != "" {
  534. out += fmt.Sprintf(" name=%q", name)
  535. }
  536. return out
  537. }
  538. // AddNetwork executes the plugin with the ADD command
  539. func (c *CNIConfig) AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
  540. result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
  541. if err != nil {
  542. return nil, err
  543. }
  544. if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
  545. return nil, fmt.Errorf("failed to set network %q cached result: %w", net.Network.Name, err)
  546. }
  547. return result, nil
  548. }
  549. // CheckNetwork executes the plugin with the CHECK command
  550. func (c *CNIConfig) CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
  551. // CHECK was added in CNI spec version 0.4.0 and higher
  552. if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
  553. return err
  554. } else if !gtet {
  555. return fmt.Errorf("configuration version %q %w", net.Network.CNIVersion, ErrorCheckNotSupp)
  556. }
  557. cachedResult, err := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
  558. if err != nil {
  559. return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
  560. }
  561. return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
  562. }
  563. // DelNetwork executes the plugin with the DEL command
  564. func (c *CNIConfig) DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
  565. var cachedResult types.Result
  566. // Cached result on DEL was added in CNI spec version 0.4.0 and higher
  567. if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
  568. return err
  569. } else if gtet {
  570. cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
  571. if err != nil {
  572. return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err)
  573. }
  574. }
  575. if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
  576. return err
  577. }
  578. _ = c.cacheDel(net.Network.Name, rt)
  579. return nil
  580. }
  581. // ValidateNetworkList checks that a configuration is reasonably valid.
  582. // - all the specified plugins exist on disk
  583. // - every plugin supports the desired version.
  584. //
  585. // Returns a list of all capabilities supported by the configuration, or error
  586. func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
  587. version := list.CNIVersion
  588. // holding map for seen caps (in case of duplicates)
  589. caps := map[string]interface{}{}
  590. errs := []error{}
  591. for _, net := range list.Plugins {
  592. if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil {
  593. errs = append(errs, err)
  594. }
  595. for c, enabled := range net.Network.Capabilities {
  596. if !enabled {
  597. continue
  598. }
  599. caps[c] = struct{}{}
  600. }
  601. }
  602. if len(errs) > 0 {
  603. return nil, fmt.Errorf("%v", errs)
  604. }
  605. // make caps list
  606. cc := make([]string, 0, len(caps))
  607. for c := range caps {
  608. cc = append(cc, c)
  609. }
  610. return cc, nil
  611. }
  612. // ValidateNetwork checks that a configuration is reasonably valid.
  613. // It uses the same logic as ValidateNetworkList)
  614. // Returns a list of capabilities
  615. func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) {
  616. caps := []string{}
  617. for c, ok := range net.Network.Capabilities {
  618. if ok {
  619. caps = append(caps, c)
  620. }
  621. }
  622. if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil {
  623. return nil, err
  624. }
  625. return caps, nil
  626. }
  627. // validatePlugin checks that an individual plugin's configuration is sane
  628. func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
  629. c.ensureExec()
  630. pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
  631. if err != nil {
  632. return err
  633. }
  634. if expectedVersion == "" {
  635. expectedVersion = "0.1.0"
  636. }
  637. vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
  638. if err != nil {
  639. return err
  640. }
  641. for _, vers := range vi.SupportedVersions() {
  642. if vers == expectedVersion {
  643. return nil
  644. }
  645. }
  646. return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion)
  647. }
  648. // GetVersionInfo reports which versions of the CNI spec are supported by
  649. // the given plugin.
  650. func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) {
  651. c.ensureExec()
  652. pluginPath, err := c.exec.FindInPath(pluginType, c.Path)
  653. if err != nil {
  654. return nil, err
  655. }
  656. return invoke.GetVersionInfo(ctx, pluginPath, c.exec)
  657. }
  658. // GCNetworkList will do two things
  659. // - dump the list of cached attachments, and issue deletes as necessary
  660. // - issue a GC to the underlying plugins (if the version is high enough)
  661. func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList, args *GCArgs) error {
  662. // If DisableGC is set, then don't bother GCing at all.
  663. if list.DisableGC {
  664. return nil
  665. }
  666. // First, get the list of cached attachments
  667. cachedAttachments, err := c.GetCachedAttachments("")
  668. if err != nil {
  669. return nil
  670. }
  671. var validAttachments map[types.GCAttachment]interface{}
  672. if args != nil {
  673. validAttachments = make(map[types.GCAttachment]interface{}, len(args.ValidAttachments))
  674. for _, a := range args.ValidAttachments {
  675. validAttachments[a] = nil
  676. }
  677. }
  678. var errs []error
  679. for _, cachedAttachment := range cachedAttachments {
  680. if cachedAttachment.Network != list.Name {
  681. continue
  682. }
  683. // we found this attachment
  684. gca := types.GCAttachment{
  685. ContainerID: cachedAttachment.ContainerID,
  686. IfName: cachedAttachment.IfName,
  687. }
  688. if _, ok := validAttachments[gca]; ok {
  689. continue
  690. }
  691. // otherwise, this attachment wasn't valid and we should issue a CNI DEL
  692. rt := RuntimeConf{
  693. ContainerID: cachedAttachment.ContainerID,
  694. NetNS: cachedAttachment.NetNS,
  695. IfName: cachedAttachment.IfName,
  696. Args: cachedAttachment.CniArgs,
  697. CapabilityArgs: cachedAttachment.CapabilityArgs,
  698. }
  699. if err := c.DelNetworkList(ctx, list, &rt); err != nil {
  700. errs = append(errs, fmt.Errorf("failed to delete stale attachment %s %s: %w", rt.ContainerID, rt.IfName, err))
  701. }
  702. }
  703. // now, if the version supports it, issue a GC
  704. if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); gt {
  705. inject := map[string]interface{}{
  706. "name": list.Name,
  707. "cniVersion": list.CNIVersion,
  708. }
  709. if args != nil {
  710. inject["cni.dev/valid-attachments"] = args.ValidAttachments
  711. // #1101: spec used incorrect variable name
  712. inject["cni.dev/attachments"] = args.ValidAttachments
  713. }
  714. for _, plugin := range list.Plugins {
  715. // build config here
  716. pluginConfig, err := InjectConf(plugin, inject)
  717. if err != nil {
  718. errs = append(errs, fmt.Errorf("failed to generate configuration to GC plugin %s: %w", plugin.Network.Type, err))
  719. }
  720. if err := c.gcNetwork(ctx, pluginConfig); err != nil {
  721. errs = append(errs, fmt.Errorf("failed to GC plugin %s: %w", plugin.Network.Type, err))
  722. }
  723. }
  724. }
  725. return errors.Join(errs...)
  726. }
  727. func (c *CNIConfig) gcNetwork(ctx context.Context, net *PluginConfig) error {
  728. c.ensureExec()
  729. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  730. if err != nil {
  731. return err
  732. }
  733. args := c.args("GC", &RuntimeConf{})
  734. return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
  735. }
  736. func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfigList) error {
  737. // If the version doesn't support status, abort.
  738. if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); !gt {
  739. return nil
  740. }
  741. inject := map[string]interface{}{
  742. "name": list.Name,
  743. "cniVersion": list.CNIVersion,
  744. }
  745. for _, plugin := range list.Plugins {
  746. // build config here
  747. pluginConfig, err := InjectConf(plugin, inject)
  748. if err != nil {
  749. return fmt.Errorf("failed to generate configuration to get plugin STATUS %s: %w", plugin.Network.Type, err)
  750. }
  751. if err := c.getStatusNetwork(ctx, pluginConfig); err != nil {
  752. return err // Don't collect errors here, so we return a clean error code.
  753. }
  754. }
  755. return nil
  756. }
  757. func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *PluginConfig) error {
  758. c.ensureExec()
  759. pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
  760. if err != nil {
  761. return err
  762. }
  763. args := c.args("STATUS", &RuntimeConf{})
  764. return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
  765. }
  766. // =====
  767. func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
  768. return &invoke.Args{
  769. Command: action,
  770. ContainerID: rt.ContainerID,
  771. NetNS: rt.NetNS,
  772. PluginArgs: rt.Args,
  773. IfName: rt.IfName,
  774. Path: strings.Join(c.Path, string(os.PathListSeparator)),
  775. }
  776. }