123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- /*
- Copyright The containerd Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package cni
- import (
- "context"
- "fmt"
- "os"
- "strings"
- "sync"
- cnilibrary "github.com/containernetworking/cni/libcni"
- "github.com/containernetworking/cni/pkg/invoke"
- "github.com/containernetworking/cni/pkg/types"
- types100 "github.com/containernetworking/cni/pkg/types/100"
- "github.com/containernetworking/cni/pkg/version"
- )
- type CNI interface {
- // Setup setup the network for the namespace
- Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error)
- // SetupSerially sets up each of the network interfaces for the namespace in serial
- SetupSerially(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error)
- // Remove tears down the network of the namespace.
- Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
- // Check checks if the network is still in desired state
- Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
- // Load loads the cni network config
- Load(opts ...Opt) error
- // Status checks the status of the cni initialization
- Status() error
- // GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
- GetConfig() *ConfigResult
- }
- type ConfigResult struct {
- PluginDirs []string
- PluginConfDir string
- PluginMaxConfNum int
- Prefix string
- Networks []*ConfNetwork
- }
- type ConfNetwork struct {
- Config *NetworkConfList
- IFName string
- }
- // NetworkConfList is a source bytes to string version of cnilibrary.NetworkConfigList
- type NetworkConfList struct {
- Name string
- CNIVersion string
- Plugins []*NetworkConf
- Source string
- }
- // NetworkConf is a source bytes to string conversion of cnilibrary.NetworkConfig
- type NetworkConf struct {
- Network *types.NetConf
- Source string
- }
- type libcni struct {
- config
- cniConfig cnilibrary.CNI
- networkCount int // minimum network plugin configurations needed to initialize cni
- networks []*Network
- sync.RWMutex
- }
- func defaultCNIConfig() *libcni {
- return &libcni{
- config: config{
- pluginDirs: []string{DefaultCNIDir},
- pluginConfDir: DefaultNetDir,
- pluginMaxConfNum: DefaultMaxConfNum,
- prefix: DefaultPrefix,
- },
- cniConfig: cnilibrary.NewCNIConfig(
- []string{
- DefaultCNIDir,
- },
- &invoke.DefaultExec{
- RawExec: &invoke.RawExec{Stderr: os.Stderr},
- PluginDecoder: version.PluginDecoder{},
- },
- ),
- networkCount: 1,
- }
- }
- // New creates a new libcni instance.
- func New(config ...Opt) (CNI, error) {
- cni := defaultCNIConfig()
- var err error
- for _, c := range config {
- if err = c(cni); err != nil {
- return nil, err
- }
- }
- return cni, nil
- }
- // Load loads the latest config from cni config files.
- func (c *libcni) Load(opts ...Opt) error {
- var err error
- c.Lock()
- defer c.Unlock()
- // Reset the networks on a load operation to ensure
- // config happens on a clean slate
- c.reset()
- for _, o := range opts {
- if err = o(c); err != nil {
- return fmt.Errorf("cni config load failed: %v: %w", err, ErrLoad)
- }
- }
- return nil
- }
- // Status returns the status of CNI initialization.
- func (c *libcni) Status() error {
- c.RLock()
- defer c.RUnlock()
- if len(c.networks) < c.networkCount {
- return ErrCNINotInitialized
- }
- return nil
- }
- // Networks returns all the configured networks.
- // NOTE: Caller MUST NOT modify anything in the returned array.
- func (c *libcni) Networks() []*Network {
- c.RLock()
- defer c.RUnlock()
- return append([]*Network{}, c.networks...)
- }
- // Setup setups the network in the namespace and returns a Result
- func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
- if err := c.Status(); err != nil {
- return nil, err
- }
- ns, err := newNamespace(id, path, opts...)
- if err != nil {
- return nil, err
- }
- result, err := c.attachNetworks(ctx, ns)
- if err != nil {
- return nil, err
- }
- return c.createResult(result)
- }
- // SetupSerially setups the network in the namespace and returns a Result
- func (c *libcni) SetupSerially(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
- if err := c.Status(); err != nil {
- return nil, err
- }
- ns, err := newNamespace(id, path, opts...)
- if err != nil {
- return nil, err
- }
- result, err := c.attachNetworksSerially(ctx, ns)
- if err != nil {
- return nil, err
- }
- return c.createResult(result)
- }
- func (c *libcni) attachNetworksSerially(ctx context.Context, ns *Namespace) ([]*types100.Result, error) {
- var results []*types100.Result
- for _, network := range c.Networks() {
- r, err := network.Attach(ctx, ns)
- if err != nil {
- return nil, err
- }
- results = append(results, r)
- }
- return results, nil
- }
- type asynchAttachResult struct {
- index int
- res *types100.Result
- err error
- }
- func asynchAttach(ctx context.Context, index int, n *Network, ns *Namespace, wg *sync.WaitGroup, rc chan asynchAttachResult) {
- defer wg.Done()
- r, err := n.Attach(ctx, ns)
- rc <- asynchAttachResult{index: index, res: r, err: err}
- }
- func (c *libcni) attachNetworks(ctx context.Context, ns *Namespace) ([]*types100.Result, error) {
- var wg sync.WaitGroup
- var firstError error
- results := make([]*types100.Result, len(c.Networks()))
- rc := make(chan asynchAttachResult)
- for i, network := range c.Networks() {
- wg.Add(1)
- go asynchAttach(ctx, i, network, ns, &wg, rc)
- }
- for range c.Networks() {
- rs := <-rc
- if rs.err != nil && firstError == nil {
- firstError = rs.err
- }
- results[rs.index] = rs.res
- }
- wg.Wait()
- return results, firstError
- }
- // Remove removes the network config from the namespace
- func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
- if err := c.Status(); err != nil {
- return err
- }
- ns, err := newNamespace(id, path, opts...)
- if err != nil {
- return err
- }
- for _, network := range c.Networks() {
- if err := network.Remove(ctx, ns); err != nil {
- // Based on CNI spec v0.7.0, empty network namespace is allowed to
- // do best effort cleanup. However, it is not handled consistently
- // right now:
- // https://github.com/containernetworking/plugins/issues/210
- // TODO(random-liu): Remove the error handling when the issue is
- // fixed and the CNI spec v0.6.0 support is deprecated.
- // NOTE(claudiub): Some CNIs could return a "not found" error, which could mean that
- // it was already deleted.
- if (path == "" && strings.Contains(err.Error(), "no such file or directory")) || strings.Contains(err.Error(), "not found") {
- continue
- }
- return err
- }
- }
- return nil
- }
- // Check checks if the network is still in desired state
- func (c *libcni) Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
- if err := c.Status(); err != nil {
- return err
- }
- ns, err := newNamespace(id, path, opts...)
- if err != nil {
- return err
- }
- for _, network := range c.Networks() {
- err := network.Check(ctx, ns)
- if err != nil {
- return err
- }
- }
- return nil
- }
- // GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
- func (c *libcni) GetConfig() *ConfigResult {
- c.RLock()
- defer c.RUnlock()
- r := &ConfigResult{
- PluginDirs: c.config.pluginDirs,
- PluginConfDir: c.config.pluginConfDir,
- PluginMaxConfNum: c.config.pluginMaxConfNum,
- Prefix: c.config.prefix,
- }
- for _, network := range c.networks {
- conf := &NetworkConfList{
- Name: network.config.Name,
- CNIVersion: network.config.CNIVersion,
- Source: string(network.config.Bytes),
- }
- for _, plugin := range network.config.Plugins {
- conf.Plugins = append(conf.Plugins, &NetworkConf{
- Network: plugin.Network,
- Source: string(plugin.Bytes),
- })
- }
- r.Networks = append(r.Networks, &ConfNetwork{
- Config: conf,
- IFName: network.ifName,
- })
- }
- return r
- }
- func (c *libcni) reset() {
- c.networks = nil
- }
|