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