I made an (embarassing) error where SPEC.md and libcni disagreed on the
key for valid attachments for a GC operation. The spec has been
updated, but libcni should set both keys as a transition mechanism.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
The SPEC and libcni disagreed on the name to use for still-valid
attachments. Change the spec to be aligned with libcni.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
For plugins that may share resources or resource pools across networks,
we should make it explicitly clear that GC must only clean up resources
known to be owned by the calling network.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
If there's no cache dir, there are no cached attachments to return. No
sense in propagating that error.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
This change make cnitool CNI v1.1.0 support. This also addresses
to fix error crash in case of GC with empty attachments.
Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
Since every plugin needs to deserialize this, it should be in the
standard network configuration struct.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
Now that plugins live in a different repository, we don't need these old
files. We don't actuall build any release binaries.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
This implements the `cniVersions` field in the network configuration
list. It allows for CNI plugins to specify that they support multiple
versions, and the runtime may select the highest version it supports.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
This adds a new top-level configuration field, `cniVersions`, where
network configurations can indicate support for more than one version.
This is to enable easy upgrading of CNI versions in plugins without
lock-step updates to the runtime.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
This adds a new verb, STATUS, which indicates whether or not a plugin is
ready to accept ADD requests. Runtimes may choose to use the STATUS
mechanism to determine if a network is ready.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
As govet and gofmt are now handled by golangci-lint, there is no need to apply them here.
Install golangci-lint
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
GC, or garbage collection, is intended as a way for runtimes to tell
plugins about valid attachments, enabling them to delete any leaked or
stale resources.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
There was a bug in which some versions didn't register their creation
function, thus leaving some types un-decodeable. Fix that by explicitly
importing all known versions.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
This adds support for CNI spec v1.1.0. Since there are no result type
changes expected, this means that we can use the existing v1.0.0 types.
That takes a bit of plumbing to decouple the type from the version.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
Container runtimes can pass pod cgroup path
when requested by CNI plugins.
Details around the use cases for this field are
documented - https://github.com/kubernetes/kubernetes/issues/113342.
Signed-off-by: Aditi Ghag <aditi@cilium.io>
The io/ioutil package has been deprecated as of Go 1.16 [1]. This commit
replaces the existing io/ioutil functions with their new definitions in
io and os packages.
[1]: https://golang.org/doc/go1.16#ioutil
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
Upgrades actions/checkout and actions/setup-go packages to v3 to resolve
CI workflow NodeJS 12 warnings.
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
CNI spec 0.4x (and possibly earlier) mistakenly specified disableCheck
as a string type, while 1.0 cleaned that up to be a real boolean.
We should be nice and interpret the string value.
Fixes: https://github.com/containernetworking/cni/issues/877
Signed-off-by: Dan Williams <dcbw@redhat.com>
Previously, we returned an arbitrary string error if the given CNI
version didn't support CHECK. This is not useful for downstream
consumption and matching.
So, declare an error variable so that users can use `errors.Is()`
instead of matching on a string.
Signed-off-by: Casey Callendrello <c1@caseyc.net>
Properly handle "null" as plugin output. This successfully unmarshals to a nil map, which crashes later when assigning confVersion to the map.
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
The CNI Spec requires plugins to return a CNIVersion in the Result
that is the same as the CNIVersion of the incoming CNI config.
Empty CNIVersions are specified to map to 0.1.0 (since the first
CNI spec didn't have versioning) and libcni handles that automatically.
Unfortunately some versions of Weave don't do that and depend on
a libcni <= 0.8 quirk that used the netconf version if the result
version was empty. This is technically a libcni regression, though
the plugin itself is violating the specification.
Thus for backwards compatibility assume that if the Result
CNIVersion is empty, the netconf CNIVersion should be used as
the Result version.
Fixes: https://github.com/containernetworking/cni/issues/895
Signed-off-by: Dan Williams <dcbw@redhat.com>
fmt.Errorf errors should wrap the original error with %w.
This allows the caller to check the errors with `errors.Is()`.
see: https://blog.golang.org/go1.13-errors
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Turns out a number of users want to read in JSON and
create a Result object from it, and CNI didn't have
a function to do that in one call. Instead you had to
create a ConfigDecoder first, then create a Result
from the given version.
That's silly, let's just make a function to do that
and let everyone do less work.
Signed-off-by: Dan Williams <dcbw@redhat.com>
The spec deviated from the existing behavior, and it was never actually
implemented in libcni. So this is a no-op.
Signed-off-by: Casey Callendrello <cdc@redhat.com>
The 0.2.0 spec strongly suggests via lack of the "(optional)" tag that
a Result must contain one or two IP addresses. We already throw an
error for that case when down-converting a 0.3.0+ Result to <= 0.2.0,
but we didn't have a testcase for it.
Signed-off-by: Dan Williams <dcbw@redhat.com>
The testcase checking if a prevResult was invalid is not actually
useful, because it was testing that an empty prevResult could be
unmarshalled. But due to an oversight, prevResults may not have
a CNIVersion key, which is all we can check for validity.
Signed-off-by: Dan Williams <dcbw@redhat.com>
Moving to spec 1.0.0 requires more complicated conversions, so
put the converter determination logic in a separate module that
any of the types can call to convert between arbitrary versions.
This is necessary to ensure the types don't have import cycles.
This also implements downconversion, which we never claimed
*not* to support, but didn't implement.
It also verifies that the Result object unmarshalled by
each result type's NewResult() function is actually supported
by the result type. This is a potentially breaking change as
this was not previously done, and for example may now fail
attempts to read a cached PrevResult written by a previous
version.
Signed-off-by: Dan Williams <dcbw@redhat.com>
In a prior change, reformatting of the errors codes within the
specification changed their values from positive to negative integers.
Signed-off-by: Caleb Hyde <caleb.hyde@gmail.com>
Now that go has modules, we don't need to do anything particularly
special to build against old cni revisions. Just let `go get` sort it
out for us.
Signed-off-by: Casey Callendrello <cdc@redhat.com>
Now that the CNI slack has been in use for ~7 months it's no longer
necessary to have it as a top-level banner headline. It should be in
CONTRIBUTING.md, though, with the other project contact channels.
Signed-off-by: toby cabot <toby@caboteria.org>
Adding [OVN4NFV-K8S-Plugin](https://github.com/opnfv/ovn4nfv-k8s-plugin) is
OVN based CNI controller plugin to provide cloud-native based Service function
chaining(SFC), Multiple OVN overlay networking, dynamic subnet creation,
dynamic creation of virtual networks, VLAN Provider network,
Direct provider network and pluggable with other Multi-network plugins,
ideal for edge-based cloud-native workloads in Multi-cluster networking
Signed-off-by: r.kuralamudhan <kuralamudhan.ramakrishnan@intel.com>
This makes the plugin validation a bit more robust by retrying the
execution if the plugin is in text file busy state. This can happen if
we write the config and the plugin at once. To avoid such races we now
try up to 5 seconds for the plugin validation.
Signed-off-by: Sascha Grunert <sgrunert@suse.com>
- Remove underscore in the Area description of the entry
- Modify the capability name to be camelCase instead of underscores
to be consistent with other capabilities (e.g `portMappings`, `ipRanges`)
As there was no CNI released and no implementations of this capability
(That i know of) i reckon we can do the attribute name adjustment.
Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
The way raw_exec invokes the command doesn't actually pass back
stderr, despite ExitError having that capability. c.Run()
internally calls c.Wait() but that doesn't capture stderr and
insert it into the returned ExitError. So we have to do that
ourselves.
Fixes: https://github.com/containernetworking/cni/issues/732
Fixes: https://github.com/containernetworking/cni/pull/759
Signed-off-by: Dan Williams <dcbw@redhat.com>
This commit attempts to standardize the consumption
of a device identifier by CNI's for networks that
is associated with a device resource and require its
identifier to perform configurations.
Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
This commit extends CONVENTION.md Well-known Capabilities
to include `infiniband_guid`. this shall be used by CNI plugins
who wish to set Infiniband GUID address for a network interface.
Change-Id: I3716e0a9ea660937a73feee00ea9c5b1a92ab478
Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
This change addresses a few missing steps and out-of-date items in the
contributor docs such as fetching proper dependencies and removing
references to paths that no longer exist.
Fixes#726
Signed-off-by: Ryan Tidwell <rtidwell@suse.com>
We can't generate a cache file path without all of network name,
container ID, and interface name in the RuntimeConf. They should
be there already anyway (otherwise errors will occur) but we should
return those errors earlier.
We should have all components of the uniqueness tuple in the cache file
so that they can be read out of the file without trying to parse the
cache file name.
Cache the config JSON, CNI_ARGs, and CapabilityArgs on ADD operations,
and remove them on DEL. This allows runtimes to retrieve the cached
information to ensure that the pod is given the same config and options
on DEL as it was given on ADD.
Add versioning to beginning of cache file
Combine cached configuration and results into one file
Signed-off-by: Michael Cambria <mcambria@redhat.com>
CacheDir is a property of the entire CNIConfig object and there is no
point to having it be different between container invocations. It was
originally put in RuntimeConfig to make re-vendoring easier so that
users wouldn't have to update calls to InitCNIConfig.
When converting from 0.2.0 to 0.3.x, we used to copy the Gateway defined
under IP4.Gateway to each route that had GW set to nil. This is wrong
because the result of converting from 0.2.0 to 0.3.x and back to 0.2.0
doesn't match the original config.
Also, the spec says the following about routes.gw: "IP of the gateway.
If omitted, a default gateway is assumed (as determined by the CNI
plugin)."
So, it should be up to the CNI plugin to determine what to do when the
gateway in a route isn't specified. Therefore, when converting from
0.2.0 to 0.3.x, we should NOT populate the gateway field.
Currently, the modified **plugins/main** was removed from master branch.
It's only available in **v0.3.0** tag. So this commit aims to update the
deprecated link to the working one.
Signed-off-by: Kim Bao Long <longkb@vn.fujitsu.com>
Current, the URLs that link docs of **rkt_networking_proposal** and
**rkt_networking_design** are already dead. So this commit aims to
clean up them from **SPEC.md**.
Co-Authored-By: Nguyen Phuong An <AnNP@vn.fujitsu.com>
Signed-off-by: Kim Bao Long <longkb@vn.fujitsu.com>
Commit containernetworking/plugins@4e1f780 (Split build.sh into two
OS-specific scripts) removes the build.sh file. Now we have
build_linux.sh and build_windows.sh
Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
While many plugins already have IPAM range info not all do. Without
a range/subnet in configuration, passing a plain IP address in
CNI_ARGS or via 'args' does not provide enough information.
This paragraph was under a heading specifically about configuration
lists, but it applies to all DEL operations whether in a list or not.
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
Plugins can use the new helpers to parse a prevResult. When parsing
their NetConf structure, assuming it has a types.NetConf embedded:
type MyConf struct {
types.NetConf
...
}
conf := MyConf{}
if err := json.Unmarshal(stdin, &conf); err != nil {
return nil, fmt.Errorf("failed to parse network configuration: %v", err)
}
if err := version.ParsePrevResult(&conf.NetConf); err != nil {
return nil, err
}
and then when the plugin wishes to access the PrevResult it can transform
conf.PrevResult into any Result version it wants:
if conf.PrevResult != nil {
res, err := current.NewResultFromResult(conf.PrevResult)
if err != nil {
return nil, fmt.Errorf("could not convert result to current version: %v", err)
}
for _, ip := range res.IPs {
...
}
}
When a downstream project just uses libcni, it takes a lot of harness
code to set up binaries on-disk that get called. Instead we can handle
that by passing an execer interface to libcni APIs that gets called
instead, which the downstreams can use to avoid execing thing entirely
and instead check arguments/environment in code.
Downstream users that currently create a CNIConfig object like this:
cniconf := libcni.CNIConfig{Path: []string{"foobar"}}
Can now fake out all on-disk binary exec/find operations with:
cniconf := libcni.NewCNIConfig([]string{"foobar"}, <exec interface>)
The CNI spec version passed from runtime to plugin is the `cniVersion`
field in the network configuration rather than a parameter to the ADD
and DEL command. So, remove the version parameter from the spec, and
also describe how the `cniVersion` field in the network configuration
should be used by the plugin.
Fixes#542
SPEC.md has indicated that 'name' is required since at least mid-2015,
but that was not enforced in pkg/skel or our unit tests. That could
lead to configs passing no network name and plugins like host-local
that require the name failing or causing odd behavior.
Since the name is required by the spec, require it in the code too.
The legacy example plugin and its libcni backwards_compatibility_test.go
just use the noop plugin, which doesn't do anything with the netns path
or make any interfaces inside the netns. So we can remove anything
netns related from the test, which means we no longer need sudo.
- Bonding CNI provides a method for aggregating multiple network interfaces into a single logical "bonded" interface.
- Linx Bonding drivers provides various flavour of bonded interface depending on the mode (bonding policies), such as round robin, active aggregation according to the 802.3 ad specification
For more information on the bonding driver. Please refer to [kernel doc(https://www.kernel.org/doc/Documentation/networking/bonding.txt)
"Libraries should never vendor their dependencies"
https://peter.bourgon.org/go-best-practices-2016/#dependency-management
The only thing in there were test-support packages anyhow
Remove Godeps folder, nothing to manage!
Remove build.sh
- not much here to build now that we've split out plugins
- for non-AMD64 platforms, travis just runs go build directly
Clean up test.sh
- no vendor --> simpler logic for enumerating packages
- use "bash strict mode"
http://redsymbol.net/articles/unofficial-bash-strict-mode
- coveralls token not required for public repos
Simplify .travis.yml, update to work without vendor
- fewer env vars
- go get -t ./...
Clean up Vagrantfile
- no godep required anymore
- bump Golang version 1.9.2
Previously, the spec did not require the container id to be set.
However, almost every plugin relies on it being unique, including the
CNI-maintained plugins. So, change the spec to require the use of
containerid and that plugins should treat it as a primary key.
Right now, I have to do the following
$ echo | CNI_COMMAND=VERSION bridge
or
CNI_COMMAND=VERSION bridge
^D
to be able to get the result back.
But VERSION doesn't need to read from stdin.
Plugins that don't have knowledge of interfaces, like host-local or
other IPAM plugins, should not set the 'interfaces' key of their
returned "Result" JSON. This should then not be translated into
an interface index of 0, which it was due to the int marshaling and
omitempty.
Instead, ensure that an omitted 'interface' in JSON ends up being
nil in the IPConfig structure, and that a nil ensures that no 'interfaces'
key is present in the JSON.
Yes, this means that plugins must call the 'current.Int(x)' method
when setting the Interfaces member. Oh well.
Fixes: https://github.com/containernetworking/cni/issues/404
Update README.md to call out how to use priv-net-run.sh and
docker-run.sh scripts post plugin split.
Signed-off-by: Manohar Castelino <manohar.r.castelino@intel.com>
types.LoadArgs does an unsafe typecast for env args key value pairs.
This can cause a panic if the config type fields do not implement
the encoding.TextUnmarshaler interface. This commit modifies it to
do a safe type cast and return an error in such scenarios.
- Use a new Fedora to get a more up to date Go version
- Don't build so many archives, they are not used.
- Handle the new build.sh script
- Just use host networking with rkt
Checking error strings makes these tests flaky, especially if the
error string is changed in libcni. Have gone ahead an introduced a new
error type `NoConfigsFoundError` and the Match is against the error
type making it more deterministic.
CNI-Genie enables orchestrators (kubernetes, mesos) for seamless connectivity to choice of CNI plugins (calico, canal, romana, weave) configured on a Node
It's difficult to include this repository using bazel, because
the file named "build" conflicts with new_go_repository generation
on case-insensitive filesystems (ref
https://github.com/bazelbuild/rules_go/issues/234). This change
renames the file to something that doesn't conflict, and also
renames the test script for consistency.
Add a new CapabilityArgs member to the RuntimeConf struct which runtimes can
use to pass arbitrary capability-based keys to the plugin. Elements of this
member will be filtered against the plugin's advertised capabilities (from
its config JSON) and then added to a new "runtimeConfig" top-level map added
to the config JSON sent to the plugin on stdin.
Also "runtime_config"->"runtimeConfig" in CONVENTIONS.md to make
capitalization consistent with other CNI config keys like "cniVersion".
moved functions that depend on linux packages (sys/unix) into a separate file
and added nop methods with a build tag for non-linux platforms in a new file.
After discussion with the CNI maintainers and representatives from Mesos and Kubernetes we've settled on this approach for passing the dynamic port mapping information from runtimes to plugins.
Including it in the plugin config allows "operators" to signal that they expect port mapping information to be passed to a specific plugin (or plugins). This follows the model that config passed in the plugin config should be acted upon by a plugin.
Runtimes can optionally return an error to users if they find no plugin in the chain that supports port mappings.
I'm sure we'd all like to see this merged soon so I'll try to turn any feedback quickly (and I'm happy to get any and all feedback - spellings, unclear working, wrong JSON terminology etc..)
A CNI network configuration file contains the plugin's executable file name.
Some platforms like Windows require a file name extension for executables.
This causes unnecessary burden on admins as they now have to maintain two
versions of each type of netconfig file, which differ only by the ".exe"
extension. A much simpler design is for invoke package to also look for
well-known extensions on platforms that require it. Existing tests are
improved and new tests are added to cover the new behavior.
Fixes#360
Hardcoding the list separator character as ":" causes CNI to fail when parsing
CNI_PATH on other operating systems. For example, Windows uses ";" as list
separator because ":" can legally appear in paths such as "C:\path\to\file".
This change replaces use of ":" with OS-agnostic APIs or os.PathListSeparator.
Fixes#358
Updates the spec and plugins to return an array of interfaces and IP details
to the runtime including:
- interface names and MAC addresses configured by the plugin
- whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc)
- multiple IP addresses configured by IPAM and which interface they
have been assigned to
Returning interface details is useful for runtimes, as well as allowing
more flexible chaining of CNI plugins themselves. For example, some
meta plugins may need to know the host-side interface to be able to
apply firewall or traffic shaping rules to the container.
Using a new ".configlist" file format that allows specifying
a list of CNI network configurations to run, add new libcni
helper functions to call each plugin in the list, injecting
the overall name, CNI version, and previous plugin's Result
structure into the configuration of the next plugin.
Chaining sends different config JSON to each plugin, but the same
environment, and if we want to test multiple noop plugin runs in
the same chain we need a way of telling each run to use a different
debug file.
This adds the option `resolvConf` to the host-local IPAM configuration.
If specified, the plugin will try to parse the file as a resolv.conf(5)
type file and return it in the DNS response.
It doesn't seem like container IDs should really have whitespace or
newlines in them. As a complete edge-case, manipulating the host-local
store's IP reservations with 'echo' puts a newline at the end, which
caused matching to fail in ReleaseByID(). Don't ask...
Add an e2e host-local plugin testcase, which requires being able
to pass the datadir into the plugin so we can erase it later.
We're not always guaranteed to have access to the default data
dir location, plus it should probably be configurable anyway.
- Add optional 'stateDir' to flannel NetConf, if not present default to
/var/lib/cni/flannel
Signed-off-by: Jay Dunkelberger <ldunkelberger@pivotal.io>
highlights:
- NetConf struct finally includes cniVersion field
- improve test coverage of current version report behavior
- godoc a few key functions
- allow tests to control version list reported by no-op plugin
When RangeEnd is given, a.end = RangeEnd+1.
If when getSearchRange() is called and lastReservedIP equals
RangeEnd, a.nextIP() only compares lastReservedIP (which in this
example is RangeEnd) against a.end (which in this example is
RangeEnd+1) and they clearly don't match, so a.nextIP() returns
start=RangeEnd+1 and end=RangeEnd.
Get() happily allocates RangeEnd+1 because it only compares 'cur'
to the end returned by getSearchRange(), not to a.end, and thus
allocates past RangeEnd.
Since a.end is inclusive (eg, host-local will allocate a.end) the
fix is to simply set a.end equal to RangeEnd.
The expectation on older kernels (< 3.19) was to have the network
namespace always be a directory. This is not true if the network
namespace is bind mounted to a file, and will make the plugin fail
erroneously in such cases. The fix is to remove this assumption
completely and just do a basic check on the file system types being
returned.
Fixes#288
The veth is moved from the container NS to the host NS.
This is handled by the code that sets the link to UP but the wrong
hostVeth is returned to the calling code.
Cross-compile cni for arm, arm64 and ppc64le with go1.6 only
Allow go tip to fail
Set fast_finish to true, which means travis will instantly return build failure when any of the required builds fail
ref #209
The current vendor of sys/unix is really old, and doesn't work on arm64 and ppc64le
Updating to the latest version might also fix other issues
ref #209
If interface name for a container provided by a user is already present,
Veth creation fails with incorrect error.
If os.IsExist error is returned by makeVethPair:
* Check for peer name, if exists generate another random peer name,
* else, IsExist error is due to container interface present, return error.
Fixes#155
Add possibility to reconfigure bridge IP address when there is a new value.
New boolean flag added to net configuration to force IP change if it is need.
Otherwise code behaves as previously and throws error
* bridge: Test the following interface's hardware address for the CNI specific
prefix:
- bridge with IP address
- container veth
* plugins/macvlan test: ensure hardware addr
This will give deterministic MAC addresses for all interfaces CNI
creates and manages the IP for:
* bridge: container veth and host bridge
* macvlan: container veth
* ptp: container veth and host veth
* _suite.go and _test.go file should be in the same package, using the
_test package for that, which requires some fields and methods to be
exported
* Introduce error type for cleaner error handling
* test adaptions for error type checking
Previously, the log lines appeared in stdout before the JSON encoding of
the error message. That would break JSON parsing of stdout. Instead, we use
stderr for these unstructured logs, consistent with the CNI spec.
Based on previous discussions on the CNI maintainers calls, the spec is
unclear on 1) when CNI_ARGS should be used and 2) the fact the dynamic
config can be passed in through the network JSON.
This PR makes it clear that per-container config can be passed in
through the network JSON, adding a top level `args` field into
which orchestrators can add additional metadata without worrying that
plugins might reject the additional data. It also allows for plugins to
reject unknown fields passed in at the top level.
Using JSON is preferable to CNI_ARGS since it allows namespaced and
structured data. CNI_ARGS is a flat list of KV pairs which has reserved
characters with no escaping rules defined.
CNI_ARGS may still be used by orchestrators that want the simplicity of
passing the network config JSON as specified by the user, unchanged
through to the CNI plugin. But for any kind of structured data, it's
recommended that the `args` field in the JSON is used instead.
Allow strings to be unmarshalled for CNI_ARGS
CNI_ARGS uses types.LoadArgs to populate a struct.
The fields in the struct must meet the TextUnmarshaler interface.
This code adds a UnmarshallableString type to assist with this.
This changes the ip allocation logic to round robin. Before this, host-local IPAM searched for available IPs from start of subnet. Hence it tends to allocate IPs that had been used recently. This is not ideal since it may cause collisions.
Thank you Zach for all the great work done on CNI, farewell!
At the same time we are happy to welcome Dan amongst us who has already
contributed lots of valuable work!
When isDefaultGateway is true it automatically sets isGateway to true.
The default route will be added via the (bridge's) gateway IP.
If a default gateway has been configured via IPAM in the same
configuration file, the plugin will error out.
Add a namespace object interface for somewhat cleaner code when
creating and switching between network namespaces. All created
namespaces are now mounted in /var/run/netns to ensure they
have persistent inodes and paths that can be passed around
between plugin components without relying on the current namespace
being correct.
Also remove the thread-locking arguments from the ns package
per https://github.com/appc/cni/issues/183 by doing all the namespace
changes in a separate goroutine that locks/unlocks itself, instead of
the caller having to track OS thread locking.
Previously this code used a run-time map lookup keyed by
runtime.GOOS/GOARCH. This version uses conditional compilation to make
this choice at compile time, giving immediate feedback for unsupported
platforms.
Resolves CNI part of https://github.com/coreos/rkt/issues/1765
Second part would be adding similar lines into kvm flavored macvlan
support (in time of creating macvtap device).
/proc/self/ns/net gives the main thread's namespace, not necessarily
the namespace of the thread that's running the testcases. This causes
sporadic failures of the tests.
For example, with a testcase reading inodes after switching netns:
/proc/27686/task/27689/ns/net 4026532565
/proc/self/ns/net 4026531969
/proc/27686/task/27689/ns/net 4026532565
See also:
008d17ae00
Running Suite: pkg/ns Suite
===========================
Random Seed: 1459953577
Will run 6 of 6 specs
• Failure [0.028 seconds]
Linux namespace operations
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:167
WithNetNS
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:166
executes the callback within the target network namespace [It]
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:97
Expected
<uint64>: 4026531969
to equal
<uint64>: 4026532565
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96
------------------------------
•••••
Summarizing 1 Failure:
[Fail] Linux namespace operations WithNetNS [It] executes the callback within the target network namespace
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96
Ran 6 of 6 Specs in 0.564 seconds
FAIL! -- 5 Passed | 1 Failed | 0 Pending | 0 Skipped --- FAIL: TestNs (0.56s)
FAIL
Previously this code used a run-time map lookup keyed by
runtime.GOOS/GOARCH. This version uses conditional compilation to make
this choice at compile time, giving immediate feedback for unsupported
platforms.
Not using NewLinkAttrs() or not initializing TxQLen leaves
the value as 0, which tells the kernel to set a zero-length
tx_queue_len. That messes up FIFO traffic shapers (like pfifo)
that use the device TX queue length as the default packet
limit. This leads to a default packet limit of 0, which drops
all packets.
/proc/self/ns/net gives the main thread's namespace, not necessarily
the namespace of the thread that's running the testcases. This causes
sporadic failures of the tests.
For example, with a testcase reading inodes after switching netns:
/proc/27686/task/27689/ns/net 4026532565
/proc/self/ns/net 4026531969
/proc/27686/task/27689/ns/net 4026532565
See also:
008d17ae00
Running Suite: pkg/ns Suite
===========================
Random Seed: 1459953577
Will run 6 of 6 specs
• Failure [0.028 seconds]
Linux namespace operations
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:167
WithNetNS
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:166
executes the callback within the target network namespace [It]
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:97
Expected
<uint64>: 4026531969
to equal
<uint64>: 4026532565
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96
------------------------------
•••••
Summarizing 1 Failure:
[Fail] Linux namespace operations WithNetNS [It] executes the callback within the target network namespace
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96
Ran 6 of 6 Specs in 0.564 seconds
FAIL! -- 5 Passed | 1 Failed | 0 Pending | 0 Skipped --- FAIL: TestNs (0.56s)
FAIL
Resolves CNI part of https://github.com/coreos/rkt/issues/1765
Second part would be adding similar lines into kvm flavored macvlan
support (in time of creating macvtap device).
This commit adds a struct type CommonArgs that is to be embedded in
every plugin's argument struct. It contains a field named
"IgnoreUnknown" which will be parsed as a boolean and can be provided to
ignore unknown arguments passed to the plugin.
Now that we no longer use godeps to manage
dependencies we can safely remove this.
Dependencies in the vendor directory will
automagically be picked up.
This is an attempt to testing the PluginMain() function of the skel pkg.
We should be able to do better by using a mockable interface for the
plugins, but this is a start.
The 'flannel' meta plugin delegates to other plugins to do the actual
OS-level work. It used the ipam.Exec{Add,Del} procedures for this
delegation, since those do precisely what's needed.
However this is a bit misleading, since the flannel plugin _isn't_
doing this for IPAM, and the ipam.Exec* procedures aren't doing
something specific to IPAM plugins.
So: anticipating that there may be more meta plugins that want to
delegate in the same way, this commit moves generic delegation
procedures to `pkg/invoke`, and makes the `pkg/ipam` procedures (still
used, accurately, in the non-meta plugins) shims.
- Believe we need sudo to create netns
- Use syscall instead of relying on ip netns
- Add sudo to .travis.yml
- Needs more -E
- Revert Godeps GoVersion to 1.4.2
- in travis, test command is run with all necessary env vars
- Loopback plugin only works on 'lo' interface
- Update README, add loopback plugin config
- note script dependency on jq
Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
Allow users to tune net network parameters such as somaxconn.
With this patch, users can add a new network configuration:
> {
> "name": "mytuning",
> "type": "tuning",
> "sysctl": {
> "net.core.somaxconn": "500"
> }
> }
The value /proc/sys/net/core/somaxconn will be set to 500 in the network
namespace but will remain unchanged on the host.
Only sysctl parameters that belong to the network subsystem can be
modified.
Related to: https://github.com/coreos/rkt/pull/2140
Eugene is no longer with CoreOS or actively involved with CNI, so remove
him from the current list of maintainers. He'll be gladly welcomed back
if he decides to rejoin the project.
appc/cni#76 added a "dns" field in the result JSON. But before this
patch, the plugins had no way of knowing which name server to return.
There could be two ways of knowing which name server to return:
1. add it as an extra argument ("CNI_ARGS")
2. add it in the network configuration as a convenience (received via
stdin)
I chose the second way because it is easier. In the case of rkt, it
means the user could just add the DNS name servers in
/etc/rkt/net.d/mynetwork.conf.
Version all artifacts using semantic versioning
so that plugins and container runtimes coded against
different versions of the spec can interoperate.
Fixes#44
In some cases the route deletion would fail due to wrong
scope. It should be NOWHERE when deleting (per iproute2).
This also adds more verbose error messages.
Instead of allocating a /31 for each container,
use the same IP on the host side for all veths.
This is very similar how real point-to-point
devices work (using donor IPs).
When plugin is executed with a DEL command, it does not
print result to stdout unless there is an error. Therefore
it stdout bytes should not be passed to json.Unmarshal.
This takes some of the machinery from CNI and from the rkt networking
code, and turns it into a library that can be linked into go apps.
Included is an example command-line application that uses the library,
called `cnitool`.
Other headline changes:
* Plugin exec'ing is factored out
The motivation here is to factor out the protocol for invoking
plugins. To that end, a generalisation of the code from api.go and
pkg/plugin/ipam.go goes into pkg/invoke/exec.go.
* Move argument-handling and conf-loading into public API
The fact that the arguments get turned into an environment for the
plugin is incidental to the API; so, provide a way of supplying them
as a struct or saying "just use the same arguments as I got" (the
latter is for IPAM plugins).
Luckily the docs haven't mentioned support for ipMasq for both plugins so far.
Even if anyone has attempted to enable the feature in their configuration files it didn't have the desired effect for the network.
A specific IP can now be requested via the environment variable CNI_ARGS, e.g.
`CNI_ARGS=ip=1.2.3.4`.
The plugin will try to reserve the specified IP.
If this is not successful the execution will fail.
There are three ways of passing information to plugins using the Container Network Interface (CNI), none of which require the [spec](SPEC.md) to be updated. These are
- plugin specific fields in the JSON config
- `args` field in the JSON config
- `CNI_ARGS` environment variable
This document aims to provide guidance on which method should be used and to provide a convention for how common information should be passed.
Establishing these conventions allows plugins to work across multiple runtimes. This helps both plugins and the runtimes.
## Plugins
* Plugin authors should aim to support these conventions where it makes sense for their plugin. This means they are more likely to "just work" with a wider range of runtimes.
* Plugins should accept arguments according to these conventions if they implement the same basic functionality as other plugins. If plugins have shared functionality that isn't covered by these conventions then a PR should be opened against this document.
## Runtimes
* Runtime authors should follow these conventions if they want to pass additional information to plugins. This will allow the extra information to be consumed by the widest range of plugins.
* These conventions serve as an abstraction for the runtime. For example, port forwarding is highly implementation specific, but users should be able to select the plugin of their choice without changing the runtime.
# Current conventions
Additional conventions can be created by creating PRs which modify this document.
## Dynamic Plugin specific fields (Capabilities / Runtime Configuration)
[Plugin specific fields](SPEC.md#network-configuration) formed part of the original CNI spec and have been present since the initial release.
> Plugins may define additional fields that they accept and may generate an error if called with unknown fields. The exception to this is the args field may be used to pass arbitrary data which may be ignored by plugins.
A plugin can define any additional fields it needs to work properly. It should return an error if it can't act on fields that were expected or where the field values were malformed.
This method of passing information to a plugin is recommended when the following conditions hold:
* The configuration has specific meaning to the plugin (i.e. it's not just general meta data)
* the plugin is expected to act on the configuration or return an error if it can't
Dynamic information (i.e. data that a runtime fills out) should be placed in a `runtimeConfig` section. Plugins can request
that the runtime insert this dynamic configuration by explicitly listing their `capabilities` in the network configuration.
For example, the configuration for a port mapping plugin might look like this to an operator (it should be included as part of a [network configuration list](SPEC.md#network-configuration-lists).
```json
{
"name" : "ExamplePlugin",
"type" : "port-mapper",
"capabilities": {"portMappings": true}
}
```
But the runtime would fill in the mappings so the plugin itself would receive something like this.
| port mappings | Pass mapping from ports on the host to ports in the container network namespace. | `portMappings` | A list of portmapping entries.<br/><pre>[<br/> { "hostPort": 8080, "containerPort": 80, "protocol": "tcp" },<br/> { "hostPort": 8000, "containerPort": 8001, "protocol": "udp" }<br/> ]<br/></pre> | kubernetes | CNI `portmap` plugin |
| ip ranges | Dynamically configure the IP range(s) for address allocation. Runtimes that manage IP pools, but not individual IP addresses, can pass these to plugins. | `ipRanges` | The same as the `ranges` key for `host-local` - a list of lists of subnets. The outer list is the number of IPs to allocate, and the inner list is a pool of subnets for each allocation. <br/><pre>[<br/> [<br/> { "subnet": "10.1.2.0/24", "rangeStart": "10.1.2.3", "rangeEnd": 10.1.2.99", "gateway": "10.1.2.254" } <br/> ]<br/>]</pre> | none | CNI `host-local` plugin |
| bandwidth limits | Dynamically configure interface bandwidth limits | `bandwidth` | Desired bandwidth limits. Rates are in bits per second, burst values are in bits. <pre> { "ingressRate": 2048, "ingressBurst": 1600, "egressRate": 4096, "egressBurst": 1600 } </pre> | none | CNI `bandwidth` plugin |
| dns | Dynamically configure dns according to runtime | `dns` | Dictionary containing a list of `servers` (string entries), a list of `searches` (string entries), a list of `options` (string entries). <pre>{ <br> "searches" : [ "internal.yoyodyne.net", "corp.tyrell.net" ] <br> "servers": [ "8.8.8.8", "10.0.0.10" ] <br/>} </pre> | kubernetes | CNI `win-bridge` plugin, CNI `win-overlay` plugin |
| ips | Dynamically allocate IPs for container interface. Runtime which has the ability of address allocation can pass these to plugins. | `ips` | A list of `IP` (\<ip\>\[/\<prefix\>\]). <pre> [ "192.168.0.1", 10.10.0.1/24", "3ffe:ffff:0:01ff::2", "3ffe:ffff:0:01ff::1/64" ] </pre> The plugin may require the IP address to include a prefix length. | none | CNI `static` plugin, CNI `host-local` plugin |
| mac | Dynamically assign MAC. Runtime can pass this to plugins which need MAC as input. | `mac` | `MAC` (string entry). <pre> "c2:11:22:33:44:55" </pre> | none | CNI `tuning` plugin |
| infiniband guid | Dynamically assign Infiniband GUID to network interface. Runtime can pass this to plugins which need Infiniband GUID as input. | `infinibandGUID` | `GUID` (string entry). <pre> "c2:11:22:33:44:55:66:77" </pre> | none | CNI [`ib-sriov-cni`](https://github.com/Mellanox/ib-sriov-cni) plugin |
| device id | Provide device identifier which is associated with the network to allow the CNI plugin to perform device dependent network configurations. | `deviceID` | `deviceID` (string entry). <pre> "0000:04:00.5" </pre> | none | CNI `host-device` plugin |
| aliases | Provide a list of names that will be mapped to the IP addresses assigned to this interface. Other containers on the same network may use one of these names to access the container.| `aliases` | List of `alias` (string entry). <pre> ["my-container", "primary-db"] </pre> | none | CNI `alias` plugin |
| cgroup path | Provide the cgroup path for pod as requested by CNI plugins. | `cgroupPath` | `cgroupPath` (string entry). <pre>"/kubelet.slice/kubelet-kubepods.slice/kubelet-kubepods-burstable.slice/kubelet-kubepods-burstable-pod28ce45bc_63f8_48a3_a99b_cfb9e63c856c.slice" </pre> | none | CNI `host-local` plugin |
## "args" in network config
`args` in [network config](SPEC.md#network-configuration) were reserved as a field in the `0.2.0` release of the CNI spec.
> args (dictionary): Optional additional arguments provided by the container runtime. For example a dictionary of labels could be passed to CNI plugins by adding them to a labels field under args.
`args` provide a way of providing more structured data than the flat strings that CNI_ARGS can support.
`args` should be used for _optional_ meta-data. Runtimes can place additional data in `args` and plugins that don't understand that data should just ignore it. Runtimes should not require that a plugin understands or consumes that data provided, and so a runtime should not expect to receive an error if the data could not be acted on.
This method of passing information to a plugin is recommended when the information is optional and the plugin can choose to ignore it. It's often that case that such information is passed to all plugins by the runtime without regard for whether the plugin can understand it.
The conventions documented here are all namespaced under `cni` so they don't conflict with any existing `args`.
For example:
```jsonc
{
"cniVersion":"0.2.0",
"name":"net",
"args":{
"cni":{
"labels": [{"key": "app", "value": "myapp"}]
}
},
// <RESTOFCNICONFIGHERE>
"ipam":{
// <IPAMCONFIGHERE>
}
}
```
| Area | Purpose| Spec and Example | Runtime implementations | Plugin Implementations |
| ips | Request specific IPs | Spec:<pre>"ips": ["\<ip\>[/\<prefix\>]", ...]</pre>Examples:<pre>"ips": ["10.2.2.42/24", "2001:db8::5"]</pre> The plugin may require the IP address to include a prefix length. | none | host-local, static |
## CNI_ARGS
CNI_ARGS formed part of the original CNI spec and have been present since the initial release.
> `CNI_ARGS`: Extra arguments passed in by the user at invocation time. Alphanumeric key-value pairs separated by semicolons; for example, "FOO=BAR;ABC=123"
The use of `CNI_ARGS` is deprecated and "args" should be used instead. If a runtime passes an equivalent key via `args` (eg the `ips``args` Area and the `CNI_ARGS``IP` Field) and the plugin understands `args`, the plugin must ignore the CNI_ARGS Field.
| Field | Purpose| Spec and Example | Runtime implementations | Plugin Implementations |
| IP | Request a specific IP from IPAM plugins | Spec:<pre>IP=\<ip\>[/\<prefix\>]</pre>Example: <pre>IP=192.168.10.4/24</pre> The plugin may require the IP addresses to include a prefix length. | *rkt* supports passing additional arguments to plugins and the [documentation](https://coreos.com/rkt/docs/latest/networking/overriding-defaults.html) suggests IP can be used. | host-local, static |
## Chained Plugins
If plugins are agnostic about the type of interface created, they SHOULD work in a chained mode and configure existing interfaces. Plugins MAY also create the desired interface when not run in a chain.
For example, the `bridge` plugin adds the host-side interface to a bridge. So, it should accept any previous result that includes a host-side interface, including `tap` devices. If not called as a chained plugin, it creates a `veth` pair first.
Plugins that meet this convention are usable by a larger set of runtimes and interfaces, including hypervisors and DPDK providers.
With bridge plugin, all containers (on the same host) are plugged into a bridge (virtual switch) that resides in the host network namespace.
The containers receive one end of the veth pair with the other end connected to the bridge.
An IP address is only assigned to one end of the veth pair -- one residing in the container.
The bridge itself can also be assigned an IP address, turning it into a gateway for the containers.
Alternatively, the bridge can function purely in L2 mode and would need to be bridged to the host network interface (if other than container-to-container communication on the same host is desired).
The network configuration specifies the name of the bridge to be used.
If the bridge is missing, the plugin will create one on first use and, if gateway mode is used, assign it an IP that was returned by IPAM plugin via the gateway field.
## Example configuration
```
{
"name": "mynet",
"type": "bridge",
"bridge": "mynet0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16"
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network.
* `type` (string, required): "bridge".
* `bridge` (string, optional): name of the bridge to use/create. Defaults to "cni0".
* `isGateway` (boolean, optional): assign an IP address to the bridge. Defaults to false.
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false.
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
host-local IPAM plugin allocates IPv4 addresses out of a specified address range.
It stores the state locally on the host filesystem, therefore ensuring uniqueness of IP addresses on a single host.
## Example configuration
```
{
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }
]
}
}
```
## Network configuration reference
* `type` (string, required): "host-local".
* `subnet` (string, required): CIDR block to allocate out of.
* `rangeStart` (string, optional): IP inside of "subnet" from which to start allocating addresses. Defaults to ".2" IP inside of the "subnet" block.
* `rangeEnd` (string, optional): IP inside of "subnet" with which to end allocating addresses. Defaults to ".254" IP inside of the "subnet" block.
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
* `routes` (string, optional): list of routes to add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
## Files
Allocated IP addresses are stored as files in /var/lib/cni/networks/$NETWORK_NAME.
ipvlan is a new [addition](https://lwn.net/Articles/620087/) to the Linux kernel.
Like its cousin macvlan, it virtualizes the host interface.
However unlike macvlan which generates a new MAC address for each interface, ipvlan devices all share the same MAC.
The kernel driver inspects the IP address of each packet when making a decision about which virtual interface should process the packet.
Because all ipvlan interfaces share the MAC address with the host interface, DHCP can only be used in conjunction with ClientID (currently not supported by DHCP plugin).
## Example configuration
```
{
"name": "mynet",
"type": "ipvlan",
"master": "eth0",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
}
}
```
## Network configuration reference
* `name` (string, required): the name of the network.
* `type` (string, required): "ipvlan".
* `master` (string, required): name of the host interface to enslave.
* `mode` (string, optional): one of "l2", "l3". Defaults to "l2".
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
## Notes
* `ipvlan` does not allow virtual interfaces to communicate with the master interface.
Therefore the container will not be able to reach the host via `ipvlan` interface.
Be sure to also have container join a network that provides connectivity to the host (e.g. `ptp`).
* A single master interface can not be enslaved by both `macvlan` and `ipvlan`.
The ptp plugin creates a point-to-point link between a container and the host by using a veth device.
One end of the veth pair is placed inside a container and the other end resides on the host.
Both ends receive an IP address out of a /31 range.
The IP of the host end becomes the gateway address inside the container.
Because ptp plugin requires a pair of IP addresses for each container, it should be used in conjuction with host-local-ptp IPAM plugin.
## Example network configuration
```
{
"name": "mynet",
"type": "ptp",
"ipam": {
"type": "host-local-ptp",
"subnet": "10.1.1.0/24"
}
}
## Network configuration reference
* `name` (string, required): the name of the network
* `type` (string, required): "ptp"
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false.
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to value chosen by the kernel.
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
- A new verb, "CHECK", was added. Runtimes can now ask plugins to verify the status of a container's attachment
- A new configuration flag, `disableCheck`, which indicates to the runtime that configuration should not be CHECK'ed
No changes were made to the result type.
# How to upgrade to CNI Specification v0.3.0 and later
The 0.3.0 specification contained a small error. The Result structure's `ip` field should have been renamed to `ips` to be consistent with the IPAM result structure definition; this rename was missed when updating the Result to accommodate multiple IP addresses and interfaces. All first-party CNI plugins (bridge, host-local, etc) were updated to use `ips` (and thus be inconsistent with the 0.3.0 specification) and most other plugins have not been updated to the 0.3.0 specification yet, so few (if any) users should be impacted by this change.
The 0.3.1 specification corrects the `Result` structure to use the `ips` field name as originally intended. This is the only change between 0.3.0 and 0.3.1.
Version 0.3.0 of the [CNI Specification](https://github.com/containernetworking/cni/blob/spec-v0.3.0/SPEC.md) provides rich information
about container network configuration, including details of network interfaces
and support for multiple IP addresses.
To support this new data, the specification changed in a couple significant
ways that will impact CNI users, plugin authors, and runtime authors.
This document provides guidance for how to upgrade:
- [For CNI Users](#for-cni-users)
- [For Plugin Authors](#for-plugin-authors)
- [For Runtime Authors](#for-runtime-authors)
**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.
----
## 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.
Configuration files without a version field should be given version 0.2.0.
The CNI spec includes example configuration files for
[single plugins](SPEC.md#example-configurations)
and for [lists of chained plugins](SPEC.md#example-configurations).
Consult the documentation for your runtime and plugins to determine what
CNI spec versions they support. Test any plugin upgrades before deploying to
production. You may find [cnitool](https://github.com/containernetworking/cni/tree/main/cnitool)
useful. Specifically, your configuration version should be the lowest common
version supported by your plugins.
## For Plugin Authors
This section provides guidance for upgrading plugins to CNI Spec Version 0.3.0.
### 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 1.0.0 while maintaining
compatibility with older versions.
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`
This document defines project governance for the project.
## Voting
The CNI project employs "organization voting" to ensure no single organization can dominate the project.
Individuals not associated with or employed by a company or organization are allowed one organization vote.
Each company or organization (regardless of the number of maintainers associated with or employed by that company/organization) receives one organization vote.
In other words, if two maintainers are employed by Company X, two by Company Y, two by Company Z, and one maintainer is an un-affiliated individual, a total of four "organization votes" are possible; one for X, one for Y, one for Z, and one for the un-affiliated individual.
Any maintainer from an organization may cast the vote for that organization.
For formal votes, a specific statement of what is being voted on should be added to the relevant github issue or PR, and a link to that issue or PR added to the maintainers meeting agenda document.
Maintainers should indicate their yes/no vote on that issue or PR, and after a suitable period of time, the votes will be tallied and the outcome noted.
## Changes in Maintainership
New maintainers are proposed by an existing maintainer and are elected by a 2/3 majority organization vote.
Maintainers can be removed by a 2/3 majority organization vote.
## Approving PRs
Non-specification-related PRs may be merged after receiving at least two organization votes.
Changes to the CNI Specification also follow the normal PR approval process (eg, 2 organization votes), but any maintainer can request that the approval require a 2/3 majority organization vote.
## Github Project Administration
Maintainers will be added to the containernetworking GitHub organization and added to the GitHub cni-maintainers team, and made a GitHub maintainer of that team.
After 6 months a maintainer will be made an "owner" of the GitHub organization.
## Changes in Governance
All changes in Governance require a 2/3 majority organization vote.
## Other Changes
Unless specified above, all other changes to the project require a 2/3 majority organization vote.
Additionally, any maintainer may request that any change require a 2/3 majority organization vote.
dhcp4client [](http://godoc.org/github.com/d2g/dhcp4client) [](https://coveralls.io/r/d2g/dhcp4client?branch=HEAD) [](https://codeship.com/projects/70187)