
Updates the spec and plugins to return an array of interfaces and IP details to the runtime including: - interface names and MAC addresses configured by the plugin - whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc) - multiple IP addresses configured by IPAM and which interface they have been assigned to Returning interface details is useful for runtimes, as well as allowing more flexible chaining of CNI plugins themselves. For example, some meta plugins may need to know the host-side interface to be able to apply firewall or traffic shaping rules to the container.
207 lines
5.0 KiB
Go
207 lines
5.0 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/types/current"
|
|
"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"`
|
|
PrevResult *current.Result `json:"prevResult,omitempty"`
|
|
}
|
|
|
|
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 %q", err, string(bytes))
|
|
}
|
|
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 getConfig(stdinData []byte, args string) (string, *NetConf, error) {
|
|
netConf, err := loadConf(stdinData)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
extraArgs, err := parseExtraArgs(args)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
debugFilePath, ok := extraArgs["DEBUG"]
|
|
if !ok {
|
|
debugFilePath = netConf.DebugFile
|
|
}
|
|
|
|
return debugFilePath, netConf, nil
|
|
}
|
|
|
|
func debugBehavior(args *skel.CmdArgs, command string) error {
|
|
debugFilePath, netConf, err := getConfig(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 if debug.ReportResult == "PASSTHROUGH" || debug.ReportResult == "INJECT-DNS" {
|
|
if debug.ReportResult == "INJECT-DNS" {
|
|
newResult, err := current.NewResultFromResult(netConf.PrevResult)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newResult.DNS.Nameservers = []string{"1.2.3.4"}
|
|
netConf.PrevResult = newResult
|
|
}
|
|
newResult, err := json.Marshal(netConf.PrevResult)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal new result: %v", err)
|
|
}
|
|
os.Stdout.WriteString(string(newResult))
|
|
} else {
|
|
os.Stdout.WriteString(debug.ReportResult)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func debugGetSupportedVersions(stdinData []byte) []string {
|
|
vers := []string{"0.-42.0", "0.1.0", "0.2.0", "0.3.0"}
|
|
cniArgs := os.Getenv("CNI_ARGS")
|
|
if cniArgs == "" {
|
|
return vers
|
|
}
|
|
|
|
debugFilePath, _, err := getConfig(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...))
|
|
}
|