docs: Edits to v0.3.0 upgrade guidance

This commit is contained in:
Gabe Rosenhouse 2017-02-26 16:53:52 -08:00
parent f658c0b4b3
commit 5b6d955695

View File

@ -1,101 +1,228 @@
# CNI Plugin Versions, Compatibility, and Writing Plugins
# How to upgrade to CNI Specification v0.3.0
## Specification Changes between CNI Specification Versions 0.2.0 and
0.3.0
Version 0.3.0 of the [CNI Specification](../SPEC.md) provides rich information
about container network configuration, including details of network interfaces
and support for multiple IP addresses.
Plugins which conform to the CNI 0.3.0 specification may elect to
return more expressive IPAM JSON that is not compatible with earlier
specification versions. This requires updates to plugins that wish to
conform to the 0.3.0 specification. It **does not** require updates to
plugins that wish to only implement the 0.2.0 and earlier
specifications.
To support this new data, the specification changed in a couple significant
ways that will impact CNI users, plugin authors, and runtime authors.
## Multi-version Compatible Plugins
This document provides guidance for how to upgrade:
Plugins can also choose to support multiple CNI versions. The plugin is
expected to return IPAM output conforming to the CNI specification
version passed in the plugin's configuration on stdin in the
`cniVersion` key. If that key is not present, then CNI specification
version 0.2.0 should be assumed and 0.2.0 conforming JSON returned.
Plugins should return all CNI specification versions they are
compatible with when given the `VERSION` command, so runtimes can
determine each plugins capabilities.
- [For CNI Users](#for-cni-users)
- [For Plugin Authors](#for-plugin-authors)
- [For Runtime Authors](#for-runtime-authors)
For example, if the plugin advertises only CNI specification version
0.2.0 support and receives a `cniVersion` key of "0.3.0" the plugin
must return an error. If the plugin advertises CNI specification
support for 0.2.0 and 0.3.0, and receives `cniVersion` key of "0.2.0",
the plugin must return IPAM JSON conforming to the CNI specification
version 0.2.0.
**Note**: the CNI Spec is versioned independently from the GitHub releases
for this repo. For example, Release v0.4.0 supports Spec version v0.2.0,
and Release v0.5.0 supports Spec v0.3.0.
## libcni API and Go Types Changes in 0.3.0
----
With the 0.5.0 CNI reference plugins and libcni release a number of
important changes were made that affect both runtimes and plugin
authors. These changes make it easier to enhance the libcni and plugin
interfaces going forward. The largest changes are to the `types`
package, which now contains multiple versions of libcni types. The
`current` package contains the latest types, while types compatible
with previous libcni releases will be placed into versioned packages
like `types020` (representing the libcni types that conform to the CNI
specification version 0.2.0).
## For CNI Users
If you maintain CNI configuration files for a container runtime that uses CNI,
ensure that the configuration files specify a `cniVersion` field and that the
version there is supported by your container runtime and CNI plugins. The CNI
spec includes example configuration files for [single plugins](https://github.com/containernetworking/cni/blob/master/SPEC.md#example-configurations)
and for [lists of chained plugins](https://github.com/containernetworking/cni/blob/master/SPEC.md#example-configurations).
The `Result` type is now an interface instead of a plain struct. The
interface has new functions to convert the Result structure between
different versions of the libcni types. Plugins and consumers of libcni
should generally be written to use the Go types of the latest libcni
release, and convert between the latest libcni types and the CNI
specification type they are passed in the `cniVersion` key of plugin
configuration.
Consult the documentation for your runtime and plugins to determine what
CNI spec versions they support.
### Plugins
## For Plugin Authors
This section provides guidance for upgrading plugins to CNI Spec Version 0.3.0.
For example, say your plugins supports both CNI specification version
0.2.0 and 0.3.0. Your plugin receives configuration with a `cniVersion`
key of "0.2.0". This means your plugin should return an IPAM structure
conforming to the CNI specification version 0.2.0. The easiest way to
code your plugin is to always use the most current libcni Result type
(from the `current` package) and then immediately before exiting,
convert that result to the requested CNI specification version result
(eg, `types020`) and print it to stdout.
### General guidance for all plugins (language agnostic)
To provide the smoothest upgrade path, **existing plugins should support
multiple versions of the CNI spec**. In particular, plugins with existing
installed bases should add support for CNI spec version 0.3.0 while maintaining
compatibility with older versions.
```
import "github.com/containernetworking/cni/pkg/types"
import "github.com/containernetworking/cni/pkg/types/current"
To do this, two changes are required. First, a plugin should advertise which
CNI spec versions it supports. It does this by responding to the `VERSION`
command with the following JSON data:
result := &current.Result{}
<<< populate result here >>>
return types.PrintResult(result, << CNI version from stdin net
config>>)
```json
{
"cniVersion": "0.3.0",
"supportedVersions": [ "0.1.0", "0.2.0", "0.3.0" ]
}
```
or if your plugin internally runs IPAM and needs to process the result:
Second, for the `ADD` command, a plugin must respect the `cniVersion` field
provided in the [network configuration JSON](https://github.com/containernetworking/cni/blob/master/SPEC.md#network-configuration).
That field is a request for the plugin to return results of a particular format:
- If the `cniVersion` field is not present, then spec v0.2.0 should be assumed
and v0.2.0 format JSON returned.
- If the plugin doesn't support the version, the plugin must error.
- Otherwise, the plugin must return a [CNI Result](https://github.com/containernetworking/cni/blob/master/SPEC.md#result)
in the format requested.
Result formats for older CNI spec versions are available in the
[git history for SPEC.md](https://github.com/containernetworking/cni/commits/master/SPEC.md).
For example, suppose a plugin, via its `VERSION` response, advertises CNI specification
support for v0.2.0 and v0.3.0. When it receives `cniVersion` key of `0.2.0`,
the plugin must return result JSON conforming to CNI spec version 0.2.0.
### Specific guidance for plugins written in Go
Plugins written in Go may leverage the Go language packages in this repository
to ease the process of upgrading and supporting multiple versions. CNI
[Library and Plugins Release v0.5.0](https://github.com/containernetworking/cni/releases)
includes important changes to the Golang APIs. Plugins using these APIs will
require some changes now, but should more-easily handle spec changes and
new features going forward.
For plugin authors, the biggest change is that `types.Result` is now an
interface implemented by concrete struct types in the `types/current` and
`types/020` subpackages.
Internally, plugins should use the `types/current` structs, and convert
to or from specific versions when required. In most plugins, the only
conversion required will at completion time, when it prints the result as
JSON to stdout. At that point, the result should be printed in the format
requested by the `cniVersion` field present in the network configuration.
Use the library function `types.PrintResult()` for that.
Additionally, the plugin should advertise which CNI Spec versions it supports
via the 3rd argument to `skel.PluginMain()`.
Here is some example code
```go
import (
"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"
)
func cmdAdd(args *skel.CmdArgs) error {
// determine spec version to use
var netConf struct {
types.NetConf
// other plugin-specific configuration goes here
}
err := json.Unmarshal(args.StdinData, &netConf)
cniVersion := netConf.CNIVersion
// plugin does its work...
// set up interfaces
// assign addresses, etc
// construct the result
result := &current.Result{
Interfaces: []*current.Interface{ ... },
IPs: []*current.IPs{ ... },
...
}
// print result to stdout, in the format defined by the requested cniVersion
return types.PrintResult(result, cniVersion)
}
func main() {
skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports("0.1.0", "0.2.0", "0.3.0"))
}
```
import "github.com/containernetworking/cni/pkg/types"
import "github.com/containernetworking/cni/pkg/types/current"
ipamResult, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
Alternately, to use the result from a delegated IPAM plugin, the `result`
value might be formed like this:
```go
ipamResult, err := ipam.ExecAdd(netConf.IPAM.Type, args.StdinData)
result, err := current.NewResultFromResult(ipamResult)
<<< manipulate result here >>>
return types.PrintResult(result, << CNI version from stdin net
config>>)
```
### Runtimes
Other examples of spec v0.3.0-compatible plugins are the
[main plugins in this repo](https://github.com/containernetworking/cni/tree/master/plugins/main)
Since libcni functions like AddNetwork() now return a Result interface
rather than a plain structure, only a single additional step is
required to convert that object to a structure you can manipulate
internally. Runtimes should code to the most current Go types provided
by the libcni they vendor into their sources, and can convert from
whatever IPAM result version the plugin provides to the most current
version like so:
## For Runtime Authors
This section provides guidance for upgrading container runtimes to support
CNI Spec Version 0.3.0.
### General guidance for all runtimes
#### Support multiple CNI spec versions
To provide the smoothest upgrade path and support the broadest range of CNI
plugins, **container runtimes should support multiple versions of the CNI spec**.
In particular, runtimes with existing installed bases should add support for CNI
spec version 0.3.0 while maintaining compatibility with older versions.
To support multiple versions of the CNI spec, runtimes should be able to
call both new and legacy plugins, and handle the results from either.
When calling a plugin, the runtime must request that the plugin respond in a
particular format by specifying the `cniVersion` field in the
[Network Configuration](https://github.com/containernetworking/cni/blob/master/SPEC.md#network-configuration)
JSON block. The plugin will then respond with
a [Result](https://github.com/containernetworking/cni/blob/master/SPEC.md#result)
in the format defined by that CNI spec version, and the runtime must parse
and handle this result.
#### Handle errors due to version incompatibility
Plugins may respond with error indicating that they don't support the requested
CNI version (see [Well-known Error Codes](https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes)),
e.g.
```json
{
"cniVersion": "0.2.0",
"code": 1,
"msg": "CNI version not supported"
}
```
resultInterface, err := cninet.AddNetwork(netconf, rt)
<<< resultInterface could wrap an object of any CNI specification
version >>>
realResult := current.NewResultFromResult(resultInterface)
<<< realResult is a struct, not an interface >>>
In that case, the runtime may retry with a lower CNI spec version, or take
some other action.
#### (optional) Discover plugin version support
Runtimes may discover which CNI spec versions are supported by a plugin, by
calling the plugin with the `VERSION` command. The `VERSION` command was
added in CNI spec v0.2.0, so older plugins may not respect it. In the absence
of a successful response to `VERSION`, assume that the plugin only supports
CNI spec v0.1.0.
#### Handle missing data in v0.3.0 results
The Result for the `ADD` command in CNI spec version 0.3.0 includes a new field
`interfaces`. An IP address in the `ip` field may describe which interface
it is assigned to, by placing a numeric index in the `interface` subfield.
However, some plugins which are v0.3.0 compatible may nonetheless omit the
`interfaces` field and/or set the `interface` index value to `-1`. Runtimes
should gracefully handle this situation, unless they have good reason to rely
on the existence of the interface data. In that case, provide the user an
error message that helps diagnose the issue.
### Specific guidance for container runtimes written in Go
Container runtimes written in Go may leverage the Go language packages in this
repository to ease the process of upgrading and supporting multiple versions.
CNI [Library and Plugins Release v0.5.0](https://github.com/containernetworking/cni/releases)
includes important changes to the Golang APIs. Runtimes using these APIs will
require some changes now, but should more-easily handle spec changes and
new features going forward.
For runtimes, the biggest changes to the Go libraries are in the `types` package.
The top-level `types.Result` is now an opaque interface instead of a struct, and
APIs exposed by other packages, such as the high-level `libcni` package, have
been updated to use this interface. Concrete types are available as subpackages.
The `types/current` subpackage contains the latest (spec v0.3.0) types.
A container runtime should use `current.NewResultFromResult()` to convert the
opaque `types.Result` to a concrete `current.Result` struct. It may then
work with the fields exposed by that struct:
```go
// runtime invokes the plugin to get the opaque types.Result
// this may conform to any CNI spec version
resultInterface, err := libcni.AddNetwork(netConf, runtimeConf)
// upconvert result to the current 0.3.0 spec
result, err := current.NewResultFromResult(resultInterface)
// use the result fields ....
for _, ip := range result.IPs { ... }
```