--- /dev/null
+// Copyright 2017 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.
+
+// This is a sample chained plugin that supports multiple CNI versions. It
+// parses prevResult according to the cniVersion
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+
+ "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"
+)
+
+// PluginConf is whatever you expect your configuration json to be. This is whatever
+// is passed in on stdin. Your plugin may wish to expose its functionality via
+// runtime args, see CONVENTIONS.md in the CNI spec.
+type PluginConf struct {
+ types.NetConf // You may wish to not nest this type
+ RuntimeConfig *struct {
+ SampleConfig map[string]interface{} `json:"sample"`
+ } `json:"runtimeConfig"`
+
+ // This is the previous result, when called in the context of a chained
+ // plugin. Because this plugin supports multiple versions, we'll have to
+ // parse this in two passes. If your plugin is not chained, this can be
+ // removed (though you may wish to error if a non-chainable plugin is
+ // chained.
+ // If you need to modify the result before returning it, you will need
+ // to actually convert it to a concrete versioned struct.
+ RawPrevResult *map[string]interface{} `json:"prevResult"`
+ PrevResult *current.Result `json:"-"`
+
+ // Add plugin-specifc flags here
+ MyAwesomeFlag bool `json:"myAwesomeFlag"`
+ AnotherAwesomeArg string `json:"anotherAwesomeArg"`
+}
+
+// parseConfig parses the supplied configuration (and prevResult) from stdin.
+func parseConfig(stdin []byte) (*PluginConf, error) {
+ conf := PluginConf{}
+
+ if err := json.Unmarshal(stdin, &conf); err != nil {
+ return nil, fmt.Errorf("failed to parse network configuration: %v", err)
+ }
+
+ // Parse previous result. Remove this if your plugin is not chained.
+ if conf.RawPrevResult != nil {
+ resultBytes, err := json.Marshal(conf.RawPrevResult)
+ if err != nil {
+ return nil, fmt.Errorf("could not serialize prevResult: %v", err)
+ }
+ res, err := version.NewResult(conf.CNIVersion, resultBytes)
+ if err != nil {
+ return nil, fmt.Errorf("could not parse prevResult: %v", err)
+ }
+ conf.RawPrevResult = nil
+ conf.PrevResult, err = current.NewResultFromResult(res)
+ if err != nil {
+ return nil, fmt.Errorf("could not convert result to current version: %v", err)
+ }
+ }
+ // End previous result parsing
+
+ // Do any validation here
+ if conf.AnotherAwesomeArg == "" {
+ return nil, fmt.Errorf("anotherAwesomeArg must be specified")
+ }
+
+ return &conf, nil
+}
+
+// cmdAdd is called for ADD requests
+func cmdAdd(args *skel.CmdArgs) error {
+ conf, err := parseConfig(args.StdinData)
+ if err != nil {
+ return err
+ }
+
+ if conf.PrevResult == nil {
+ return fmt.Errorf("must be called as chained plugin")
+ }
+
+ // This is some sample code to generate the list of container-side IPs.
+ // We're casting the prevResult to a 0.3.0 response, which can also include
+ // host-side IPs (but doesn't when converted from a 0.2.0 response).
+ containerIPs := make([]net.IP, 0, len(conf.PrevResult.IPs))
+ if conf.CNIVersion != "0.3.0" {
+ for _, ip := range conf.PrevResult.IPs {
+ containerIPs = append(containerIPs, ip.Address.IP)
+ }
+ } else {
+ for _, ip := range conf.PrevResult.IPs {
+ intIdx := ip.Interface
+ // Every IP is indexed in to the interfaces array, with "-1" standing
+ // for an unknown interface (which we'll assume to be Container-side
+ // Skip all IPs we know belong to an interface with the wrong name.
+ if intIdx >= 0 && intIdx < len(conf.PrevResult.Interfaces) && conf.PrevResult.Interfaces[intIdx].Name != args.IfName {
+ continue
+ }
+ containerIPs = append(containerIPs, ip.Address.IP)
+ }
+ }
+
+ // Pass through the result for the next plugin
+ return types.PrintResult(conf.PrevResult, conf.CNIVersion)
+}
+
+// cmdDel is called for DELETE requests
+func cmdDel(args *skel.CmdArgs) error {
+ conf, err := parseConfig(args.StdinData)
+ if err != nil {
+ return err
+ }
+ _ = conf
+
+ // Do your delete here
+
+ return nil
+}
+
+func main() {
+ skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports("", "0.1.0", "0.2.0", version.Current()))
+}