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