Merge pull request #267 from rosenhouse/version-cmd

Adds VERSION command
This commit is contained in:
Tom Denham 2016-08-12 15:06:22 -07:00 committed by GitHub
commit 349d66d51c
5 changed files with 107 additions and 20 deletions

View File

@ -22,9 +22,7 @@ Other sysctls can be modified as long as they belong to the network namespace (`
A successful result would simply be: A successful result would simply be:
``` ```
{ { }
"cniVersion": "0.1.0"
}
``` ```
## Network sysctls documentation ## Network sysctls documentation

18
SPEC.md
View File

@ -62,10 +62,14 @@ The operations that the CNI plugin needs to support are:
- **Extra arguments**, as defined above. - **Extra arguments**, as defined above.
- **Name of the interface inside the container**, as defined above. - **Name of the interface inside the container**, as defined above.
- Report version
- Parameters: NONE.
- Result:
- The version of the CNI spec implemented by the plugin: `{ "cniVersion": "0.2.0" }`
The executable command-line API uses the type of network (see [Network Configuration](#network-configuration) below) as the name of the executable to invoke. The executable command-line API uses the type of network (see [Network Configuration](#network-configuration) below) as the name of the executable to invoke.
It will then look for this executable in a list of predefined directories. Once found, it will invoke the executable using the following environment variables for argument passing: It will then look for this executable in a list of predefined directories. Once found, it will invoke the executable using the following environment variables for argument passing:
- `CNI_VERSION`: [Semantic Version 2.0](http://semver.org) of CNI specification. This effectively versions the CNI_XXX environment variables. - `CNI_COMMAND`: indicates the desired operation; `ADD`, `DEL` or `VERSION`.
- `CNI_COMMAND`: indicates the desired operation; either `ADD` or `DEL`
- `CNI_CONTAINERID`: Container ID - `CNI_CONTAINERID`: Container ID
- `CNI_NETNS`: Path to network namespace file - `CNI_NETNS`: Path to network namespace file
- `CNI_IFNAME`: Interface name to set up - `CNI_IFNAME`: Interface name to set up
@ -81,7 +85,7 @@ Success is indicated by a return code of zero and the following JSON printed to
``` ```
{ {
"cniVersion": "0.1.0", "cniVersion": "0.2.0",
"ip4": { "ip4": {
"ip": <ipv4-and-subnet-in-CIDR>, "ip": <ipv4-and-subnet-in-CIDR>,
"gateway": <ipv4-of-the-gateway>, (optional) "gateway": <ipv4-of-the-gateway>, (optional)
@ -110,7 +114,7 @@ Examples include generating an `/etc/resolv.conf` file to be injected into the c
Errors are indicated by a non-zero return code and the following JSON being printed to stdout: Errors are indicated by a non-zero return code and the following JSON being printed to stdout:
``` ```
{ {
"cniVersion": "0.1.0", "cniVersion": "0.2.0",
"code": <numeric-error-code>, "code": <numeric-error-code>,
"msg": <short-error-message>, "msg": <short-error-message>,
"details": <long-error-message> (optional) "details": <long-error-message> (optional)
@ -147,7 +151,7 @@ Plugins may define additional fields that they accept and may generate an error
```json ```json
{ {
"cniVersion": "0.1.0", "cniVersion": "0.2.0",
"name": "dbnet", "name": "dbnet",
"type": "bridge", "type": "bridge",
// type (plugin) specific // type (plugin) specific
@ -166,7 +170,7 @@ Plugins may define additional fields that they accept and may generate an error
```json ```json
{ {
"cniVersion": "0.1.0", "cniVersion": "0.2.0",
"name": "pci", "name": "pci",
"type": "ovs", "type": "ovs",
// type (plugin) specific // type (plugin) specific
@ -216,7 +220,7 @@ Success is indicated by a zero return code and the following JSON being printed
``` ```
{ {
"cniVersion": "0.1.0", "cniVersion": "0.2.0",
"ip4": { "ip4": {
"ip": <ipv4-and-subnet-in-CIDR>, "ip": <ipv4-and-subnet-in-CIDR>,
"gateway": <ipv4-of-the-gateway>, (optional) "gateway": <ipv4-of-the-gateway>, (optional)

View File

@ -24,6 +24,7 @@ import (
"os" "os"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
) )
// CmdArgs captures all the arguments passed in to the plugin // CmdArgs captures all the arguments passed in to the plugin
@ -40,7 +41,9 @@ type CmdArgs struct {
type dispatcher struct { type dispatcher struct {
Getenv func(string) string Getenv func(string) string
Stdin io.Reader Stdin io.Reader
Stdout io.Writer
Stderr io.Writer Stderr io.Writer
Versioner version.PluginVersioner
} }
type reqForCmdEntry map[string]bool type reqForCmdEntry map[string]bool
@ -154,6 +157,9 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) *types.Er
case "DEL": case "DEL":
err = cmdDel(cmdArgs) err = cmdDel(cmdArgs)
case "VERSION":
err = t.Versioner.Encode(t.Stdout)
default: default:
return createTypedError("unknown CNI_COMMAND: %v", cmd) return createTypedError("unknown CNI_COMMAND: %v", cmd)
} }
@ -174,7 +180,9 @@ func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) {
caller := dispatcher{ caller := dispatcher{
Getenv: os.Getenv, Getenv: os.Getenv,
Stdin: os.Stdin, Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
Versioner: version.DefaultPluginVersioner,
} }
err := caller.pluginMain(cmdAdd, cmdDel) err := caller.pluginMain(cmdAdd, cmdDel)

View File

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -48,7 +49,7 @@ var _ = Describe("dispatching to the correct callback", func() {
var ( var (
environment map[string]string environment map[string]string
stdin io.Reader stdin io.Reader
stderr *bytes.Buffer stdout, stderr *bytes.Buffer
cmdAdd, cmdDel *fakeCmd cmdAdd, cmdDel *fakeCmd
dispatch *dispatcher dispatch *dispatcher
expectedCmdArgs *CmdArgs expectedCmdArgs *CmdArgs
@ -64,11 +65,15 @@ var _ = Describe("dispatching to the correct callback", func() {
"CNI_PATH": "/some/cni/path", "CNI_PATH": "/some/cni/path",
} }
stdin = strings.NewReader(`{ "some": "config" }`) stdin = strings.NewReader(`{ "some": "config" }`)
stdout = &bytes.Buffer{}
stderr = &bytes.Buffer{} stderr = &bytes.Buffer{}
versioner := &version.BasicVersioner{CNIVersion: "9.8.7"}
dispatch = &dispatcher{ dispatch = &dispatcher{
Getenv: func(key string) string { return environment[key] }, Getenv: func(key string) string { return environment[key] },
Stdin: stdin, Stdin: stdin,
Stdout: stdout,
Stderr: stderr, Stderr: stderr,
Versioner: versioner,
} }
cmdAdd = &fakeCmd{} cmdAdd = &fakeCmd{}
cmdDel = &fakeCmd{} cmdDel = &fakeCmd{}
@ -171,6 +176,36 @@ var _ = Describe("dispatching to the correct callback", func() {
) )
}) })
Context("when the CNI_COMMAND is VERSION", func() {
BeforeEach(func() {
environment["CNI_COMMAND"] = "VERSION"
})
It("prints the version to stdout", func() {
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
Expect(err).NotTo(HaveOccurred())
Expect(stdout).To(MatchJSON(`{ "cniVersion": "9.8.7" }`))
})
It("does not call cmdAdd or cmdDel", func() {
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func)
Expect(err).NotTo(HaveOccurred())
Expect(cmdAdd.CallCount).To(Equal(0))
Expect(cmdDel.CallCount).To(Equal(0))
})
DescribeTable("VERSION does not need the usual env vars", envVarChecker,
Entry("command", "CNI_COMMAND", true),
Entry("container id", "CNI_CONTAINER_ID", false),
Entry("net ns", "CNI_NETNS", false),
Entry("if name", "CNI_IFNAME", false),
Entry("args", "CNI_ARGS", false),
Entry("path", "CNI_PATH", false),
)
})
Context("when the CNI_COMMAND is unrecognized", func() { Context("when the CNI_COMMAND is unrecognized", func() {
BeforeEach(func() { BeforeEach(func() {
environment["CNI_COMMAND"] = "NOPE" environment["CNI_COMMAND"] = "NOPE"

42
pkg/version/version.go Normal file
View File

@ -0,0 +1,42 @@
// 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.
package version
import (
"encoding/json"
"io"
)
// A PluginVersioner can encode information about its version
type PluginVersioner interface {
Encode(io.Writer) error
}
// BasicVersioner is a PluginVersioner which reports a single cniVersion string
type BasicVersioner struct {
CNIVersion string `json:"cniVersion"`
}
func (p *BasicVersioner) Encode(w io.Writer) error {
return json.NewEncoder(w).Encode(p)
}
// Current reports the version of the CNI spec implemented by this library
func Current() string {
return "0.2.0"
}
// DefaultPluginVersioner reports the Current library spec version as the cniVersion
var DefaultPluginVersioner = &BasicVersioner{CNIVersion: Current()}