github.com/openshift/dpu-operator@v0.0.0-20240502153209-3af840d137c2/dpu-cni/pkgs/cni/cnishim.go (about)

     1  package cni
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"net/http"
    10  
    11  	"github.com/containernetworking/cni/pkg/skel"
    12  	"github.com/containernetworking/cni/pkg/types"
    13  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/cnihelper"
    14  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/cnilogging"
    15  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/cnitypes"
    16  )
    17  
    18  // Plugin is the data structure to hold the endpoint information and the corresponding
    19  // functions that use it
    20  type Plugin struct {
    21  	SocketPath string
    22  }
    23  
    24  // NewCNIPlugin creates the internal Plugin object
    25  func NewCNIPlugin() *Plugin {
    26  	return &Plugin{SocketPath: cnitypes.ServerSocketPath}
    27  }
    28  
    29  // postRequest reads the cni config args and forwards it via an HTTP post request. The response.
    30  // if any, is passed back to this CNI's plugin.
    31  func (p *Plugin) PostRequest(args *skel.CmdArgs) (*cnitypes.Response, string, error) {
    32  	cniRequest := cnihelper.NewCNIRequest(args)
    33  
    34  	// Read the cni config stdin args to obtain cniVersion
    35  	conf, err := cnihelper.ReadCNIConfig(args.StdinData)
    36  	if err != nil {
    37  		err = fmt.Errorf("invalid stdin args %v", err)
    38  		return nil, conf.CNIVersion, err
    39  	}
    40  
    41  	var body []byte
    42  	body, err = p.doCNI("http://dummy/cni", cniRequest)
    43  	if err != nil {
    44  		return nil, conf.CNIVersion, fmt.Errorf("%s: StdinData: %s", err.Error(), string(args.StdinData))
    45  	}
    46  
    47  	response := &cnitypes.Response{}
    48  	if len(body) != 0 {
    49  		if err = json.Unmarshal(body, response); err != nil {
    50  			err = fmt.Errorf("failed to unmarshal response '%s': %v", string(body), err)
    51  			return nil, conf.CNIVersion, err
    52  		}
    53  	}
    54  	return response, conf.CNIVersion, nil
    55  }
    56  
    57  // doCNI sends a CNI request to the CNI server via JSON + HTTP over a root-owned unix socket,
    58  // and returns the result
    59  func (p *Plugin) doCNI(url string, req interface{}) ([]byte, error) {
    60  	data, err := json.Marshal(req)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("failed to marshal CNI request %v: %v", req, err)
    63  	}
    64  
    65  	client := &http.Client{
    66  		Transport: &http.Transport{
    67  			Dial: func(proto, addr string) (net.Conn, error) {
    68  				return net.Dial("unix", p.SocketPath)
    69  			},
    70  		},
    71  	}
    72  
    73  	resp, err := client.Post(url, "application/json", bytes.NewReader(data))
    74  	if err != nil {
    75  		return nil, fmt.Errorf("failed to send CNI request: %v", err)
    76  	}
    77  	defer resp.Body.Close()
    78  
    79  	body, err := io.ReadAll(resp.Body)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("failed to read CNI result: %v", err)
    82  	}
    83  
    84  	if resp.StatusCode != 200 {
    85  		return nil, fmt.Errorf("CNI request failed with status %v: '%s'", resp.StatusCode, string(body))
    86  	}
    87  
    88  	return body, nil
    89  }
    90  
    91  // SetLogging sets global logging parameters.
    92  func SetLogging(stdinData []byte, containerID, netns, ifName string) error {
    93  	n := &cnitypes.NetConf{}
    94  	if err := json.Unmarshal(stdinData, n); err != nil {
    95  		return fmt.Errorf("SetLogging(): failed to load netconf: %v", err)
    96  	}
    97  
    98  	cnilogging.Init(n.LogLevel, n.LogFile, containerID, netns, ifName)
    99  	return nil
   100  }
   101  
   102  // CmdAdd is the callback for 'add' cni calls from skel
   103  func (p *Plugin) CmdAdd(args *skel.CmdArgs) error {
   104  	if err := SetLogging(args.StdinData, args.ContainerID, args.Netns, args.IfName); err != nil {
   105  		return err
   106  	}
   107  
   108  	cnilogging.Info("function called",
   109  		"func", "cmdAdd",
   110  		"args.Path", args.Path, "args.StdinData", string(args.StdinData), "args.Args", args.Args)
   111  
   112  	resp, cniVersion, err := p.PostRequest(args)
   113  	if err != nil {
   114  		return fmt.Errorf("failed to post request for cmdAdd: %v", err)
   115  	}
   116  
   117  	return types.PrintResult(resp.Result, cniVersion)
   118  }
   119  
   120  func (p *Plugin) CmdDel(args *skel.CmdArgs) error {
   121  	if err := SetLogging(args.StdinData, args.ContainerID, args.Netns, args.IfName); err != nil {
   122  		return err
   123  	}
   124  
   125  	cnilogging.Info("function called",
   126  		"func", "cmdDel",
   127  		"args.Path", args.Path, "args.StdinData", string(args.StdinData), "args.Args", args.Args)
   128  
   129  	_, _, err := p.PostRequest(args)
   130  	if err != nil {
   131  		return fmt.Errorf("failed to post request for cmdAdd: %v", err)
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func (p *Plugin) CmdCheck(_ *skel.CmdArgs) error {
   138  	return nil
   139  }