3a4d901fa6
CNI spec defines the way to return errors from the CNI plugin, along with well-defined error codes that can be used by the CNI to decide how to treat the error. This change updates service.py and main.go to return CNI errors in the specified form. Change-Id: Ib76debb56aeb746b92c8260be00f3445cca5948f Closes-Bug: #1899489
236 lines
5.9 KiB
Go
236 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
|
|
"github.com/containernetworking/cni/pkg/skel"
|
|
"github.com/containernetworking/cni/pkg/types"
|
|
cni "github.com/containernetworking/cni/pkg/types"
|
|
"github.com/containernetworking/cni/pkg/types/current"
|
|
"github.com/containernetworking/cni/pkg/version"
|
|
)
|
|
|
|
const (
|
|
// FIXME(dulek): We don't really have a good way to fetch current URL:port binding here.
|
|
// I'm hardcoding it for now, but in the future we should probably put it in
|
|
// the JSON config in 10-kuryr.conflist file that we will get passed on stdin.
|
|
urlBase = "http://localhost:5036/"
|
|
addPath = "addNetwork"
|
|
delPath = "delNetwork"
|
|
|
|
ErrVif uint = 899
|
|
ErrParsing uint = 799
|
|
)
|
|
|
|
type KuryrDaemonData struct {
|
|
IfName string `json:"CNI_IFNAME"`
|
|
Netns string `json:"CNI_NETNS"`
|
|
Path string `json:"CNI_PATH"`
|
|
Command string `json:"CNI_COMMAND"`
|
|
ContainerID string `json:"CNI_CONTAINERID"`
|
|
Args string `json:"CNI_ARGS"`
|
|
KuryrConf interface{} `json:"config_kuryr"`
|
|
}
|
|
|
|
func transformData(args *skel.CmdArgs, command string) (KuryrDaemonData, error) {
|
|
var conf interface{}
|
|
err := json.Unmarshal(args.StdinData, &conf)
|
|
if err != nil {
|
|
newErr := types.Error{
|
|
Code: types.ErrDecodingFailure,
|
|
Msg: fmt.Sprintf("Error when reading configuration: %v", err),
|
|
Details: "",
|
|
}
|
|
return KuryrDaemonData{}, &newErr
|
|
}
|
|
|
|
return KuryrDaemonData{
|
|
IfName: args.IfName,
|
|
Netns: args.Netns,
|
|
Path: args.Path,
|
|
Command: command,
|
|
ContainerID: args.ContainerID,
|
|
Args: args.Args,
|
|
KuryrConf: conf,
|
|
}, nil
|
|
}
|
|
|
|
func makeDaemonRequest(data KuryrDaemonData, expectedCode int) ([]byte, error) {
|
|
log.Printf("Calling kuryr-daemon with %s request (CNI_ARGS=%s, CNI_NETNS=%s).", data.Command, data.Args, data.Netns)
|
|
|
|
b, err := json.Marshal(data)
|
|
if err != nil {
|
|
return []byte{}, &types.Error{
|
|
Code: types.ErrInvalidNetworkConfig,
|
|
Msg: fmt.Sprintf("Error when preparing payload for kuryr-daemon: %v", err),
|
|
Details: "",
|
|
}
|
|
}
|
|
|
|
url := ""
|
|
switch data.Command {
|
|
case "ADD":
|
|
url = urlBase + addPath
|
|
case "DEL":
|
|
url = urlBase + delPath
|
|
default:
|
|
return []byte{}, &types.Error{
|
|
Code: types.ErrInvalidEnvironmentVariables,
|
|
Msg: fmt.Sprintf("Cannot handle command %s", data.Command),
|
|
Details: "",
|
|
}
|
|
}
|
|
|
|
resp, err := http.Post(url, "application/json", bytes.NewBuffer(b))
|
|
if err != nil {
|
|
return []byte{}, &types.Error{
|
|
Code: types.ErrTryAgainLater,
|
|
Msg: fmt.Sprintf("Looks like %s cannot be reached. Is kuryr-daemon running?", url),
|
|
Details: fmt.Sprintf("%v", err),
|
|
}
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
if resp.StatusCode != expectedCode {
|
|
if len(body) > 1 {
|
|
var err types.Error
|
|
json.Unmarshal(body, &err)
|
|
return []byte{}, &err
|
|
}
|
|
return []byte{}, &types.Error{
|
|
Code: uint(resp.StatusCode),
|
|
Msg: fmt.Sprintf("CNI Daemon returned error %d %s", resp.StatusCode, body),
|
|
Details: "",
|
|
}
|
|
}
|
|
return body, nil
|
|
}
|
|
|
|
func cmdAdd(args *skel.CmdArgs) error {
|
|
data, err := transformData(args, "ADD")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
body, err := makeDaemonRequest(data, 202)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
vif := VIF{}
|
|
er := json.Unmarshal(body, &vif)
|
|
if er != nil {
|
|
return &types.Error{
|
|
Code: ErrVif,
|
|
Msg: fmt.Sprintf("Error when reading response from kuryr-daemon: %s", string(body)),
|
|
Details: fmt.Sprintf("%v", er),
|
|
}
|
|
}
|
|
|
|
iface := current.Interface{}
|
|
iface.Name = args.IfName
|
|
iface.Mac = vif.Address
|
|
iface.Sandbox = args.ContainerID
|
|
|
|
var ips []*current.IPConfig
|
|
var dns types.DNS
|
|
var routes []*types.Route
|
|
for _, subnet := range vif.Network.Subnets {
|
|
addrStr := subnet.Ips[0].Address
|
|
addr := net.ParseIP(addrStr)
|
|
if addr == nil {
|
|
return &types.Error{
|
|
Code: ErrParsing,
|
|
Msg: fmt.Sprintf("Error when parsing IP address %s received from kuryr-daemon", addrStr),
|
|
Details: "",
|
|
}
|
|
}
|
|
_, cidr, err := net.ParseCIDR(subnet.Cidr)
|
|
if err != nil {
|
|
return &types.Error{
|
|
Code: ErrParsing,
|
|
Msg: fmt.Sprintf("Error when parsing CIDR %s received from kuryr-daemon", subnet.Cidr),
|
|
Details: fmt.Sprintf("%v", err),
|
|
}
|
|
}
|
|
|
|
ver := "4"
|
|
if addr.To4() == nil {
|
|
ver = "6"
|
|
}
|
|
|
|
prefixSize, _ := cidr.Mask.Size()
|
|
ifaceCIDR := fmt.Sprintf("%s/%d", addr.String(), prefixSize)
|
|
ipAddress, err := cni.ParseCIDR(ifaceCIDR)
|
|
if err != nil {
|
|
return &types.Error{
|
|
Code: ErrParsing,
|
|
Msg: fmt.Sprintf("Error when parsing CIDR %s received from kuryr-daemon", ifaceCIDR),
|
|
Details: fmt.Sprintf("%v", err),
|
|
}
|
|
}
|
|
ifaceNum := 0
|
|
|
|
ips = append(ips, ¤t.IPConfig{
|
|
Version: ver,
|
|
Interface: &ifaceNum,
|
|
Gateway: net.ParseIP(subnet.Gateway),
|
|
Address: *ipAddress,
|
|
})
|
|
|
|
for _, route := range subnet.Routes {
|
|
_, dst, err := net.ParseCIDR(route.Cidr)
|
|
if err != nil {
|
|
return &types.Error{
|
|
Code: ErrParsing,
|
|
Msg: fmt.Sprintf("Error when parsing CIDR %s received from kuryr-daemon", route.Cidr),
|
|
Details: fmt.Sprintf("%v", err),
|
|
}
|
|
}
|
|
|
|
gw := net.ParseIP(route.Gateway)
|
|
if gw == nil {
|
|
return &types.Error{
|
|
Code: ErrParsing,
|
|
Msg: fmt.Sprintf("Error when parsing IP address %s received from kuryr-daemon", route.Gateway),
|
|
Details: "",
|
|
}
|
|
}
|
|
|
|
routes = append(routes, &types.Route{Dst: *dst, GW: gw})
|
|
}
|
|
|
|
dns.Nameservers = append(dns.Nameservers, subnet.DNS...)
|
|
}
|
|
|
|
res := ¤t.Result{
|
|
Interfaces: []*current.Interface{&iface},
|
|
IPs: ips,
|
|
DNS: dns,
|
|
Routes: routes,
|
|
}
|
|
|
|
return types.PrintResult(res, "0.3.1")
|
|
}
|
|
|
|
func cmdDel(args *skel.CmdArgs) error {
|
|
data, err := transformData(args, "DEL")
|
|
_, err = makeDaemonRequest(data, 204)
|
|
return err
|
|
}
|
|
|
|
func cmdCheck(args *skel.CmdArgs) error {
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, "CNI Plugin Kuryr-Kubernetes v1.0.0")
|
|
}
|