Dan Williams 0a1b48f520 noop: allow specifying debug file in config JSON
Chaining sends different config JSON to each plugin, but the same
environment, and if we want to test multiple noop plugin runs in
the same chain we need a way of telling each run to use a different
debug file.
2017-01-19 09:56:42 -06:00

191 lines
4.3 KiB
Go

// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Noop plugin is a CNI plugin designed for use as a test-double.
When calling, set the CNI_ARGS env var equal to the path of a file containing
the JSON encoding of a Debug.
*/
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
)
type NetConf struct {
types.NetConf
DebugFile string `json:"debugFile"`
}
func loadConf(bytes []byte) (*NetConf, error) {
n := &NetConf{}
if err := json.Unmarshal(bytes, n); err != nil {
return nil, fmt.Errorf("failed to load netconf: %v", err)
}
return n, nil
}
// parse extra args i.e. FOO=BAR;ABC=123
func parseExtraArgs(args string) (map[string]string, error) {
m := make(map[string]string)
if len(args) == 0 {
return m, nil
}
items := strings.Split(args, ";")
for _, item := range items {
kv := strings.Split(item, "=")
if len(kv) != 2 {
return nil, fmt.Errorf("CNI_ARGS invalid key/value pair: %s\n", kv)
}
m[kv[0]] = kv[1]
}
return m, nil
}
func getDebugFilePath(stdinData []byte, args string) (string, error) {
netConf, err := loadConf(stdinData)
if err != nil {
return "", err
}
extraArgs, err := parseExtraArgs(args)
if err != nil {
return "", err
}
debugFilePath, ok := extraArgs["DEBUG"]
if !ok {
debugFilePath = netConf.DebugFile
}
return debugFilePath, nil
}
func debugBehavior(args *skel.CmdArgs, command string) error {
debugFilePath, err := getDebugFilePath(args.StdinData, args.Args)
if err != nil {
return err
}
if debugFilePath == "" {
fmt.Printf(`{}`)
os.Stderr.WriteString("CNI_ARGS or config empty, no debug behavior\n")
return nil
}
debug, err := noop_debug.ReadDebug(debugFilePath)
if err != nil {
return err
}
debug.CmdArgs = *args
debug.Command = command
if debug.ReportResult == "" {
debug.ReportResult = fmt.Sprintf(` { "result": %q }`, noop_debug.EmptyReportResultMessage)
}
err = debug.WriteDebug(debugFilePath)
if err != nil {
return err
}
os.Stderr.WriteString(debug.ReportStderr)
if debug.ReportError != "" {
return errors.New(debug.ReportError)
} else {
os.Stdout.WriteString(debug.ReportResult)
}
return nil
}
func debugGetSupportedVersions(stdinData []byte) []string {
vers := []string{"0.-42.0", "0.1.0", "0.2.0"}
cniArgs := os.Getenv("CNI_ARGS")
if cniArgs == "" {
return vers
}
debugFilePath, err := getDebugFilePath(stdinData, cniArgs)
if err != nil {
panic("test setup error: unable to get debug file path: " + err.Error())
}
debug, err := noop_debug.ReadDebug(debugFilePath)
if err != nil {
panic("test setup error: unable to read debug file: " + err.Error())
}
if debug.ReportVersionSupport == nil {
return vers
}
return debug.ReportVersionSupport
}
func cmdAdd(args *skel.CmdArgs) error {
return debugBehavior(args, "ADD")
}
func cmdDel(args *skel.CmdArgs) error {
return debugBehavior(args, "DEL")
}
func saveStdin() ([]byte, error) {
// Read original stdin
stdinData, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return nil, err
}
// Make a new pipe for stdin, and write original stdin data to it
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
if _, err := w.Write(stdinData); err != nil {
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
os.Stdin = r
return stdinData, nil
}
func main() {
// Grab and read stdin before pkg/skel gets it
stdinData, err := saveStdin()
if err != nil {
panic("test setup error: unable to read stdin: " + err.Error())
}
supportedVersions := debugGetSupportedVersions(stdinData)
skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports(supportedVersions...))
}