commit
02981925c9
86
Documentation/flannel.md
Normal file
86
Documentation/flannel.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# flannel plugin
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This plugin is designed to work in conjunction with [flannel](https://github.com/coreos/flannel), a network fabric for containers.
|
||||||
|
When flannel daemon is started, it outputs a `/run/flannel/subnet.env` file that looks like this:
|
||||||
|
```
|
||||||
|
FLANNEL_SUBNET=10.1.17.0/24
|
||||||
|
FLANNEL_MTU=1472
|
||||||
|
FLANNEL_IPMASQ=true
|
||||||
|
```
|
||||||
|
|
||||||
|
This information reflects the attributes of flannel network on the host.
|
||||||
|
The flannel CNI plugin uses this information to configure another CNI plugin, such as bridge plugin.
|
||||||
|
|
||||||
|
## Operation
|
||||||
|
Given the following network configuration file and the contents of `/run/flannel/subnet.env` above,
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "flannel"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
the flannel plugin will generate another network configuration file:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "bridge",
|
||||||
|
"mtu": 1472,
|
||||||
|
"ipMasq": false,
|
||||||
|
"isGateway": true,
|
||||||
|
"ipam": {
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.1.17.0/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It will then invoke the bridge plugin, passing it the generated configuration.
|
||||||
|
|
||||||
|
As can be seen from above, the flannel plugin, by default, will delegate to the bridge plugin.
|
||||||
|
If additional configuration values need to be passed to the bridge plugin, it can be done so via the `delegate` field:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "flannel",
|
||||||
|
"delegate": {
|
||||||
|
"bridge": "mynet0",
|
||||||
|
"mtu": 1400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This supplies a configuration parameter to the bridge plugin -- the created bridge will now be named `mynet0`.
|
||||||
|
Notice that `mtu` has also been specified and this value will not be overwritten by flannel plugin.
|
||||||
|
|
||||||
|
Additionally, the `delegate` field can be used to select a different kind of plugin altogether.
|
||||||
|
To use `ipvlan` instead of `bridge`, the following configuratoin can be specified:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "flannel",
|
||||||
|
"delegate": {
|
||||||
|
"type": "ipvlan",
|
||||||
|
"master": "eth0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network configuration reference
|
||||||
|
|
||||||
|
* `name` (string, required): the name of the network
|
||||||
|
* `type` (string, required): "flannel"
|
||||||
|
* `subnetFile` (string, optional): full path to the subnet file written out by flanneld. Defaults to /run/flannel/subnet.env
|
||||||
|
* `delegate` (dictionary, optional): specifies configuration options for the delegated plugin.
|
||||||
|
|
||||||
|
flannel plugin will always set the following fields in the delegated plugin configuration:
|
||||||
|
|
||||||
|
* `name`: value of its "name" field.
|
||||||
|
* `ipam`: "host-local" type will be used with "subnet" set to `$FLANNEL_SUBNET`.
|
||||||
|
|
||||||
|
flannel plugin will set the following fields in the delegated plugin configuration if they are not present:
|
||||||
|
* `ipMasq`: the inverse of `$FLANNEL_IPMASQ`
|
||||||
|
* `mtu`: `$FLANNEL_MTU`
|
||||||
|
|
||||||
|
Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not present.
|
@ -17,6 +17,7 @@ Hence we are proposing this specification, along with an initial set of plugins
|
|||||||
|
|
||||||
## Included Plugins
|
## Included Plugins
|
||||||
This repository includes a number of common plugins that can be found in plugins/ directory.
|
This repository includes a number of common plugins that can be found in plugins/ directory.
|
||||||
|
Please see Documentation/ folder for documentation about particular plugins.
|
||||||
|
|
||||||
## Running the plugins
|
## Running the plugins
|
||||||
The scripts/ directory contains two scripts, priv-net-run.sh and docker-run.sh, that can be used to excercise the plugins.
|
The scripts/ directory contains two scripts, priv-net-run.sh and docker-run.sh, that can be used to excercise the plugins.
|
||||||
|
2
build
2
build
@ -13,7 +13,7 @@ export GOPATH=${PWD}/gopath
|
|||||||
|
|
||||||
echo "Building plugins"
|
echo "Building plugins"
|
||||||
|
|
||||||
PLUGINS="plugins/main/* plugins/ipam/*"
|
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/*"
|
||||||
for d in $PLUGINS; do
|
for d in $PLUGINS; do
|
||||||
if [ -d $d ]; then
|
if [ -d $d ]; then
|
||||||
plugin=$(basename $d)
|
plugin=$(basename $d)
|
||||||
|
216
plugins/meta/flannel/flannel.go
Normal file
216
plugins/meta/flannel/flannel.go
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
// Copyright 2015 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 "meta-plugin". It reads in its own netconf, combines it with
|
||||||
|
// the data from flannel generated subnet file and then invokes a plugin
|
||||||
|
// like bridge or ipvlan to do the real work.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/appc/cni/pkg/plugin"
|
||||||
|
"github.com/appc/cni/pkg/skel"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultSubnetFile = "/run/flannel/subnet.env"
|
||||||
|
stateDir = "/var/lib/cni/flannel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetConf struct {
|
||||||
|
plugin.NetConf
|
||||||
|
SubnetFile string `json:"subnetFile"`
|
||||||
|
Delegate map[string]interface{} `json:"delegate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type subnetEnv struct {
|
||||||
|
sn *net.IPNet
|
||||||
|
mtu uint
|
||||||
|
ipmasq bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadFlannelNetConf(bytes []byte) (*NetConf, error) {
|
||||||
|
n := &NetConf{
|
||||||
|
SubnetFile: defaultSubnetFile,
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadFlannelSubnetEnv(fn string) (*subnetEnv, error) {
|
||||||
|
f, err := os.Open(fn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
se := &subnetEnv{}
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
for s.Scan() {
|
||||||
|
parts := strings.SplitN(s.Text(), "=", 2)
|
||||||
|
switch parts[0] {
|
||||||
|
case "FLANNEL_SUBNET":
|
||||||
|
_, se.sn, err = net.ParseCIDR(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case "FLANNEL_MTU":
|
||||||
|
mtu, err := strconv.ParseUint(parts[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
se.mtu = uint(mtu)
|
||||||
|
|
||||||
|
case "FLANNEL_IPMASQ":
|
||||||
|
se.ipmasq = parts[1] == "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return se, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveScratchNetConf(containerID string, netconf []byte) error {
|
||||||
|
if err := os.MkdirAll(stateDir, 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path := filepath.Join(stateDir, containerID)
|
||||||
|
return ioutil.WriteFile(path, netconf, 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumeScratchNetConf(containerID string) ([]byte, error) {
|
||||||
|
path := filepath.Join(stateDir, containerID)
|
||||||
|
defer os.Remove(path)
|
||||||
|
|
||||||
|
return ioutil.ReadFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func delegateAdd(cid string, netconf map[string]interface{}) error {
|
||||||
|
netconfBytes, err := json.Marshal(netconf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error serializing delegate netconf: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the rendered netconf for cmdDel
|
||||||
|
if err = saveScratchNetConf(cid, netconfBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := plugin.ExecAdd(netconf["type"].(string), netconfBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Print()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasKey(m map[string]interface{}, k string) bool {
|
||||||
|
_, ok := m[k]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func isString(i interface{}) bool {
|
||||||
|
_, ok := i.(string)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
|
n, err := loadFlannelNetConf(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fenv, err := loadFlannelSubnetEnv(n.SubnetFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.Delegate == nil {
|
||||||
|
n.Delegate = make(map[string]interface{})
|
||||||
|
} else {
|
||||||
|
if hasKey(n.Delegate, "type") && !isString(n.Delegate["type"]) {
|
||||||
|
return fmt.Errorf("'delegate' dictionary, if present, must have (string) 'type' field")
|
||||||
|
}
|
||||||
|
if hasKey(n.Delegate, "name") {
|
||||||
|
return fmt.Errorf("'delegate' dictionary must not have 'name' field, it'll be set by flannel")
|
||||||
|
}
|
||||||
|
if hasKey(n.Delegate, "ipam") {
|
||||||
|
return fmt.Errorf("'delegate' dictionary must not have 'ipam' field, it'll be set by flannel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Delegate["name"] = n.Name
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "type") {
|
||||||
|
n.Delegate["type"] = "bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "ipMasq") {
|
||||||
|
// if flannel is not doing ipmasq, we should
|
||||||
|
ipmasq := !fenv.ipmasq
|
||||||
|
n.Delegate["ipMasq"] = ipmasq
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "mtu") {
|
||||||
|
mtu := fenv.mtu
|
||||||
|
n.Delegate["mtu"] = mtu
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.Delegate["type"].(string) == "bridge" {
|
||||||
|
if !hasKey(n.Delegate, "isGateway") {
|
||||||
|
n.Delegate["isGateway"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Delegate["ipam"] = map[string]string{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": fenv.sn.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegateAdd(args.ContainerID, n.Delegate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
|
netconfBytes, err := consumeScratchNetConf(args.ContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := &plugin.NetConf{}
|
||||||
|
if err = json.Unmarshal(netconfBytes, n); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse netconf: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin.ExecDel(n.Type, netconfBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
skel.PluginMain(cmdAdd, cmdDel)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user