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 }