kubernetes flannel源码学习疑问(一):cni-plugin配置文件json转换为struct为何未区分plugins json数组

文章探讨了在学习Kubernetes的CNI组件flannel时遇到的配置文件与源码中结构体不匹配的问题。通过分析flannel的源码及CNI工具的加载流程,解释了配置文件中的`plugins`数组如何被转换,并展示了配置信息如何在转换过程中丢失层级关系。作者提供了配置文件和源码片段,并指出了`LoadConfList`和`AddNetworkList`方法在处理配置时的关键步骤。
摘要由CSDN通过智能技术生成

背景介绍

最近在学习kubernetes cni部分功能,学习 flannel源码发现数据转换处struct与配置文件中json无法对应,特此记录。【个人学习记录,如有错误,欢迎指出】

1. flannel配置文件如下所示

cat /etc/cni/net.d/10-flannel.conflist 
{
  "name": "cbr0",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}

2.flannel源码功能代码展示 (flannel-release-v0.14.1)

cat flannel-release-v0.14.1\flannel.go

......
type NetConf struct {
   types.NetConf

   // IPAM field "replaces" that of types.NetConf which is incomplete
   IPAM          map[string]interface{} `json:"ipam,omitempty"`
   SubnetFile    string                 `json:"subnetFile"`
   DataDir       string                 `json:"dataDir"`
   Delegate      map[string]interface{} `json:"delegate"`
   RuntimeConfig map[string]interface{} `json:"runtimeConfig,omitempty"`
}

......
func loadFlannelNetConf(bytes []byte) (*NetConf, error) {
   n := &NetConf{
      SubnetFile: defaultSubnetFile,
      DataDir:    defaultDataDir,
   }
   if err := json.Unmarshal(bytes, n); err != nil {
      return nil, fmt.Errorf("failed to load netconf: %v", err)
   }

   return n, nil
}
......

3.疑问:

此处delegate属于plugins数组内的子项,为何NetConf中定义却未体现层级关系?

4.源码分析

cni/cnitool.go at release-0.8 · containernetworking/cni · GitHubContainer Network Interface - networking for Linux containers - cni/cnitool.go at release-0.8 · containernetworking/cnihttps://github.com/containernetworking/cni/blob/release-0.8/cnitool/cnitool.gohttps://github.com/containernetworking/cni/blob/release-0.8/cnitool/cnitool.go

func main() {
   ......

        netconf, err := libcni.LoadConfList(netdir, os.Args[2])
        if err != nil {
            exit(err)
        }
    ......

        switch os.Args[1] {
        case CmdAdd:
            result, err := cninet.AddNetworkList(context.TODO(), netconf, rt)
            if result != nil {
                _ = result.Print()
            }
            exit(err)

重点关注这2个方法:

LoadConfList 重点关注调用流程如下:

https://github.com/containernetworking/cni/blob/9fc34aee0a6396d6484992035819c92c42522eea/libcni/conf.go#L185

func LoadConfList(dir, name string) (*NetworkConfigList, error)

func ConfListFromFile(filename string) (*NetworkConfigList, error)

func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {

func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {

AddNetworkList 重点关注调用流程如下:

https://github.com/containernetworking/cni/blob/9fc34aee0a6396d6484992035819c92c42522eea/libcni/api.go#L425

func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {

func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {

这里重点看一下LoadConfList 的func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) 方法。

func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
    rawList := make(map[string]interface{})

   //这里的bytes为1中完整配置文件的byte形式。
    if err := json.Unmarshal(bytes, &rawList); err != nil {
        return nil, fmt.Errorf("error parsing configuration list: %s", err)
    }

    list := &NetworkConfigList{
        Name:         name,
        DisableCheck: disableCheck,
        CNIVersion:   cniVersion,
        Bytes:        bytes,
    }

    var plugins []interface{}
    plug, ok := rawList["plugins"]
    if !ok {
        return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key")
    }
    plugins, ok = plug.([]interface{})

......

    for i, conf := range plugins {

       //这里的newBytes为步骤1中完整配置文件单个plugin的byte形式。
        newBytes, err := json.Marshal(conf)
        if err != nil {
            return nil, fmt.Errorf("failed to marshal plugin config %d: %v", i, err)
        }
        netConf, err := ConfFromBytes(newBytes)
        if err != nil {
            return nil, fmt.Errorf("failed to parse plugin config %d: %v", i, err)
        }
        list.Plugins = append(list.Plugins, netConf)
    }

    return list, nil
}

//ConfFromBytes 中也是类似的转换,最后NetworkConfig的Bytes字段值为如下json串对应byte格式。该Bytes将传递给cni插件cmdAdd使用。

5.此方法修改后,plugins数据内Bytes的内容为类似如下json对应byte格式数据:

{
  "name": "cbr0",
  "cniVersion": "0.3.1",
  "type": "flannel",
  "delegate": {
       "hairpinMode": true,
       "isDefaultGateway": true
   }
}

希望能够帮助您解决问题,欢迎您提出宝贵的建议。

end~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值