multus-cni/pkg/server/shim.go
Tomofumi Hayashi d4a30c383d Make binary file and directory name consistent
This change make binary file and directory name consistent.
In addition, change the package name cni to server because cni
is a bit umbiguous for cni plugin's repository.
2022-04-06 00:34:53 +09:00

150 lines
3.8 KiB
Go

package server
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"strings"
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
)
const (
defaultMultusRunDir = "/var/run/multus-cni/"
)
// CmdAdd implements the CNI spec ADD command handler
func CmdAdd(args *skel.CmdArgs) error {
response, err := postRequest(args)
if err != nil {
return err
}
logging.Verbosef("CmdAdd (shim): %v", *response.Result)
return cnitypes.PrintResult(response.Result, response.Result.CNIVersion)
}
// CmdCheck implements the CNI spec CHECK command handler
func CmdCheck(args *skel.CmdArgs) error {
response, err := postRequest(args)
if err != nil {
return err
}
logging.Verbosef("CmdCheck (shim): %v", *response.Result)
return cnitypes.PrintResult(response.Result, response.Result.CNIVersion)
}
// CmdDel implements the CNI spec DEL command handler
func CmdDel(args *skel.CmdArgs) error {
response, err := postRequest(args)
if err != nil {
return err
}
logging.Verbosef("CmdDel (shim): %v", *response.Result)
return cnitypes.PrintResult(response.Result, response.Result.CNIVersion)
}
func postRequest(args *skel.CmdArgs) (*Response, error) {
multusShimConfig, err := shimConfig(args.StdinData)
if err != nil {
return nil, fmt.Errorf("invalid CNI configuration passed to multus-shim: %w", err)
}
cniRequest, err := newCNIRequest(args)
if err != nil {
return nil, err
}
body, err := DoCNI("http://dummy/", cniRequest, SocketPath(multusShimConfig.MultusSocketDir))
if err != nil {
return nil, err
}
response := &Response{}
if err = json.Unmarshal(body, response); err != nil {
err = fmt.Errorf("failed to unmarshal response '%s': %v", string(body), err)
return nil, err
}
return response, nil
}
// Create and fill a Request with this Plugin's environment and stdin which
// contain the CNI variables and configuration
func newCNIRequest(args *skel.CmdArgs) (*Request, error) {
envMap := make(map[string]string)
for _, item := range os.Environ() {
idx := strings.Index(item, "=")
if idx > 0 {
envMap[strings.TrimSpace(item[:idx])] = item[idx+1:]
}
}
return &Request{
Env: envMap,
Config: args.StdinData,
}, nil
}
func shimConfig(cniConfig []byte) (*types.ShimNetConf, error) {
multusConfig := &types.ShimNetConf{}
if err := json.Unmarshal(cniConfig, multusConfig); err != nil {
return nil, fmt.Errorf("failed to gather the multus configuration: %w", err)
}
if multusConfig.MultusSocketDir == "" {
multusConfig.MultusSocketDir = defaultMultusRunDir
}
// Logging
logging.SetLogStderr(multusConfig.LogToStderr)
if multusConfig.LogFile != "" {
logging.SetLogFile(multusConfig.LogFile)
}
if multusConfig.LogLevel != "" {
logging.SetLogLevel(multusConfig.LogLevel)
}
return multusConfig, nil
}
// DoCNI sends a CNI request to the CNI server via JSON + HTTP over a root-owned unix socket,
// and returns the result
func DoCNI(url string, req interface{}, socketPath string) ([]byte, error) {
data, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("failed to marshal CNI request %v: %v", req, err)
}
client := &http.Client{
Transport: &http.Transport{
Dial: func(proto, addr string) (net.Conn, error) {
return net.Dial("unix", socketPath)
},
},
}
resp, err := client.Post(url, "application/json", bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("failed to send CNI request: %v", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read CNI result: %v", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("CNI request failed with status %v: '%s'", resp.StatusCode, string(body))
}
return body, nil
}