mirror of
https://github.com/rancher/plugins.git
synced 2025-07-07 20:20:52 +00:00
Updates the spec and plugins to return an array of interfaces and IP details to the runtime including: - interface names and MAC addresses configured by the plugin - whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc) - multiple IP addresses configured by IPAM and which interface they have been assigned to Returning interface details is useful for runtimes, as well as allowing more flexible chaining of CNI plugins themselves. For example, some meta plugins may need to know the host-side interface to be able to apply firewall or traffic shaping rules to the container.
292 lines
7.2 KiB
Go
292 lines
7.2 KiB
Go
// Copyright 2016 CNI authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package current
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
|
|
"github.com/containernetworking/cni/pkg/types"
|
|
"github.com/containernetworking/cni/pkg/types/020"
|
|
)
|
|
|
|
const implementedSpecVersion string = "0.3.0"
|
|
|
|
var SupportedVersions = []string{implementedSpecVersion}
|
|
|
|
func NewResult(data []byte) (types.Result, error) {
|
|
result := &Result{}
|
|
if err := json.Unmarshal(data, result); err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func GetResult(r types.Result) (*Result, error) {
|
|
resultCurrent, err := r.GetAsVersion(implementedSpecVersion)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, ok := resultCurrent.(*Result)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to convert result")
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
var resultConverters = []struct {
|
|
versions []string
|
|
convert func(types.Result) (*Result, error)
|
|
}{
|
|
{types020.SupportedVersions, convertFrom020},
|
|
{SupportedVersions, convertFrom030},
|
|
}
|
|
|
|
func convertFrom020(result types.Result) (*Result, error) {
|
|
oldResult, err := types020.GetResult(result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newResult := &Result{
|
|
DNS: oldResult.DNS,
|
|
Routes: []*types.Route{},
|
|
}
|
|
|
|
if oldResult.IP4 != nil {
|
|
newResult.IPs = append(newResult.IPs, &IPConfig{
|
|
Version: "4",
|
|
Interface: -1,
|
|
Address: oldResult.IP4.IP,
|
|
Gateway: oldResult.IP4.Gateway,
|
|
})
|
|
for _, route := range oldResult.IP4.Routes {
|
|
gw := route.GW
|
|
if gw == nil {
|
|
gw = oldResult.IP4.Gateway
|
|
}
|
|
newResult.Routes = append(newResult.Routes, &types.Route{
|
|
Dst: route.Dst,
|
|
GW: gw,
|
|
})
|
|
}
|
|
}
|
|
|
|
if oldResult.IP6 != nil {
|
|
newResult.IPs = append(newResult.IPs, &IPConfig{
|
|
Version: "6",
|
|
Interface: -1,
|
|
Address: oldResult.IP6.IP,
|
|
Gateway: oldResult.IP6.Gateway,
|
|
})
|
|
for _, route := range oldResult.IP6.Routes {
|
|
gw := route.GW
|
|
if gw == nil {
|
|
gw = oldResult.IP6.Gateway
|
|
}
|
|
newResult.Routes = append(newResult.Routes, &types.Route{
|
|
Dst: route.Dst,
|
|
GW: gw,
|
|
})
|
|
}
|
|
}
|
|
|
|
if len(newResult.IPs) == 0 {
|
|
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
|
}
|
|
|
|
return newResult, nil
|
|
}
|
|
|
|
func convertFrom030(result types.Result) (*Result, error) {
|
|
newResult, ok := result.(*Result)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to convert result")
|
|
}
|
|
return newResult, nil
|
|
}
|
|
|
|
func NewResultFromResult(result types.Result) (*Result, error) {
|
|
version := result.Version()
|
|
for _, converter := range resultConverters {
|
|
for _, supportedVersion := range converter.versions {
|
|
if version == supportedVersion {
|
|
return converter.convert(result)
|
|
}
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("unsupported CNI result version %q", version)
|
|
}
|
|
|
|
// Result is what gets returned from the plugin (via stdout) to the caller
|
|
type Result struct {
|
|
Interfaces []*Interface `json:"interfaces,omitempty"`
|
|
IPs []*IPConfig `json:"ips,omitempty"`
|
|
Routes []*types.Route `json:"routes,omitempty"`
|
|
DNS types.DNS `json:"dns,omitempty"`
|
|
}
|
|
|
|
// Convert to the older 0.2.0 CNI spec Result type
|
|
func (r *Result) convertTo020() (*types020.Result, error) {
|
|
oldResult := &types020.Result{
|
|
DNS: r.DNS,
|
|
}
|
|
|
|
for _, ip := range r.IPs {
|
|
// Only convert the first IP address of each version as 0.2.0
|
|
// and earlier cannot handle multiple IP addresses
|
|
if ip.Version == "4" && oldResult.IP4 == nil {
|
|
oldResult.IP4 = &types020.IPConfig{
|
|
IP: ip.Address,
|
|
Gateway: ip.Gateway,
|
|
}
|
|
} else if ip.Version == "6" && oldResult.IP6 == nil {
|
|
oldResult.IP6 = &types020.IPConfig{
|
|
IP: ip.Address,
|
|
Gateway: ip.Gateway,
|
|
}
|
|
}
|
|
|
|
if oldResult.IP4 != nil && oldResult.IP6 != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
for _, route := range r.Routes {
|
|
is4 := route.Dst.IP.To4() != nil
|
|
if is4 && oldResult.IP4 != nil {
|
|
oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{
|
|
Dst: route.Dst,
|
|
GW: route.GW,
|
|
})
|
|
} else if !is4 && oldResult.IP6 != nil {
|
|
oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{
|
|
Dst: route.Dst,
|
|
GW: route.GW,
|
|
})
|
|
}
|
|
}
|
|
|
|
if oldResult.IP4 == nil && oldResult.IP6 == nil {
|
|
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
|
}
|
|
|
|
return oldResult, nil
|
|
}
|
|
|
|
func (r *Result) Version() string {
|
|
return implementedSpecVersion
|
|
}
|
|
|
|
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
|
switch version {
|
|
case implementedSpecVersion:
|
|
return r, nil
|
|
case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
|
|
return r.convertTo020()
|
|
}
|
|
return nil, fmt.Errorf("cannot convert version 0.3.0 to %q", version)
|
|
}
|
|
|
|
func (r *Result) Print() error {
|
|
data, err := json.MarshalIndent(r, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = os.Stdout.Write(data)
|
|
return err
|
|
}
|
|
|
|
// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
|
|
// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
|
|
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
|
func (r *Result) String() string {
|
|
var str string
|
|
if len(r.Interfaces) > 0 {
|
|
str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
|
|
}
|
|
if len(r.IPs) > 0 {
|
|
str += fmt.Sprintf("IP:%+v, ", r.IPs)
|
|
}
|
|
if len(r.Routes) > 0 {
|
|
str += fmt.Sprintf("Routes:%+v, ", r.Routes)
|
|
}
|
|
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
|
}
|
|
|
|
// Convert this old version result to the current CNI version result
|
|
func (r *Result) Convert() (*Result, error) {
|
|
return r, nil
|
|
}
|
|
|
|
// Interface contains values about the created interfaces
|
|
type Interface struct {
|
|
Name string `json:"name"`
|
|
Mac string `json:"mac,omitempty"`
|
|
Sandbox string `json:"sandbox,omitempty"`
|
|
}
|
|
|
|
func (i *Interface) String() string {
|
|
return fmt.Sprintf("%+v", *i)
|
|
}
|
|
|
|
// IPConfig contains values necessary to configure an IP address on an interface
|
|
type IPConfig struct {
|
|
// IP version, either "4" or "6"
|
|
Version string
|
|
// Index into Result structs Interfaces list
|
|
Interface int
|
|
Address net.IPNet
|
|
Gateway net.IP
|
|
}
|
|
|
|
func (i *IPConfig) String() string {
|
|
return fmt.Sprintf("%+v", *i)
|
|
}
|
|
|
|
// JSON (un)marshallable types
|
|
type ipConfig struct {
|
|
Version string `json:"version"`
|
|
Interface int `json:"interface,omitempty"`
|
|
Address types.IPNet `json:"address"`
|
|
Gateway net.IP `json:"gateway,omitempty"`
|
|
}
|
|
|
|
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
|
ipc := ipConfig{
|
|
Version: c.Version,
|
|
Interface: c.Interface,
|
|
Address: types.IPNet(c.Address),
|
|
Gateway: c.Gateway,
|
|
}
|
|
|
|
return json.Marshal(ipc)
|
|
}
|
|
|
|
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
|
ipc := ipConfig{}
|
|
if err := json.Unmarshal(data, &ipc); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.Version = ipc.Version
|
|
c.Interface = ipc.Interface
|
|
c.Address = net.IPNet(ipc.Address)
|
|
c.Gateway = ipc.Gateway
|
|
return nil
|
|
}
|