github.com/john-lin/cni@v0.6.0-rc1.0.20170712150331-b69e640cc0e2/pkg/types/current/types.go (about) 1 // Copyright 2016 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 current 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net" 21 "os" 22 23 "github.com/containernetworking/cni/pkg/types" 24 "github.com/containernetworking/cni/pkg/types/020" 25 ) 26 27 const ImplementedSpecVersion string = "0.3.1" 28 29 var SupportedVersions = []string{"0.3.0", ImplementedSpecVersion} 30 31 func NewResult(data []byte) (types.Result, error) { 32 result := &Result{} 33 if err := json.Unmarshal(data, result); err != nil { 34 return nil, err 35 } 36 return result, nil 37 } 38 39 func GetResult(r types.Result) (*Result, error) { 40 resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) 41 if err != nil { 42 return nil, err 43 } 44 result, ok := resultCurrent.(*Result) 45 if !ok { 46 return nil, fmt.Errorf("failed to convert result") 47 } 48 return result, nil 49 } 50 51 var resultConverters = []struct { 52 versions []string 53 convert func(types.Result) (*Result, error) 54 }{ 55 {types020.SupportedVersions, convertFrom020}, 56 {SupportedVersions, convertFrom030}, 57 } 58 59 func convertFrom020(result types.Result) (*Result, error) { 60 oldResult, err := types020.GetResult(result) 61 if err != nil { 62 return nil, err 63 } 64 65 newResult := &Result{ 66 CNIVersion: ImplementedSpecVersion, 67 DNS: oldResult.DNS, 68 Routes: []*types.Route{}, 69 } 70 71 if oldResult.IP4 != nil { 72 newResult.IPs = append(newResult.IPs, &IPConfig{ 73 Version: "4", 74 Address: oldResult.IP4.IP, 75 Gateway: oldResult.IP4.Gateway, 76 }) 77 for _, route := range oldResult.IP4.Routes { 78 gw := route.GW 79 if gw == nil { 80 gw = oldResult.IP4.Gateway 81 } 82 newResult.Routes = append(newResult.Routes, &types.Route{ 83 Dst: route.Dst, 84 GW: gw, 85 }) 86 } 87 } 88 89 if oldResult.IP6 != nil { 90 newResult.IPs = append(newResult.IPs, &IPConfig{ 91 Version: "6", 92 Address: oldResult.IP6.IP, 93 Gateway: oldResult.IP6.Gateway, 94 }) 95 for _, route := range oldResult.IP6.Routes { 96 gw := route.GW 97 if gw == nil { 98 gw = oldResult.IP6.Gateway 99 } 100 newResult.Routes = append(newResult.Routes, &types.Route{ 101 Dst: route.Dst, 102 GW: gw, 103 }) 104 } 105 } 106 107 if len(newResult.IPs) == 0 { 108 return nil, fmt.Errorf("cannot convert: no valid IP addresses") 109 } 110 111 return newResult, nil 112 } 113 114 func convertFrom030(result types.Result) (*Result, error) { 115 newResult, ok := result.(*Result) 116 if !ok { 117 return nil, fmt.Errorf("failed to convert result") 118 } 119 newResult.CNIVersion = ImplementedSpecVersion 120 return newResult, nil 121 } 122 123 func NewResultFromResult(result types.Result) (*Result, error) { 124 version := result.Version() 125 for _, converter := range resultConverters { 126 for _, supportedVersion := range converter.versions { 127 if version == supportedVersion { 128 return converter.convert(result) 129 } 130 } 131 } 132 return nil, fmt.Errorf("unsupported CNI result22 version %q", version) 133 } 134 135 // Result is what gets returned from the plugin (via stdout) to the caller 136 type Result struct { 137 CNIVersion string `json:"cniVersion,omitempty"` 138 Interfaces []*Interface `json:"interfaces,omitempty"` 139 IPs []*IPConfig `json:"ips,omitempty"` 140 Routes []*types.Route `json:"routes,omitempty"` 141 DNS types.DNS `json:"dns,omitempty"` 142 } 143 144 // Convert to the older 0.2.0 CNI spec Result type 145 func (r *Result) convertTo020() (*types020.Result, error) { 146 oldResult := &types020.Result{ 147 CNIVersion: types020.ImplementedSpecVersion, 148 DNS: r.DNS, 149 } 150 151 for _, ip := range r.IPs { 152 // Only convert the first IP address of each version as 0.2.0 153 // and earlier cannot handle multiple IP addresses 154 if ip.Version == "4" && oldResult.IP4 == nil { 155 oldResult.IP4 = &types020.IPConfig{ 156 IP: ip.Address, 157 Gateway: ip.Gateway, 158 } 159 } else if ip.Version == "6" && oldResult.IP6 == nil { 160 oldResult.IP6 = &types020.IPConfig{ 161 IP: ip.Address, 162 Gateway: ip.Gateway, 163 } 164 } 165 166 if oldResult.IP4 != nil && oldResult.IP6 != nil { 167 break 168 } 169 } 170 171 for _, route := range r.Routes { 172 is4 := route.Dst.IP.To4() != nil 173 if is4 && oldResult.IP4 != nil { 174 oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{ 175 Dst: route.Dst, 176 GW: route.GW, 177 }) 178 } else if !is4 && oldResult.IP6 != nil { 179 oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{ 180 Dst: route.Dst, 181 GW: route.GW, 182 }) 183 } 184 } 185 186 if oldResult.IP4 == nil && oldResult.IP6 == nil { 187 return nil, fmt.Errorf("cannot convert: no valid IP addresses") 188 } 189 190 return oldResult, nil 191 } 192 193 func (r *Result) Version() string { 194 return ImplementedSpecVersion 195 } 196 197 func (r *Result) GetAsVersion(version string) (types.Result, error) { 198 switch version { 199 case "0.3.0", ImplementedSpecVersion: 200 r.CNIVersion = version 201 return r, nil 202 case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: 203 return r.convertTo020() 204 } 205 return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version) 206 } 207 208 func (r *Result) Print() error { 209 data, err := json.MarshalIndent(r, "", " ") 210 if err != nil { 211 return err 212 } 213 _, err = os.Stdout.Write(data) 214 return err 215 } 216 217 // String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where 218 // $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the 219 // receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string. 220 func (r *Result) String() string { 221 var str string 222 if len(r.Interfaces) > 0 { 223 str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces) 224 } 225 if len(r.IPs) > 0 { 226 str += fmt.Sprintf("IP:%+v, ", r.IPs) 227 } 228 if len(r.Routes) > 0 { 229 str += fmt.Sprintf("Routes:%+v, ", r.Routes) 230 } 231 return fmt.Sprintf("%sDNS:%+v", str, r.DNS) 232 } 233 234 // Convert this old version result to the current CNI version result 235 func (r *Result) Convert() (*Result, error) { 236 return r, nil 237 } 238 239 // Interface contains values about the created interfaces 240 type Interface struct { 241 Name string `json:"name"` 242 Mac string `json:"mac,omitempty"` 243 Sandbox string `json:"sandbox,omitempty"` 244 } 245 246 func (i *Interface) String() string { 247 return fmt.Sprintf("%+v", *i) 248 } 249 250 // Int returns a pointer to the int value passed in. Used to 251 // set the IPConfig.Interface field. 252 func Int(v int) *int { 253 return &v 254 } 255 256 // IPConfig contains values necessary to configure an IP address on an interface 257 type IPConfig struct { 258 // IP version, either "4" or "6" 259 Version string 260 // Index into Result structs Interfaces list 261 Interface *int 262 Address net.IPNet 263 Gateway net.IP 264 } 265 266 func (i *IPConfig) String() string { 267 return fmt.Sprintf("%+v", *i) 268 } 269 270 // JSON (un)marshallable types 271 type ipConfig struct { 272 Version string `json:"version"` 273 Interface *int `json:"interface,omitempty"` 274 Address types.IPNet `json:"address"` 275 Gateway net.IP `json:"gateway,omitempty"` 276 } 277 278 func (c *IPConfig) MarshalJSON() ([]byte, error) { 279 ipc := ipConfig{ 280 Version: c.Version, 281 Interface: c.Interface, 282 Address: types.IPNet(c.Address), 283 Gateway: c.Gateway, 284 } 285 286 return json.Marshal(ipc) 287 } 288 289 func (c *IPConfig) UnmarshalJSON(data []byte) error { 290 ipc := ipConfig{} 291 if err := json.Unmarshal(data, &ipc); err != nil { 292 return err 293 } 294 295 c.Version = ipc.Version 296 c.Interface = ipc.Interface 297 c.Address = net.IPNet(ipc.Address) 298 c.Gateway = ipc.Gateway 299 return nil 300 }