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 }