...

Source file src/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils/cniconfig.go

Documentation: github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils

     1  // Copyright (c) 2019 Kubernetes Network Plumbing Working Group
     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 utils
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"github.com/containernetworking/cni/libcni"
    21  	"strings"
    22  
    23  	v1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
    24  )
    25  
    26  // GetCNIConfig (from annotation string to CNI JSON bytes)
    27  func GetCNIConfig(net *v1.NetworkAttachmentDefinition, confDir string) (config []byte, err error) {
    28  	emptySpec := v1.NetworkAttachmentDefinitionSpec{}
    29  	if net.Spec == emptySpec {
    30  		// Network Spec empty; generate delegate from CNI JSON config
    31  		// from the configuration directory that has the same network
    32  		// name as the custom resource
    33  		config, err = GetCNIConfigFromFile(net.Name, confDir)
    34  		if err != nil {
    35  			return nil, fmt.Errorf("GetCNIConfig: err in GetCNIConfigFromFile: %v", err)
    36  		}
    37  	} else {
    38  		// Config contains a standard JSON-encoded CNI configuration
    39  		// or configuration list which defines the plugin chain to
    40  		// execute.
    41  		config, err = GetCNIConfigFromSpec(net.Spec.Config, net.Name)
    42  		if err != nil {
    43  			return nil, fmt.Errorf("GetCNIConfig: err in getCNIConfigFromSpec: %v", err)
    44  		}
    45  	}
    46  	return config, nil
    47  }
    48  
    49  // GetCNIConfigFromSpec reads a CNI JSON configuration from given directory (confDir)
    50  func GetCNIConfigFromFile(name, confDir string) ([]byte, error) {
    51  	// In the absence of valid keys in a Spec, the runtime (or
    52  	// meta-plugin) should load and execute a CNI .configlist
    53  	// or .config (in that order) file on-disk whose JSON
    54  	// "name" key matches this Network object’s name.
    55  
    56  	// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#getDefaultCNINetwork
    57  	files, err := libcni.ConfFiles(confDir, []string{".conf", ".json", ".conflist"})
    58  	switch {
    59  	case err != nil:
    60  		return nil, fmt.Errorf("No networks found in %s", confDir)
    61  	case len(files) == 0:
    62  		return nil, fmt.Errorf("No networks found in %s", confDir)
    63  	}
    64  
    65  	for _, confFile := range files {
    66  		var confList *libcni.NetworkConfigList
    67  		if strings.HasSuffix(confFile, ".conflist") {
    68  			confList, err = libcni.ConfListFromFile(confFile)
    69  			if err != nil {
    70  				return nil, fmt.Errorf("Error loading CNI conflist file %s: %v", confFile, err)
    71  			}
    72  
    73  			if confList.Name == name || name == "" {
    74  				return confList.Bytes, nil
    75  			}
    76  
    77  		} else {
    78  			conf, err := libcni.ConfFromFile(confFile)
    79  			if err != nil {
    80  				return nil, fmt.Errorf("Error loading CNI config file %s: %v", confFile, err)
    81  			}
    82  
    83  			if conf.Network.Name == name || name == "" {
    84  				// Ensure the config has a "type" so we know what plugin to run.
    85  				// Also catches the case where somebody put a conflist into a conf file.
    86  				if conf.Network.Type == "" {
    87  					return nil, fmt.Errorf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile)
    88  				}
    89  				return conf.Bytes, nil
    90  			}
    91  		}
    92  	}
    93  
    94  	return nil, fmt.Errorf("no network available in the name %s in cni dir %s", name, confDir)
    95  }
    96  
    97  // GetCNIConfigFromSpec reads a CNI JSON configuration from the NetworkAttachmentDefinition
    98  // object's Spec.Config field and fills in any missing details like the network name
    99  func GetCNIConfigFromSpec(configData, netName string) ([]byte, error) {
   100  	var rawConfig map[string]interface{}
   101  	var err error
   102  
   103  	configBytes := []byte(configData)
   104  	err = json.Unmarshal(configBytes, &rawConfig)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("failed to unmarshal Spec.Config: %v", err)
   107  	}
   108  
   109  	// Inject network name if missing from Config for the thick plugin case
   110  	if n, ok := rawConfig["name"]; !ok || n == "" {
   111  		rawConfig["name"] = netName
   112  		configBytes, err = json.Marshal(rawConfig)
   113  		if err != nil {
   114  			return nil, fmt.Errorf("failed to re-marshal Spec.Config: %v", err)
   115  		}
   116  	}
   117  
   118  	return configBytes, nil
   119  }
   120  

View as plain text