Skip to content

Commit

Permalink
libcni, skel: implement STATUS
Browse files Browse the repository at this point in the history
This adds an implementation of STATUS to libcni and skel, along with
tests.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
  • Loading branch information
squeed committed Nov 6, 2023
1 parent 17331ad commit ea6bad2
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 18 deletions.
36 changes: 36 additions & 0 deletions libcni/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ type CNI interface {
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)

GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error

GetCachedAttachments(containerID string) ([]*NetworkAttachment, error)
}
Expand Down Expand Up @@ -829,6 +830,41 @@ func (c *CNIConfig) gcNetwork(ctx context.Context, net *NetworkConfig) error {
return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
}

func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfigList) error {
// If the version doesn't support status, abort.
if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); !gt {
return nil
}

inject := map[string]interface{}{
"name": list.Name,
"cniVersion": list.CNIVersion,
}

for _, plugin := range list.Plugins {
// build config here
pluginConfig, err := InjectConf(plugin, inject)
if err != nil {
return fmt.Errorf("failed to generate configuration to get plugin STATUS %s: %w", plugin.Network.Type, err)
}
if err := c.getStatusNetwork(ctx, pluginConfig); err != nil {
return err // Don't collect errors here, so we return a clean error code.
}
}
return nil
}

func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *NetworkConfig) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
args := c.args("STATUS", &RuntimeConf{})

return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec)
}

// =====
func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
return &invoke.Args{
Expand Down
38 changes: 38 additions & 0 deletions libcni/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,44 @@ var _ = Describe("Invoking plugins", func() {
}
})
})
Describe("GetStatusNetworkList", func() {
It("issues a STATUS request", func() {
netConfigList, plugins = makePluginList("1.1.0", ipResult, rcMap)

err := cniConfig.GetStatusNetworkList(ctx, netConfigList)
Expect(err).NotTo(HaveOccurred())

debug, err := noop_debug.ReadDebug(plugins[0].debugFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(debug.Command).To(Equal("STATUS"))
})

It("correctly reports an error", func() {
netConfigList, plugins = makePluginList("1.1.0", ipResult, rcMap)

plugins[1].debug.ReportError = "plugin error: banana"
plugins[1].debug.ReportErrorCode = 50
Expect(plugins[1].debug.WriteDebug(plugins[1].debugFilePath)).To(Succeed())

err := cniConfig.GetStatusNetworkList(ctx, netConfigList)
Expect(err).To(HaveOccurred())
var eerr *types.Error
Expect(errors.As(err, &eerr)).To(BeTrue())
Expect(eerr.Code).To(Equal(uint(50)))

debug, err := noop_debug.ReadDebug(plugins[0].debugFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(debug.Command).To(Equal("STATUS"))

debug, err = noop_debug.ReadDebug(plugins[1].debugFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(debug.Command).To(Equal("STATUS"))

debug, err = noop_debug.ReadDebug(plugins[2].debugFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(debug.Command).To(Equal(""))
})
})
})

Describe("Invoking a sleep plugin", func() {
Expand Down
49 changes: 37 additions & 12 deletions pkg/skel/skel.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) {
"CNI_COMMAND",
&cmd,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": true,
"GC": true,
"ADD": true,
"CHECK": true,
"DEL": true,
"GC": true,
"STATUS": true,
},
nil,
},
Expand Down Expand Up @@ -120,10 +121,11 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) {
"CNI_PATH",
&path,
reqForCmdEntry{
"ADD": true,
"CHECK": true,
"DEL": true,
"GC": true,
"ADD": true,
"CHECK": true,
"DEL": true,
"GC": true,
"STATUS": true,
},
nil,
},
Expand Down Expand Up @@ -310,6 +312,28 @@ func (t *dispatcher) pluginMain(funcs CNIFuncs, versionInfo version.PluginInfo,
}
}
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow GC", "")
case "STATUS":
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
}
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "1.1.0"); err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if !gtet {
return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow STATUS", "")
}
for _, pluginVersion := range versionInfo.SupportedVersions() {
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
if err != nil {
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if gtet {
if err := t.checkVersionAndCall(cmdArgs, versionInfo, funcs.Status); err != nil {
return err
}
return nil
}
}
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow STATUS", "")
case "VERSION":
if err := versionInfo.Encode(t.Stdout); err != nil {
return types.NewError(types.ErrIOFailure, err.Error(), "")
Expand Down Expand Up @@ -342,10 +366,11 @@ func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versio
// CNIFuncs contains a group of callback command funcs to be passed in as
// parameters to the core "main" for a plugin.
type CNIFuncs struct {
Add func(_ *CmdArgs) error
Del func(_ *CmdArgs) error
Check func(_ *CmdArgs) error
GC func(_ *CmdArgs) error
Add func(_ *CmdArgs) error
Del func(_ *CmdArgs) error
Check func(_ *CmdArgs) error
GC func(_ *CmdArgs) error
Status func(_ *CmdArgs) error
}

// PluginMainFuncsWithError is the core "main" for a plugin. It accepts
Expand Down
1 change: 1 addition & 0 deletions plugins/test/noop/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Debug struct {
// Report* fields allow the test to control the behavior of the no-op plugin
ReportResult string
ReportError string
ReportErrorCode uint
ReportStderr string
ReportVersionSupport []string
ExitWithCode int
Expand Down
23 changes: 17 additions & 6 deletions plugins/test/noop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ package main

import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -136,7 +135,14 @@ func debugBehavior(args *skel.CmdArgs, command string) error {
}
switch {
case debug.ReportError != "":
return errors.New(debug.ReportError)
ec := debug.ReportErrorCode
if ec == 0 {
ec = types.ErrInternal
}
return &types.Error{
Msg: debug.ReportError,
Code: ec,
}
case debug.ReportResult == "PASSTHROUGH" || debug.ReportResult == "INJECT-DNS":
prevResult := netConf.PrevResult
if debug.ReportResult == "INJECT-DNS" {
Expand Down Expand Up @@ -212,6 +218,10 @@ func cmdGC(args *skel.CmdArgs) error {
return debugBehavior(args, "GC")
}

func cmdStatus(args *skel.CmdArgs) error {
return debugBehavior(args, "STATUS")
}

func saveStdin() ([]byte, error) {
// Read original stdin
stdinData, err := io.ReadAll(os.Stdin)
Expand Down Expand Up @@ -244,9 +254,10 @@ func main() {

supportedVersions := debugGetSupportedVersions(stdinData)
skel.PluginMainFuncs(skel.CNIFuncs{
Add: cmdAdd,
Check: cmdCheck,
Del: cmdDel,
GC: cmdGC,
Add: cmdAdd,
Check: cmdCheck,
Del: cmdDel,
GC: cmdGC,
Status: cmdStatus,
}, version.PluginSupports(supportedVersions...), "CNI noop plugin v0.7.0")
}

0 comments on commit ea6bad2

Please sign in to comment.