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")
|
|
}
|