github.com/mccv1r0/cni@v0.7.0-alpha1/plugins/test/noop/main.go (about)

     1  // Copyright 2016 CNI authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  /*
    16  Noop plugin is a CNI plugin designed for use as a test-double.
    17  
    18  When calling, set the CNI_ARGS env var equal to the path of a file containing
    19  the JSON encoding of a Debug.
    20  */
    21  
    22  package main
    23  
    24  import (
    25  	"encoding/json"
    26  	"errors"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"os"
    30  	"strings"
    31  
    32  	"github.com/containernetworking/cni/pkg/skel"
    33  	"github.com/containernetworking/cni/pkg/types"
    34  	"github.com/containernetworking/cni/pkg/types/current"
    35  	"github.com/containernetworking/cni/pkg/version"
    36  	noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
    37  )
    38  
    39  type NetConf struct {
    40  	types.NetConf
    41  	DebugFile  string          `json:"debugFile"`
    42  	PrevResult *current.Result `json:"prevResult,omitempty"`
    43  }
    44  
    45  func loadConf(bytes []byte) (*NetConf, error) {
    46  	n := &NetConf{}
    47  	if err := json.Unmarshal(bytes, n); err != nil {
    48  		return nil, fmt.Errorf("failed to load netconf: %v %q", err, string(bytes))
    49  	}
    50  	return n, nil
    51  }
    52  
    53  // parse extra args i.e. FOO=BAR;ABC=123
    54  func parseExtraArgs(args string) (map[string]string, error) {
    55  	m := make(map[string]string)
    56  	if len(args) == 0 {
    57  		return m, nil
    58  	}
    59  
    60  	items := strings.Split(args, ";")
    61  	for _, item := range items {
    62  		kv := strings.Split(item, "=")
    63  		if len(kv) != 2 {
    64  			return nil, fmt.Errorf("CNI_ARGS invalid key/value pair: %s\n", kv)
    65  		}
    66  		m[kv[0]] = kv[1]
    67  	}
    68  	return m, nil
    69  }
    70  
    71  func getConfig(stdinData []byte, args string) (string, *NetConf, error) {
    72  	netConf, err := loadConf(stdinData)
    73  	if err != nil {
    74  		return "", nil, err
    75  	}
    76  
    77  	extraArgs, err := parseExtraArgs(args)
    78  	if err != nil {
    79  		return "", nil, err
    80  	}
    81  
    82  	debugFilePath, ok := extraArgs["DEBUG"]
    83  	if !ok {
    84  		debugFilePath = netConf.DebugFile
    85  	}
    86  
    87  	return debugFilePath, netConf, nil
    88  }
    89  
    90  func debugBehavior(args *skel.CmdArgs, command string) error {
    91  	debugFilePath, netConf, err := getConfig(args.StdinData, args.Args)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	if debugFilePath == "" {
    97  		fmt.Printf(`{}`)
    98  		os.Stderr.WriteString("CNI_ARGS or config empty, no debug behavior\n")
    99  		return nil
   100  	}
   101  
   102  	debug, err := noop_debug.ReadDebug(debugFilePath)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	debug.CmdArgs = *args
   108  	debug.Command = command
   109  
   110  	if debug.ReportResult == "" {
   111  		debug.ReportResult = fmt.Sprintf(` { "result": %q }`, noop_debug.EmptyReportResultMessage)
   112  	}
   113  
   114  	err = debug.WriteDebug(debugFilePath)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	os.Stderr.WriteString(debug.ReportStderr)
   120  
   121  	if debug.ReportError != "" {
   122  		return errors.New(debug.ReportError)
   123  	} else if debug.ReportResult == "PASSTHROUGH" || debug.ReportResult == "INJECT-DNS" {
   124  		prevResult := netConf.PrevResult
   125  		if debug.ReportResult == "INJECT-DNS" {
   126  			prevResult, err = current.NewResultFromResult(netConf.PrevResult)
   127  			if err != nil {
   128  				return err
   129  			}
   130  			prevResult.DNS.Nameservers = []string{"1.2.3.4"}
   131  		}
   132  
   133  		// Must print the prevResult as the CNIVersion of the config
   134  		newResult, err := prevResult.GetAsVersion(netConf.CNIVersion)
   135  		if err != nil {
   136  			return fmt.Errorf("failed to convert result to config %q: %v", netConf.CNIVersion, err)
   137  		}
   138  		resultBytes, err := json.Marshal(newResult)
   139  		if err != nil {
   140  			return fmt.Errorf("failed to marshal new result: %v", err)
   141  		}
   142  		os.Stdout.WriteString(string(resultBytes))
   143  	} else {
   144  		os.Stdout.WriteString(debug.ReportResult)
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func debugGetSupportedVersions(stdinData []byte) []string {
   151  	vers := []string{"0.-42.0", "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0"}
   152  	cniArgs := os.Getenv("CNI_ARGS")
   153  	if cniArgs == "" {
   154  		return vers
   155  	}
   156  
   157  	debugFilePath, _, err := getConfig(stdinData, cniArgs)
   158  	if err != nil {
   159  		panic("test setup error: unable to get debug file path: " + err.Error())
   160  	}
   161  
   162  	debug, err := noop_debug.ReadDebug(debugFilePath)
   163  	if err != nil {
   164  		panic("test setup error: unable to read debug file: " + err.Error())
   165  	}
   166  	if debug.ReportVersionSupport == nil {
   167  		return vers
   168  	}
   169  	return debug.ReportVersionSupport
   170  }
   171  
   172  func cmdAdd(args *skel.CmdArgs) error {
   173  	return debugBehavior(args, "ADD")
   174  }
   175  
   176  func cmdGet(args *skel.CmdArgs) error {
   177  	return debugBehavior(args, "GET")
   178  }
   179  
   180  func cmdDel(args *skel.CmdArgs) error {
   181  	return debugBehavior(args, "DEL")
   182  }
   183  
   184  func saveStdin() ([]byte, error) {
   185  	// Read original stdin
   186  	stdinData, err := ioutil.ReadAll(os.Stdin)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	// Make a new pipe for stdin, and write original stdin data to it
   192  	r, w, err := os.Pipe()
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	if _, err := w.Write(stdinData); err != nil {
   197  		return nil, err
   198  	}
   199  	if err := w.Close(); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	os.Stdin = r
   204  	return stdinData, nil
   205  }
   206  
   207  func main() {
   208  	// Grab and read stdin before pkg/skel gets it
   209  	stdinData, err := saveStdin()
   210  	if err != nil {
   211  		panic("test setup error: unable to read stdin: " + err.Error())
   212  	}
   213  
   214  	supportedVersions := debugGetSupportedVersions(stdinData)
   215  	skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.PluginSupports(supportedVersions...), "CNI nnop plugin v0.7.0")
   216  }