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