github.com/jk-he/cni@v0.8.1/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", 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  		_, err = os.Stderr.WriteString("CNI_ARGS or config empty, no debug behavior\n")
    99  		if err != nil {
   100  			return err
   101  		}
   102  		return nil
   103  	}
   104  
   105  	debug, err := noop_debug.ReadDebug(debugFilePath)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	debug.CmdArgs = *args
   111  	debug.Command = command
   112  
   113  	err = debug.WriteDebug(debugFilePath)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	if debug.ReportStderr != "" {
   119  		if _, err = os.Stderr.WriteString(debug.ReportStderr); err != nil {
   120  			return err
   121  		}
   122  	}
   123  
   124  	if debug.ReportError != "" {
   125  		return errors.New(debug.ReportError)
   126  	} else if debug.ReportResult == "PASSTHROUGH" || debug.ReportResult == "INJECT-DNS" {
   127  		prevResult := netConf.PrevResult
   128  		if debug.ReportResult == "INJECT-DNS" {
   129  			prevResult, err = current.NewResultFromResult(netConf.PrevResult)
   130  			if err != nil {
   131  				return err
   132  			}
   133  			prevResult.DNS.Nameservers = []string{"1.2.3.4"}
   134  		}
   135  
   136  		// Must print the prevResult as the CNIVersion of the config
   137  		newResult, err := prevResult.GetAsVersion(netConf.CNIVersion)
   138  		if err != nil {
   139  			return fmt.Errorf("failed to convert result to config %q: %v", netConf.CNIVersion, err)
   140  		}
   141  		resultBytes, err := json.Marshal(newResult)
   142  		if err != nil {
   143  			return fmt.Errorf("failed to marshal new result: %v", err)
   144  		}
   145  		_, err = os.Stdout.WriteString(string(resultBytes))
   146  		if err != nil {
   147  			return err
   148  		}
   149  	} else if debug.ReportResult != "" {
   150  		_, err = os.Stdout.WriteString(debug.ReportResult)
   151  		if err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	if debug.ExitWithCode > 0 {
   157  		os.Exit(debug.ExitWithCode)
   158  	}
   159  	return nil
   160  }
   161  
   162  func debugGetSupportedVersions(stdinData []byte) []string {
   163  	vers := []string{"0.-42.0", "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0"}
   164  	cniArgs := os.Getenv("CNI_ARGS")
   165  	if cniArgs == "" {
   166  		return vers
   167  	}
   168  
   169  	debugFilePath, _, err := getConfig(stdinData, cniArgs)
   170  	if err != nil {
   171  		panic("test setup error: unable to get debug file path: " + err.Error())
   172  	}
   173  
   174  	debug, err := noop_debug.ReadDebug(debugFilePath)
   175  	if err != nil {
   176  		panic("test setup error: unable to read debug file: " + err.Error())
   177  	}
   178  	if debug.ReportVersionSupport == nil {
   179  		return vers
   180  	}
   181  	return debug.ReportVersionSupport
   182  }
   183  
   184  func cmdAdd(args *skel.CmdArgs) error {
   185  	return debugBehavior(args, "ADD")
   186  }
   187  
   188  func cmdCheck(args *skel.CmdArgs) error {
   189  	return debugBehavior(args, "CHECK")
   190  }
   191  
   192  func cmdDel(args *skel.CmdArgs) error {
   193  	return debugBehavior(args, "DEL")
   194  }
   195  
   196  func saveStdin() ([]byte, error) {
   197  	// Read original stdin
   198  	stdinData, err := ioutil.ReadAll(os.Stdin)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	// Make a new pipe for stdin, and write original stdin data to it
   204  	r, w, err := os.Pipe()
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	if _, err := w.Write(stdinData); err != nil {
   209  		return nil, err
   210  	}
   211  	if err := w.Close(); err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	os.Stdin = r
   216  	return stdinData, nil
   217  }
   218  
   219  func main() {
   220  	// Grab and read stdin before pkg/skel gets it
   221  	stdinData, err := saveStdin()
   222  	if err != nil {
   223  		panic("test setup error: unable to read stdin: " + err.Error())
   224  	}
   225  
   226  	supportedVersions := debugGetSupportedVersions(stdinData)
   227  	skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.PluginSupports(supportedVersions...), "CNI noop plugin v0.7.0")
   228  }