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  }