github.com/john-lin/cni@v0.6.0-rc1.0.20170712150331-b69e640cc0e2/libcni/conf.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  	"encoding/json"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"os"
    22  	"path/filepath"
    23  	"sort"
    24  )
    25  
    26  type NotFoundError struct {
    27  	Dir  string
    28  	Name string
    29  }
    30  
    31  func (e NotFoundError) Error() string {
    32  	return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
    33  }
    34  
    35  type NoConfigsFoundError struct {
    36  	Dir string
    37  }
    38  
    39  func (e NoConfigsFoundError) Error() string {
    40  	return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
    41  }
    42  
    43  func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
    44  	conf := &NetworkConfig{Bytes: bytes}
    45  	if err := json.Unmarshal(bytes, &conf.Network); err != nil {
    46  		return nil, fmt.Errorf("error parsing configuration: %s", err)
    47  	}
    48  	return conf, nil
    49  }
    50  
    51  func ConfFromFile(filename string) (*NetworkConfig, error) {
    52  	bytes, err := ioutil.ReadFile(filename)
    53  	if err != nil {
    54  		return nil, fmt.Errorf("error reading %s: %s", filename, err)
    55  	}
    56  	return ConfFromBytes(bytes)
    57  }
    58  
    59  func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
    60  	rawList := make(map[string]interface{})
    61  	if err := json.Unmarshal(bytes, &rawList); err != nil {
    62  		return nil, fmt.Errorf("error parsing configuration list: %s", err)
    63  	}
    64  
    65  	rawName, ok := rawList["name"]
    66  	if !ok {
    67  		return nil, fmt.Errorf("error parsing configuration list: no name")
    68  	}
    69  	name, ok := rawName.(string)
    70  	if !ok {
    71  		return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName)
    72  	}
    73  
    74  	var cniVersion string
    75  	rawVersion, ok := rawList["cniVersion"]
    76  	if ok {
    77  		cniVersion, ok = rawVersion.(string)
    78  		if !ok {
    79  			return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion)
    80  		}
    81  	}
    82  
    83  	list := &NetworkConfigList{
    84  		Name:       name,
    85  		CNIVersion: cniVersion,
    86  		Bytes:      bytes,
    87  	}
    88  
    89  	var plugins []interface{}
    90  	plug, ok := rawList["plugins"]
    91  	if !ok {
    92  		return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key")
    93  	}
    94  	plugins, ok = plug.([]interface{})
    95  	if !ok {
    96  		return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
    97  	}
    98  	if len(plugins) == 0 {
    99  		return nil, fmt.Errorf("error parsing configuration list: no plugins in list")
   100  	}
   101  
   102  	for i, conf := range plugins {
   103  		newBytes, err := json.Marshal(conf)
   104  		if err != nil {
   105  			return nil, fmt.Errorf("Failed to marshal plugin config %d: %v", i, err)
   106  		}
   107  		netConf, err := ConfFromBytes(newBytes)
   108  		if err != nil {
   109  			return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err)
   110  		}
   111  		list.Plugins = append(list.Plugins, netConf)
   112  	}
   113  
   114  	return list, nil
   115  }
   116  
   117  func ConfListFromFile(filename string) (*NetworkConfigList, error) {
   118  	bytes, err := ioutil.ReadFile(filename)
   119  	if err != nil {
   120  		return nil, fmt.Errorf("error reading %s: %s", filename, err)
   121  	}
   122  	return ConfListFromBytes(bytes)
   123  }
   124  
   125  func ConfFiles(dir string, extensions []string) ([]string, error) {
   126  	// In part, adapted from rkt/networking/podenv.go#listFiles
   127  	files, err := ioutil.ReadDir(dir)
   128  	switch {
   129  	case err == nil: // break
   130  	case os.IsNotExist(err):
   131  		return nil, nil
   132  	default:
   133  		return nil, err
   134  	}
   135  
   136  	confFiles := []string{}
   137  	for _, f := range files {
   138  		if f.IsDir() {
   139  			continue
   140  		}
   141  		fileExt := filepath.Ext(f.Name())
   142  		for _, ext := range extensions {
   143  			if fileExt == ext {
   144  				confFiles = append(confFiles, filepath.Join(dir, f.Name()))
   145  			}
   146  		}
   147  	}
   148  	return confFiles, nil
   149  }
   150  
   151  func LoadConf(dir, name string) (*NetworkConfig, error) {
   152  	files, err := ConfFiles(dir, []string{".conf", ".json"})
   153  	switch {
   154  	case err != nil:
   155  		return nil, err
   156  	case len(files) == 0:
   157  		return nil, NoConfigsFoundError{Dir: dir}
   158  	}
   159  	sort.Strings(files)
   160  
   161  	for _, confFile := range files {
   162  		conf, err := ConfFromFile(confFile)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  		if conf.Network.Name == name {
   167  			return conf, nil
   168  		}
   169  	}
   170  	return nil, NotFoundError{dir, name}
   171  }
   172  
   173  func LoadConfList(dir, name string) (*NetworkConfigList, error) {
   174  	files, err := ConfFiles(dir, []string{".conflist"})
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	sort.Strings(files)
   179  
   180  	for _, confFile := range files {
   181  		conf, err := ConfListFromFile(confFile)
   182  		if err != nil {
   183  			return nil, err
   184  		}
   185  		if conf.Name == name {
   186  			return conf, nil
   187  		}
   188  	}
   189  
   190  	// Try and load a network configuration file (instead of list)
   191  	// from the same name, then upconvert.
   192  	singleConf, err := LoadConf(dir, name)
   193  	if err != nil {
   194  		// A little extra logic so the error makes sense
   195  		if _, ok := err.(NoConfigsFoundError); len(files) != 0 && ok {
   196  			// Config lists found but no config files found
   197  			return nil, NotFoundError{dir, name}
   198  		}
   199  
   200  		return nil, err
   201  	}
   202  	return ConfListFromConf(singleConf)
   203  }
   204  
   205  func InjectConf(original *NetworkConfig, newValues map[string]interface{}) (*NetworkConfig, error) {
   206  	config := make(map[string]interface{})
   207  	err := json.Unmarshal(original.Bytes, &config)
   208  	if err != nil {
   209  		return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
   210  	}
   211  
   212  	for key, value := range newValues {
   213  		if key == "" {
   214  			return nil, fmt.Errorf("keys cannot be empty")
   215  		}
   216  
   217  		if value == nil {
   218  			return nil, fmt.Errorf("key '%s' value must not be nil", key)
   219  		}
   220  
   221  		config[key] = value
   222  	}
   223  
   224  	newBytes, err := json.Marshal(config)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	return ConfFromBytes(newBytes)
   230  }
   231  
   232  // ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
   233  // with the single network as the only entry in the list.
   234  func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) {
   235  	// Re-deserialize the config's json, then make a raw map configlist.
   236  	// This may seem a bit strange, but it's to make the Bytes fields
   237  	// actually make sense. Otherwise, the generated json is littered with
   238  	// golang default values.
   239  
   240  	rawConfig := make(map[string]interface{})
   241  	if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	rawConfigList := map[string]interface{}{
   246  		"name":       original.Network.Name,
   247  		"cniVersion": original.Network.CNIVersion,
   248  		"plugins":    []interface{}{rawConfig},
   249  	}
   250  
   251  	b, err := json.Marshal(rawConfigList)
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	return ConfListFromBytes(b)
   256  }