Add CNI network plugin
This PR adds CNI network plugin to stackube. Usage: 1. CNI network config { "cniVersion": "0.3.1", "name": "net", "type": "kubestack", "kubestack-config": "/etc/kubestack.conf" } 2. /etc/kubestack.conf [Global] auth-url=https://192.168.0.3/identity_admin/v2.0 username=admin password=password tenant-name=admin region=RegionOne ext-net-id=550370a3-4fc2-4494-919d-cae33f5b3de8 [Plugin] plugin-name=ovs integration-bridge=br-int 3. Put stackube command to /opt/cni/bin Change-Id: I3a666aa2b0b90ef5d8e2f065ddda0deb6d0fcd94 Implements: blueprint cni-plugin Signed-off-by: Pengfei Ni <feiskyer@gmail.com> Signed-off-by: mozhuli <21621232@zju.edu.cn>
This commit is contained in:
parent
7bb760620f
commit
69dc3b3a6b
46
Godeps/Godeps.json
generated
46
Godeps/Godeps.json
generated
@ -25,6 +25,35 @@
|
||||
"ImportPath": "github.com/PuerkitoBio/urlesc",
|
||||
"Rev": "5bd2802263f21d8788851d5305584c82a5c75d7e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/containernetworking/cni/pkg/skel",
|
||||
"Comment": "v0.5.2-64-g6a6f9d7",
|
||||
"Rev": "6a6f9d7f5c148ba7134c9cf31687329bdf1c7eeb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/containernetworking/cni/pkg/types",
|
||||
"Comment": "v0.5.2-64-g6a6f9d7",
|
||||
"Rev": "6a6f9d7f5c148ba7134c9cf31687329bdf1c7eeb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/containernetworking/cni/pkg/types/020",
|
||||
"Comment": "v0.5.2-64-g6a6f9d7",
|
||||
"Rev": "6a6f9d7f5c148ba7134c9cf31687329bdf1c7eeb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/containernetworking/cni/pkg/types/current",
|
||||
"Comment": "v0.5.2-64-g6a6f9d7",
|
||||
"Rev": "6a6f9d7f5c148ba7134c9cf31687329bdf1c7eeb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/containernetworking/cni/pkg/version",
|
||||
"Comment": "v0.5.2-64-g6a6f9d7",
|
||||
"Rev": "6a6f9d7f5c148ba7134c9cf31687329bdf1c7eeb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/containernetworking/plugins/pkg/ns",
|
||||
"Rev": "56d8984bbc73211051aaaa07ab32b2c194e39340"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||
"Comment": "v1.1.0",
|
||||
@ -40,6 +69,11 @@
|
||||
"Comment": "v2.6.1-2-g42ea75c",
|
||||
"Rev": "42ea75ca2d11ff2f34b8b4fffae7e3dc45ccf906"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/distribution/uuid",
|
||||
"Comment": "v2.6.1-2-g42ea75c",
|
||||
"Rev": "42ea75ca2d11ff2f34b8b4fffae7e3dc45ccf906"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/emicklei/go-restful",
|
||||
"Comment": "2.2.0-4-gff4f55a",
|
||||
@ -162,6 +196,18 @@
|
||||
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers",
|
||||
"Rev": "5102b608e3e070dadf65b060362fe4052f0c5967"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding",
|
||||
"Rev": "5102b608e3e070dadf65b060362fe4052f0c5967"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups",
|
||||
"Rev": "5102b608e3e070dadf65b060362fe4052f0c5967"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules",
|
||||
"Rev": "5102b608e3e070dadf65b060362fe4052f0c5967"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/networks",
|
||||
"Rev": "5102b608e3e070dadf65b060362fe4052f0c5967"
|
||||
|
10
Makefile
10
Makefile
@ -37,11 +37,15 @@ depend-update: work
|
||||
|
||||
.PHONY: build
|
||||
build: depend
|
||||
cd $(DEST) && go build $(GOFLAGS) -a -o $(OUTPUT)/stackube-controller ./cmd/stackube-controller
|
||||
cd $(DEST)
|
||||
go build $(GOFLAGS) -a -o $(OUTPUT)/stackube-controller ./cmd/stackube-controller
|
||||
go build $(GOFLAGS) -a -o $(OUTPUT)/kubestack ./cmd/kubestack
|
||||
|
||||
.PHONY: install
|
||||
install: depend
|
||||
cd $(DEST) && go install $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./...
|
||||
cd $(DEST)
|
||||
install -D -m 755 $(OUTPUT)/stackube-controller /usr/local/bin/stackube-controller
|
||||
install -D -m 755 $(OUTPUT)/kubestack /opt/cni/bin/kubestack
|
||||
|
||||
.PHONY: test
|
||||
test: test-unit
|
||||
@ -59,7 +63,7 @@ test-flags:
|
||||
# to detect if any files are listed as having format problems.
|
||||
.PHONY: fmt
|
||||
fmt: work
|
||||
files=$$(cd $(DEST) && find . -not \( \( -wholename '*/vendor/*' \) -prune \) -name '*.go' | xargs gofmt -s -l | tee >(cat - >&2)); [ -z "$$files" ]
|
||||
files=$$(cd $(DEST) && find . -not \( \( -wholename '*/vendor/*' \) -prune \) -name '*.go' | xargs gofmt -s -l | tee >(cat - >&2)); [ -z "$$files" ]
|
||||
|
||||
.PHONY: fmtfix
|
||||
fmtfix: work
|
||||
|
322
cmd/kubestack/kubestack.go
Normal file
322
cmd/kubestack/kubestack.go
Normal file
@ -0,0 +1,322 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
|
||||
"git.openstack.org/openstack/stackube/pkg/kubestack/plugins"
|
||||
kubestacktypes "git.openstack.org/openstack/stackube/pkg/kubestack/types"
|
||||
"git.openstack.org/openstack/stackube/pkg/openstack"
|
||||
"git.openstack.org/openstack/stackube/pkg/util"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
cniSpecVersion "github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/golang/glog"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||
|
||||
// import plugins
|
||||
_ "git.openstack.org/openstack/stackube/pkg/kubestack/plugins/openvswitch"
|
||||
)
|
||||
|
||||
var (
|
||||
// VERSION is filled out during the build process (using git describe output)
|
||||
VERSION = "0.1"
|
||||
)
|
||||
|
||||
// OpenStack describes openstack client and its plugins.
|
||||
type OpenStack struct {
|
||||
Client openstack.Client
|
||||
Plugin plugins.PluginInterface
|
||||
}
|
||||
|
||||
func init() {
|
||||
// this ensures that main runs only on main thread (thread group leader).
|
||||
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||
// must ensure that the goroutine does not jump from OS thread to thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func loadNetConf(bytes []byte) (*kubestacktypes.NetConf, string, error) {
|
||||
n := &kubestacktypes.NetConf{}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
return n, n.CNIVersion, nil
|
||||
}
|
||||
|
||||
func (os *OpenStack) getNetworkIDByNamespace(namespace string) (string, error) {
|
||||
// Only support one network and network's name is same with namespace.
|
||||
// TODO: make it general after multi-network is supported.
|
||||
networkName := util.BuildNetworkName(namespace, namespace)
|
||||
network, err := os.Client.GetNetwork(networkName)
|
||||
if err != nil {
|
||||
glog.Errorf("Get network by name %q failed: %v", networkName, err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return network.Uid, nil
|
||||
}
|
||||
|
||||
func getHostName() string {
|
||||
host, err := os.Hostname()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func getK8sArgs(args string) (string, string, error) {
|
||||
k8sArgs := kubestacktypes.K8sArgs{}
|
||||
if err := types.LoadArgs(args, &k8sArgs); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return string(k8sArgs.K8S_POD_NAME), string(k8sArgs.K8S_POD_NAMESPACE), nil
|
||||
}
|
||||
|
||||
func initOpenstack(stdinData []byte) (OpenStack, string, error) {
|
||||
// Load cni net config
|
||||
n, cniVersion, err := loadNetConf(stdinData)
|
||||
if err != nil {
|
||||
return OpenStack{}, "", err
|
||||
}
|
||||
|
||||
//Init openstack client
|
||||
if n.KubestackConfig == "" {
|
||||
return OpenStack{}, "", fmt.Errorf("kubestack-config not specified")
|
||||
}
|
||||
openStackClient, err := openstack.NewClient(n.KubestackConfig)
|
||||
if err != nil {
|
||||
return OpenStack{}, "", err
|
||||
}
|
||||
os := OpenStack{
|
||||
Client: *openStackClient,
|
||||
}
|
||||
|
||||
// Init plugin
|
||||
pluginName := os.Client.PluginName
|
||||
if pluginName == "" {
|
||||
pluginName = "ovs"
|
||||
}
|
||||
integrationBridge := os.Client.IntegrationBridge
|
||||
if integrationBridge == "" {
|
||||
integrationBridge = "br-int"
|
||||
}
|
||||
plugin, _ := plugins.GetNetworkPlugin(pluginName)
|
||||
if plugin == nil {
|
||||
return OpenStack{}, "", fmt.Errorf("plugin %q not found", pluginName)
|
||||
}
|
||||
plugin.Init(integrationBridge)
|
||||
os.Plugin = plugin
|
||||
|
||||
return os, cniVersion, nil
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
os, cniVersion, err := initOpenstack(args.StdinData)
|
||||
if err != nil {
|
||||
glog.Errorf("Init OpenStack failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get k8s args
|
||||
podName, podNamespace, err := getK8sArgs(args.Args)
|
||||
if err != nil {
|
||||
glog.Errorf("GetK8sArgs failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get tenantID
|
||||
tenantID, err := os.Client.GetTenantIDFromName(podNamespace)
|
||||
if err != nil {
|
||||
glog.Errorf("Get tenantID failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get networkID
|
||||
networkID, err := os.getNetworkIDByNamespace(podNamespace)
|
||||
if err != nil {
|
||||
glog.Errorf("Get networkID failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Build port name
|
||||
portName := util.BuildPortName(podNamespace, podName)
|
||||
|
||||
// Get port from openstack.
|
||||
port, err := os.Client.GetPort(portName)
|
||||
if err == util.ErrNotFound || port == nil {
|
||||
// Port not found, create a new one.
|
||||
portWithBinding, err := os.Client.CreatePort(networkID, tenantID, portName)
|
||||
if err != nil {
|
||||
glog.Errorf("CreatePort failed: %v", err)
|
||||
return err
|
||||
}
|
||||
port = &portWithBinding.Port
|
||||
} else if err != nil {
|
||||
glog.Errorf("GetPort failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
deviceOwner := fmt.Sprintf("compute:%s", getHostName())
|
||||
if port.DeviceOwner != deviceOwner {
|
||||
// Update hostname in order to make sure it is correct
|
||||
updateOpts := portsbinding.UpdateOpts{
|
||||
HostID: getHostName(),
|
||||
UpdateOptsBuilder: ports.UpdateOpts{
|
||||
DeviceOwner: deviceOwner,
|
||||
},
|
||||
}
|
||||
_, err = portsbinding.Update(os.Client.Network, port.ID, updateOpts).Extract()
|
||||
if err != nil {
|
||||
ports.Delete(os.Client.Network, port.ID)
|
||||
glog.Errorf("Update port %s failed: %v", portName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
glog.V(4).Infof("Pod %s's port is %v", podName, port)
|
||||
|
||||
// Get subnet and gateway
|
||||
subnet, err := os.Client.GetProviderSubnet(port.FixedIPs[0].SubnetID)
|
||||
if err != nil {
|
||||
glog.Errorf("Get info of subnet %s failed: %v", port.FixedIPs[0].SubnetID, err)
|
||||
if nil != ports.Delete(os.Client.Network, port.ID).ExtractErr() {
|
||||
glog.Warningf("Delete port %s failed", port.ID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Get network namespace.
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
// Setup interface for pod
|
||||
_, cidr, _ := net.ParseCIDR(subnet.Cidr)
|
||||
prefixSize, _ := cidr.Mask.Size()
|
||||
netnsName := path.Base(netns.Path())
|
||||
brInterface, conInterface, err := os.Plugin.SetupInterface(portName, args.ContainerID, port,
|
||||
fmt.Sprintf("%s/%d", port.FixedIPs[0].IPAddress, prefixSize),
|
||||
subnet.Gateway, args.IfName, netnsName)
|
||||
if err != nil {
|
||||
glog.Errorf("SetupInterface failed: %v", err)
|
||||
if nil != ports.Delete(os.Client.Network, port.ID).ExtractErr() {
|
||||
glog.Warningf("Delete port %s failed", port.ID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Collect the result in this variable - this is ultimately what gets "returned"
|
||||
// by this function by printing it to stdout.
|
||||
result := ¤t.Result{}
|
||||
// Populate container interface sandbox path
|
||||
conInterface.Sandbox = netns.Path()
|
||||
|
||||
// Populate result.Interfaces
|
||||
result.Interfaces = []*current.Interface{brInterface, conInterface}
|
||||
// Populate result.IPs
|
||||
ip := net.ParseIP(port.FixedIPs[0].IPAddress)
|
||||
ipCidr := net.IPNet{
|
||||
IP: ip,
|
||||
Mask: cidr.Mask,
|
||||
}
|
||||
gateway := net.ParseIP(subnet.Gateway)
|
||||
containerIPConfig := ¤t.IPConfig{
|
||||
Version: "4",
|
||||
Address: ipCidr,
|
||||
Gateway: gateway,
|
||||
}
|
||||
result.IPs = []*current.IPConfig{containerIPConfig}
|
||||
|
||||
// Print result to stdout, in the format defined by the requested cniVersion.
|
||||
return types.PrintResult(result, cniVersion)
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
os, _, err := initOpenstack(args.StdinData)
|
||||
if err != nil {
|
||||
glog.Errorf("Init OpenStack failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get k8s args
|
||||
podName, podNamespace, err := getK8sArgs(args.Args)
|
||||
if err != nil {
|
||||
glog.Errorf("GetK8sArgs failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Build port name
|
||||
portName := util.BuildPortName(podNamespace, podName)
|
||||
|
||||
// Get port from openstack
|
||||
port, err := os.Client.GetPort(portName)
|
||||
if err != nil {
|
||||
glog.Errorf("GetPort %s failed: %v", portName, err)
|
||||
return err
|
||||
}
|
||||
if port == nil {
|
||||
glog.Warningf("Port %s already deleted", portName)
|
||||
return nil
|
||||
}
|
||||
glog.V(4).Infof("Pod %s's port is %v", podName, port)
|
||||
|
||||
// Delete interface
|
||||
err = os.Plugin.DestroyInterface(portName, args.ContainerID, port)
|
||||
if err != nil {
|
||||
glog.Errorf("DestroyInterface for pod %s failed: %v", podName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete port from openstack
|
||||
err = os.Client.DeletePort(portName)
|
||||
if err != nil {
|
||||
glog.Errorf("DeletePort %s failed: %v", portName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddIgnoreUnknownArgs appends the 'IgnoreUnknown=1' option to CNI_ARGS before
|
||||
// calling the IPAM plugin. Otherwise, it will complain about the Kubernetes
|
||||
// arguments. See https://github.com/kubernetes/kubernetes/pull/24983
|
||||
func AddIgnoreUnknownArgs() error {
|
||||
cniArgs := "IgnoreUnknown=1"
|
||||
if os.Getenv("CNI_ARGS") != "" {
|
||||
cniArgs = fmt.Sprintf("%s;%s", cniArgs, os.Getenv("CNI_ARGS"))
|
||||
}
|
||||
return os.Setenv("CNI_ARGS", cniArgs)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Display the version on "-v", otherwise just delegate to the skel code.
|
||||
// Use a new flag set so as not to conflict with existing libraries which use "flag"
|
||||
flagSet := flag.NewFlagSet("kubestack", flag.ExitOnError)
|
||||
|
||||
version := flagSet.Bool("v", false, "Display version")
|
||||
err := flagSet.Parse(os.Args[1:])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if *version {
|
||||
fmt.Println(VERSION)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if err := AddIgnoreUnknownArgs(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
skel.PluginMain(cmdAdd, cmdDel, cniSpecVersion.All)
|
||||
}
|
276
pkg/kubestack/plugins/openvswitch/openvswitch.go
Normal file
276
pkg/kubestack/plugins/openvswitch/openvswitch.go
Normal file
@ -0,0 +1,276 @@
|
||||
package openvswitch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.openstack.org/openstack/stackube/pkg/kubestack/plugins"
|
||||
"git.openstack.org/openstack/stackube/pkg/util"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/golang/glog"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||
)
|
||||
|
||||
const (
|
||||
pluginName = "ovs"
|
||||
)
|
||||
|
||||
type OVSPlugin struct {
|
||||
IntegrationBridge string
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugins.RegisterNetworkPlugin(pluginName, func() (plugins.PluginInterface, error) {
|
||||
return NewOVSPlugin(), nil
|
||||
})
|
||||
}
|
||||
|
||||
func NewOVSPlugin() *OVSPlugin {
|
||||
return &OVSPlugin{}
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) Name() string {
|
||||
return pluginName
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) Init(integrationBridge string) error {
|
||||
p.IntegrationBridge = integrationBridge
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) buildBridgeName(portID string) string {
|
||||
return ("qbr" + portID)[:14]
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) buildTapName(portID string) string {
|
||||
return ("tap" + portID)[:14]
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) buildSandboxInterfaceName(portID string) (string, string) {
|
||||
return ("vib" + portID)[:14], ("vif" + portID)[:14]
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) buildVethName(portID string) (string, string) {
|
||||
return ("qvb" + portID)[:14], ("qvo" + portID)[:14]
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) SetupSandboxInterface(podName, podInfraContainerID string, port *ports.Port, ipcidr, gateway, ifName, netns string) (*current.Interface, error) {
|
||||
vibName, vifName := p.buildSandboxInterfaceName(port.ID)
|
||||
ret, err := util.RunCommand("ip", "link", "add", vibName, "type", "veth", "peer", "name", vifName)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bridge := p.buildBridgeName(port.ID)
|
||||
ret, err = util.RunCommand("brctl", "addif", bridge, vibName)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "link", "set", "dev", vifName, "address", port.MACAddress)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "link", "set", vifName, "netns", netns)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "netns", "exec", netns, "ip", "link", "set", vifName, "down")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "netns", "exec", netns, "ip", "link", "set", vifName, "name", ifName)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "netns", "exec", netns, "ip", "link", "set", ifName, "up")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "netns", "exec", netns, "ip", "addr", "add", "dev", ifName, ipcidr)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "netns", "exec", netns, "ip", "route", "add", "default", "via", gateway)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "link", "set", "dev", vibName, "up")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = util.RunCommand("rm", "-f", fmt.Sprintf("/var/run/netns/%s", netns))
|
||||
if err != nil {
|
||||
glog.V(5).Infof("Warning: remove netns symlink failed: %v", err)
|
||||
}
|
||||
|
||||
return ¤t.Interface{
|
||||
Name: p.buildTapName(port.ID),
|
||||
Mac: port.MACAddress,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) SetupOVSInterface(podName, podInfraContainerID string, port *ports.Port) (*current.Interface, error) {
|
||||
qvb, qvo := p.buildVethName(port.ID)
|
||||
ret, err := util.RunCommand("ip", "link", "add", qvb, "type", "veth", "peer", "name", qvo)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bridge := p.buildBridgeName(port.ID)
|
||||
ret, err = util.RunCommand("brctl", "addbr", bridge)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "link", "set", qvb, "up")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "link", "set", qvo, "up")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ip", "link", "set", bridge, "up")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("brctl", "addif", bridge, qvb)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err = util.RunCommand("ovs-vsctl", "-vconsole:off", "--", "--if-exists", "del-port",
|
||||
qvo, "--", "add-port", p.IntegrationBridge, qvo, "--", "set", "Interface", qvo,
|
||||
fmt.Sprintf("external_ids:attached-mac=%s", port.MACAddress),
|
||||
fmt.Sprintf("external_ids:iface-id=%s", port.ID),
|
||||
fmt.Sprintf("external_ids:vm-id=%s", podName),
|
||||
"external_ids:iface-status=active")
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get bridge mac
|
||||
ret, err = util.RunCommand("ip", "link", "show", bridge)
|
||||
if err != nil {
|
||||
glog.Warningf("SetupInterface failed, ret:%s, error:%v", strings.Join(ret, "\n"), err)
|
||||
p.DestroyInterface(podName, podInfraContainerID, port)
|
||||
return nil, err
|
||||
}
|
||||
mac := ret[1][15:32]
|
||||
return ¤t.Interface{
|
||||
Name: bridge,
|
||||
Mac: mac,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) SetupInterface(podName, podInfraContainerID string, port *ports.Port, ipcidr, gateway, ifName, netns string) (*current.Interface, *current.Interface, error) {
|
||||
brInterface, err := p.SetupOVSInterface(podName, podInfraContainerID, port)
|
||||
if err != nil {
|
||||
glog.Errorf("SetupOVSInterface failed: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
conInterface, err := p.SetupSandboxInterface(podName, podInfraContainerID, port, ipcidr, gateway, ifName, netns)
|
||||
if err != nil {
|
||||
glog.Errorf("SetupSandboxInterface failed: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("SetupInterface for %s done", podName)
|
||||
return brInterface, conInterface, nil
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) destroyOVSInterface(podName, portID string) error {
|
||||
_, qvo := p.buildVethName(portID)
|
||||
bridge := p.buildBridgeName(portID)
|
||||
|
||||
output, err := util.RunCommand("ovs-vsctl", "-vconsole:off", "--if-exists", "del-port", qvo)
|
||||
if err != nil {
|
||||
glog.Warningf("Warning: ovs del-port %s failed: %v, %v", qvo, output, err)
|
||||
}
|
||||
|
||||
output, err = util.RunCommand("ip", "link", "set", "dev", qvo, "down")
|
||||
if err != nil {
|
||||
glog.Warningf("Warning: set dev %s down failed: %v, %v", qvo, output, err)
|
||||
}
|
||||
|
||||
output, err = util.RunCommand("ip", "link", "delete", "dev", qvo)
|
||||
if err != nil {
|
||||
glog.Warningf("Warning: delete dev %s failed: %v, %v", qvo, output, err)
|
||||
}
|
||||
|
||||
output, err = util.RunCommand("ip", "link", "set", "dev", bridge, "down")
|
||||
if err != nil {
|
||||
glog.Warningf("Warning: set bridge %s down failed: %v, %v", bridge, output, err)
|
||||
}
|
||||
|
||||
output, err = util.RunCommand("brctl", "delbr", bridge)
|
||||
if err != nil {
|
||||
glog.Warningf("Warning: delete bridge %s failed: %v, %v", bridge, output, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) destroySandboxInterface(podName, podInfraContainerID, portID string) error {
|
||||
vibName, _ := p.buildSandboxInterfaceName(portID)
|
||||
_, err := util.RunCommand("ip", "link", "delete", vibName)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("Warning: DestroyInterface failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *OVSPlugin) DestroyInterface(podName, podInfraContainerID string, port *ports.Port) error {
|
||||
p.destroyOVSInterface(podName, port.ID)
|
||||
p.destroySandboxInterface(podName, podInfraContainerID, port.ID)
|
||||
glog.V(4).Infof("DestroyInterface for %s done", podName)
|
||||
return nil
|
||||
}
|
73
pkg/kubestack/plugins/plugins.go
Normal file
73
pkg/kubestack/plugins/plugins.go
Normal file
@ -0,0 +1,73 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/golang/glog"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||
)
|
||||
|
||||
type PluginInterface interface {
|
||||
SetupInterface(podName, podInfraContainerID string, port *ports.Port, ipcidr, gateway, ifName, netns string) (*current.Interface, *current.Interface, error)
|
||||
DestroyInterface(podName, podInfraContainerID string, port *ports.Port) error
|
||||
Init(integrationBridge string) error
|
||||
}
|
||||
|
||||
// Factory is a function that returns a networkplugin.Interface.
|
||||
// The config parameter provides an io.Reader handler to the factory in
|
||||
// order to load specific configurations. If no configuration is provided
|
||||
// the parameter is nil.
|
||||
type Factory func() (PluginInterface, error)
|
||||
|
||||
// All registered network plugins.
|
||||
var pluginsMutex sync.Mutex
|
||||
var plugins = make(map[string]Factory)
|
||||
|
||||
// RegisterNetworkPlugin registers a networkplugin.Factory by name. This
|
||||
// is expected to happen during app startup.
|
||||
func RegisterNetworkPlugin(name string, networkPlugin Factory) {
|
||||
pluginsMutex.Lock()
|
||||
defer pluginsMutex.Unlock()
|
||||
if _, found := plugins[name]; found {
|
||||
glog.Fatalf("Network plugin %q was registered twice", name)
|
||||
}
|
||||
glog.V(1).Infof("Registered network plugin %q", name)
|
||||
plugins[name] = networkPlugin
|
||||
}
|
||||
|
||||
// GetNetworkPlugin creates an instance of the named network plugin, or nil if
|
||||
// the name is not known. The error return is only used if the named plugin
|
||||
// was known but failed to initialize.
|
||||
func GetNetworkPlugin(name string) (PluginInterface, error) {
|
||||
pluginsMutex.Lock()
|
||||
defer pluginsMutex.Unlock()
|
||||
f, found := plugins[name]
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
return f()
|
||||
}
|
||||
|
||||
// InitNetworkPlugin creates an instance of the named networkPlugin plugin.
|
||||
func InitNetworkPlugin(name string) (PluginInterface, error) {
|
||||
var networkPlugin PluginInterface
|
||||
|
||||
if name == "" {
|
||||
glog.Info("No network plugin specified.")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
networkPlugin, err = GetNetworkPlugin(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not init networkPlugin plugin %q: %v", name, err)
|
||||
}
|
||||
if networkPlugin == nil {
|
||||
return nil, fmt.Errorf("unknown networkPlugin plugin %q", name)
|
||||
}
|
||||
|
||||
return networkPlugin, nil
|
||||
}
|
21
pkg/kubestack/types/types.go
Normal file
21
pkg/kubestack/types/types.go
Normal file
@ -0,0 +1,21 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
KubestackConfig string `json:"kubestack-config"`
|
||||
}
|
||||
|
||||
// K8sArgs is the valid CNI_ARGS used for Kubernetes
|
||||
type K8sArgs struct {
|
||||
types.CommonArgs
|
||||
IP net.IP
|
||||
K8S_POD_NAME types.UnmarshallableString
|
||||
K8S_POD_NAMESPACE types.UnmarshallableString
|
||||
K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString
|
||||
}
|
@ -2,22 +2,25 @@ package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
drivertypes "git.openstack.org/openstack/stackube/pkg/openstack/types"
|
||||
"git.openstack.org/openstack/stackube/pkg/util"
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/golang/glog"
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
|
||||
"fmt"
|
||||
|
||||
drivertypes "git.openstack.org/openstack/stackube/pkg/openstack/types"
|
||||
gcfg "gopkg.in/gcfg.v1"
|
||||
)
|
||||
|
||||
@ -26,7 +29,7 @@ const (
|
||||
|
||||
podNamePrefix = "kube"
|
||||
securitygroupName = "kube-securitygroup-default"
|
||||
hostnameMaxLen = 63
|
||||
HostnameMaxLen = 63
|
||||
|
||||
// Service affinities
|
||||
ServiceAffinityNone = "None"
|
||||
@ -41,11 +44,18 @@ var (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Identity *gophercloud.ServiceClient
|
||||
Provider *gophercloud.ProviderClient
|
||||
Network *gophercloud.ServiceClient
|
||||
Region string
|
||||
ExtNetID string
|
||||
Identity *gophercloud.ServiceClient
|
||||
Provider *gophercloud.ProviderClient
|
||||
Network *gophercloud.ServiceClient
|
||||
Region string
|
||||
ExtNetID string
|
||||
PluginName string
|
||||
IntegrationBridge string
|
||||
}
|
||||
|
||||
type PluginOpts struct {
|
||||
PluginName string `gcfg:"plugin-name"`
|
||||
IntegrationBridge string `gcfg:"integration-bridge"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@ -57,6 +67,7 @@ type Config struct {
|
||||
Region string `gcfg:"region"`
|
||||
ExtNetID string `gcfg:"ext-net-id"`
|
||||
}
|
||||
Plugin PluginOpts
|
||||
}
|
||||
|
||||
func toAuthOptions(cfg Config) gophercloud.AuthOptions {
|
||||
@ -108,11 +119,13 @@ func NewClient(config string) (*Client, error) {
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
Identity: identity,
|
||||
Provider: provider,
|
||||
Network: network,
|
||||
Region: cfg.Global.Region,
|
||||
ExtNetID: cfg.Global.ExtNetID,
|
||||
Identity: identity,
|
||||
Provider: provider,
|
||||
Network: network,
|
||||
Region: cfg.Global.Region,
|
||||
ExtNetID: cfg.Global.ExtNetID,
|
||||
PluginName: cfg.Plugin.PluginName,
|
||||
IntegrationBridge: cfg.Plugin.IntegrationBridge,
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
@ -243,6 +256,12 @@ func reasonForError(err error) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Get tenant's network by tenantID(tenant and network are one to one mapping in stackube)
|
||||
func (os *Client) GetOpenStackNetworkByTenantID(tenantID string) (*networks.Network, error) {
|
||||
opts := networks.ListOpts{TenantID: tenantID}
|
||||
return os.getOpenStackNetwork(&opts)
|
||||
}
|
||||
|
||||
// Get openstack network by id
|
||||
func (os *Client) getOpenStackNetworkByID(id string) (*networks.Network, error) {
|
||||
opts := networks.ListOpts{ID: id}
|
||||
@ -280,7 +299,7 @@ func (os *Client) getOpenStackNetwork(opts *networks.ListOpts) (*networks.Networ
|
||||
}
|
||||
|
||||
// Get provider subnet by id
|
||||
func (os *Client) getProviderSubnet(osSubnetID string) (*drivertypes.Subnet, error) {
|
||||
func (os *Client) GetProviderSubnet(osSubnetID string) (*drivertypes.Subnet, error) {
|
||||
s, err := subnets.Get(os.Network, osSubnetID).Extract()
|
||||
if err != nil {
|
||||
glog.Errorf("Get openstack subnet failed: %v", err)
|
||||
@ -339,7 +358,7 @@ func (os *Client) OSNetworktoProviderNetwork(osNetwork *networks.Network) (*driv
|
||||
providerNetwork.TenantID = osNetwork.TenantID
|
||||
|
||||
for _, subnetID := range osNetwork.Subnets {
|
||||
s, err := os.getProviderSubnet(subnetID)
|
||||
s, err := os.GetProviderSubnet(subnetID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -580,3 +599,204 @@ func (os *Client) CheckTenantID(tenantID string) (bool, error) {
|
||||
|
||||
return found, err
|
||||
}
|
||||
|
||||
func (os *Client) GetPort(name string) (*ports.Port, error) {
|
||||
opts := ports.ListOpts{Name: name}
|
||||
pager := ports.List(os.Network, opts)
|
||||
|
||||
var port *ports.Port
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
portList, err := ports.ExtractPorts(page)
|
||||
if err != nil {
|
||||
glog.Errorf("Get openstack ports error: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(portList) > 1 {
|
||||
return false, ErrMultipleResults
|
||||
}
|
||||
|
||||
if len(portList) == 0 {
|
||||
return false, ErrNotFound
|
||||
}
|
||||
|
||||
port = &portList[0]
|
||||
|
||||
return true, err
|
||||
})
|
||||
|
||||
return port, err
|
||||
}
|
||||
|
||||
func getHostName() string {
|
||||
host, err := os.Hostname()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
||||
|
||||
func (os *Client) ensureSecurityGroup(tenantID string) (string, error) {
|
||||
var securitygroup *groups.SecGroup
|
||||
|
||||
opts := groups.ListOpts{
|
||||
TenantID: tenantID,
|
||||
Name: securitygroupName,
|
||||
}
|
||||
pager := groups.List(os.Network, opts)
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
sg, err := groups.ExtractGroups(page)
|
||||
if err != nil {
|
||||
glog.Errorf("Get openstack securitygroups error: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(sg) > 0 {
|
||||
securitygroup = &sg[0]
|
||||
}
|
||||
|
||||
return true, err
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If securitygroup doesn't exist, create a new one
|
||||
if securitygroup == nil {
|
||||
securitygroup, err = groups.Create(os.Network, groups.CreateOpts{
|
||||
Name: securitygroupName,
|
||||
TenantID: tenantID,
|
||||
}).Extract()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
var secGroupsRules int
|
||||
listopts := rules.ListOpts{
|
||||
TenantID: tenantID,
|
||||
Direction: string(rules.DirIngress),
|
||||
SecGroupID: securitygroup.ID,
|
||||
}
|
||||
rulesPager := rules.List(os.Network, listopts)
|
||||
err = rulesPager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
r, err := rules.ExtractRules(page)
|
||||
if err != nil {
|
||||
glog.Errorf("Get openstack securitygroup rules error: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
secGroupsRules = len(r)
|
||||
|
||||
return true, err
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// create new rules
|
||||
if secGroupsRules == 0 {
|
||||
// create egress rule
|
||||
_, err = rules.Create(os.Network, rules.CreateOpts{
|
||||
TenantID: tenantID,
|
||||
SecGroupID: securitygroup.ID,
|
||||
Direction: rules.DirEgress,
|
||||
EtherType: rules.EtherType4,
|
||||
}).Extract()
|
||||
|
||||
// create ingress rule
|
||||
_, err := rules.Create(os.Network, rules.CreateOpts{
|
||||
TenantID: tenantID,
|
||||
SecGroupID: securitygroup.ID,
|
||||
Direction: rules.DirIngress,
|
||||
EtherType: rules.EtherType4,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return securitygroup.ID, nil
|
||||
}
|
||||
|
||||
// Create an port
|
||||
func (os *Client) CreatePort(networkID, tenantID, portName string) (*portsbinding.Port, error) {
|
||||
securitygroup, err := os.ensureSecurityGroup(tenantID)
|
||||
if err != nil {
|
||||
glog.Errorf("EnsureSecurityGroup failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := portsbinding.CreateOpts{
|
||||
HostID: getHostName(),
|
||||
CreateOptsBuilder: ports.CreateOpts{
|
||||
NetworkID: networkID,
|
||||
Name: portName,
|
||||
AdminStateUp: &adminStateUp,
|
||||
TenantID: tenantID,
|
||||
DeviceID: uuid.Generate().String(),
|
||||
DeviceOwner: fmt.Sprintf("compute:%s", getHostName()),
|
||||
SecurityGroups: []string{securitygroup},
|
||||
},
|
||||
}
|
||||
|
||||
port, err := portsbinding.Create(os.Network, opts).Extract()
|
||||
if err != nil {
|
||||
glog.Errorf("Create port %s failed: %v", portName, err)
|
||||
return nil, err
|
||||
}
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// List all ports in the network
|
||||
func (os *Client) ListPorts(networkID, deviceOwner string) ([]ports.Port, error) {
|
||||
var results []ports.Port
|
||||
opts := ports.ListOpts{
|
||||
NetworkID: networkID,
|
||||
DeviceOwner: deviceOwner,
|
||||
}
|
||||
pager := ports.List(os.Network, opts)
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
portList, err := ports.ExtractPorts(page)
|
||||
if err != nil {
|
||||
glog.Errorf("Get openstack ports error: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, port := range portList {
|
||||
results = append(results, port)
|
||||
}
|
||||
|
||||
return true, err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Delete port by portName
|
||||
func (os *Client) DeletePort(portName string) error {
|
||||
port, err := os.GetPort(portName)
|
||||
if err == util.ErrNotFound {
|
||||
glog.V(4).Infof("Port %s already deleted", portName)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
glog.Errorf("Get openstack port %s failed: %v", portName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if port != nil {
|
||||
err := ports.Delete(os.Network, port.ID).ExtractErr()
|
||||
if err != nil {
|
||||
glog.Errorf("Delete openstack port %s failed: %v", portName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
39
pkg/util/exec.go
Normal file
39
pkg/util/exec.go
Normal file
@ -0,0 +1,39 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func absPath(cmd string) (string, error) {
|
||||
cmdAbsPath, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cmdAbsPath, nil
|
||||
}
|
||||
|
||||
func buildCommand(cmd string, args ...string) (*exec.Cmd, error) {
|
||||
cmdAbsPath, err := absPath(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
command := exec.Command(cmdAbsPath)
|
||||
command.Args = append(command.Args, args...)
|
||||
return command, nil
|
||||
}
|
||||
|
||||
func RunCommand(cmd string, args ...string) ([]string, error) {
|
||||
command, err := buildCommand(cmd, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
return []string{string(output)}, err
|
||||
}
|
||||
return strings.Split(strings.TrimSpace(string(output)), "\n"), nil
|
||||
}
|
@ -18,3 +18,7 @@ func BuildNetworkName(namespace, name string) string {
|
||||
func BuildLoadBalancerName(namespace, name string) string {
|
||||
return namePrefix + "_" + namespace + "_" + name
|
||||
}
|
||||
|
||||
func BuildPortName(namespace, podName string) string {
|
||||
return namePrefix + "_" + namespace + "_" + podName
|
||||
}
|
||||
|
202
vendor/github.com/containernetworking/cni/LICENSE
generated
vendored
Normal file
202
vendor/github.com/containernetworking/cni/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
|
228
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
Normal file
228
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
// Copyright 2014-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 skel provides skeleton code for a CNI plugin.
|
||||
// In particular, it implements argument parsing and validation.
|
||||
package skel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
// CmdArgs captures all the arguments passed in to the plugin
|
||||
// via both env vars and stdin
|
||||
type CmdArgs struct {
|
||||
ContainerID string
|
||||
Netns string
|
||||
IfName string
|
||||
Args string
|
||||
Path string
|
||||
StdinData []byte
|
||||
}
|
||||
|
||||
type dispatcher struct {
|
||||
Getenv func(string) string
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
|
||||
ConfVersionDecoder version.ConfigDecoder
|
||||
VersionReconciler version.Reconciler
|
||||
}
|
||||
|
||||
type reqForCmdEntry map[string]bool
|
||||
|
||||
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
var cmd, contID, netns, ifName, args, path string
|
||||
|
||||
vars := []struct {
|
||||
name string
|
||||
val *string
|
||||
reqForCmd reqForCmdEntry
|
||||
}{
|
||||
{
|
||||
"CNI_COMMAND",
|
||||
&cmd,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_CONTAINERID",
|
||||
&contID,
|
||||
reqForCmdEntry{
|
||||
"ADD": false,
|
||||
"DEL": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_NETNS",
|
||||
&netns,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_IFNAME",
|
||||
&ifName,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_ARGS",
|
||||
&args,
|
||||
reqForCmdEntry{
|
||||
"ADD": false,
|
||||
"DEL": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_PATH",
|
||||
&path,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
argsMissing := false
|
||||
for _, v := range vars {
|
||||
*v.val = t.Getenv(v.name)
|
||||
if *v.val == "" {
|
||||
if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" {
|
||||
fmt.Fprintf(t.Stderr, "%v env variable missing\n", v.name)
|
||||
argsMissing = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if argsMissing {
|
||||
return "", nil, fmt.Errorf("required env variables missing")
|
||||
}
|
||||
|
||||
stdinData, err := ioutil.ReadAll(t.Stdin)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error reading from stdin: %v", err)
|
||||
}
|
||||
|
||||
cmdArgs := &CmdArgs{
|
||||
ContainerID: contID,
|
||||
Netns: netns,
|
||||
IfName: ifName,
|
||||
Args: args,
|
||||
Path: path,
|
||||
StdinData: stdinData,
|
||||
}
|
||||
return cmd, cmdArgs, nil
|
||||
}
|
||||
|
||||
func createTypedError(f string, args ...interface{}) *types.Error {
|
||||
return &types.Error{
|
||||
Code: 100,
|
||||
Msg: fmt.Sprintf(f, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error {
|
||||
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)
|
||||
if verErr != nil {
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "incompatible CNI versions",
|
||||
Details: verErr.Details(),
|
||||
}
|
||||
}
|
||||
return toCall(cmdArgs)
|
||||
}
|
||||
|
||||
func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error {
|
||||
cmd, cmdArgs, err := t.getCmdArgsFromEnv()
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "ADD":
|
||||
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd)
|
||||
case "DEL":
|
||||
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel)
|
||||
case "VERSION":
|
||||
err = versionInfo.Encode(t.Stdout)
|
||||
default:
|
||||
return createTypedError("unknown CNI_COMMAND: %v", cmd)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if e, ok := err.(*types.Error); ok {
|
||||
// don't wrap Error in Error
|
||||
return e
|
||||
}
|
||||
return createTypedError(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PluginMainWithError is the core "main" for a plugin. It accepts
|
||||
// callback functions for add and del CNI commands and returns an error.
|
||||
//
|
||||
// The caller must also specify what CNI spec versions the plugin supports.
|
||||
//
|
||||
// It is the responsibility of the caller to check for non-nil error return.
|
||||
//
|
||||
// For a plugin to comply with the CNI spec, it must print any error to stdout
|
||||
// as JSON and then exit with nonzero status code.
|
||||
//
|
||||
// To let this package automatically handle errors and call os.Exit(1) for you,
|
||||
// use PluginMain() instead.
|
||||
func PluginMainWithError(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error {
|
||||
return (&dispatcher{
|
||||
Getenv: os.Getenv,
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}).pluginMain(cmdAdd, cmdDel, versionInfo)
|
||||
}
|
||||
|
||||
// PluginMain is the core "main" for a plugin which includes automatic error handling.
|
||||
//
|
||||
// The caller must also specify what CNI spec versions the plugin supports.
|
||||
//
|
||||
// When an error occurs in either cmdAdd or cmdDel, PluginMain will print the error
|
||||
// as JSON to stdout and call os.Exit(1).
|
||||
//
|
||||
// To have more control over error handling, use PluginMainWithError() instead.
|
||||
func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) {
|
||||
if e := PluginMainWithError(cmdAdd, cmdDel, versionInfo); e != nil {
|
||||
if err := e.Print(); err != nil {
|
||||
log.Print("Error writing error JSON to stdout: ", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
135
vendor/github.com/containernetworking/cni/pkg/types/020/types.go
generated
vendored
Normal file
135
vendor/github.com/containernetworking/cni/pkg/types/020/types.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
// 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 types020
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
const ImplementedSpecVersion string = "0.2.0"
|
||||
|
||||
var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion}
|
||||
|
||||
// Compatibility types for CNI version 0.1.0 and 0.2.0
|
||||
|
||||
func NewResult(data []byte) (types.Result, error) {
|
||||
result := &Result{}
|
||||
if err := json.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetResult(r types.Result) (*Result, error) {
|
||||
// We expect version 0.1.0/0.2.0 results
|
||||
result020, err := r.GetAsVersion(ImplementedSpecVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, ok := result020.(*Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert result")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||
type Result struct {
|
||||
CNIVersion string `json:"cniVersion,omitempty"`
|
||||
IP4 *IPConfig `json:"ip4,omitempty"`
|
||||
IP6 *IPConfig `json:"ip6,omitempty"`
|
||||
DNS types.DNS `json:"dns,omitempty"`
|
||||
}
|
||||
|
||||
func (r *Result) Version() string {
|
||||
return ImplementedSpecVersion
|
||||
}
|
||||
|
||||
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
||||
for _, supportedVersion := range SupportedVersions {
|
||||
if version == supportedVersion {
|
||||
r.CNIVersion = version
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version)
|
||||
}
|
||||
|
||||
func (r *Result) Print() error {
|
||||
data, err := json.MarshalIndent(r, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stdout.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
|
||||
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||
func (r *Result) String() string {
|
||||
var str string
|
||||
if r.IP4 != nil {
|
||||
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
|
||||
}
|
||||
if r.IP6 != nil {
|
||||
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
|
||||
}
|
||||
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||
}
|
||||
|
||||
// IPConfig contains values necessary to configure an interface
|
||||
type IPConfig struct {
|
||||
IP net.IPNet
|
||||
Gateway net.IP
|
||||
Routes []types.Route
|
||||
}
|
||||
|
||||
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||
// for our custom IPNet type
|
||||
|
||||
// JSON (un)marshallable types
|
||||
type ipConfig struct {
|
||||
IP types.IPNet `json:"ip"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
Routes []types.Route `json:"routes,omitempty"`
|
||||
}
|
||||
|
||||
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
||||
ipc := ipConfig{
|
||||
IP: types.IPNet(c.IP),
|
||||
Gateway: c.Gateway,
|
||||
Routes: c.Routes,
|
||||
}
|
||||
|
||||
return json.Marshal(ipc)
|
||||
}
|
||||
|
||||
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||
ipc := ipConfig{}
|
||||
if err := json.Unmarshal(data, &ipc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.IP = net.IPNet(ipc.IP)
|
||||
c.Gateway = ipc.Gateway
|
||||
c.Routes = ipc.Routes
|
||||
return nil
|
||||
}
|
101
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
Normal file
101
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// 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.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UnmarshallableBool typedef for builtin bool
|
||||
// because builtin type's methods can't be declared
|
||||
type UnmarshallableBool bool
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
// Returns boolean true if the string is "1" or "[Tt]rue"
|
||||
// Returns boolean false if the string is "0" or "[Ff]alse"
|
||||
func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
|
||||
s := strings.ToLower(string(data))
|
||||
switch s {
|
||||
case "1", "true":
|
||||
*b = true
|
||||
case "0", "false":
|
||||
*b = false
|
||||
default:
|
||||
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshallableString typedef for builtin string
|
||||
type UnmarshallableString string
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
// Returns the string
|
||||
func (s *UnmarshallableString) UnmarshalText(data []byte) error {
|
||||
*s = UnmarshallableString(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommonArgs contains the IgnoreUnknown argument
|
||||
// and must be embedded by all Arg structs
|
||||
type CommonArgs struct {
|
||||
IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"`
|
||||
}
|
||||
|
||||
// GetKeyField is a helper function to receive Values
|
||||
// Values that represent a pointer to a struct
|
||||
func GetKeyField(keyString string, v reflect.Value) reflect.Value {
|
||||
return v.Elem().FieldByName(keyString)
|
||||
}
|
||||
|
||||
// LoadArgs parses args from a string in the form "K=V;K2=V2;..."
|
||||
func LoadArgs(args string, container interface{}) error {
|
||||
if args == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
containerValue := reflect.ValueOf(container)
|
||||
|
||||
pairs := strings.Split(args, ";")
|
||||
unknownArgs := []string{}
|
||||
for _, pair := range pairs {
|
||||
kv := strings.Split(pair, "=")
|
||||
if len(kv) != 2 {
|
||||
return fmt.Errorf("ARGS: invalid pair %q", pair)
|
||||
}
|
||||
keyString := kv[0]
|
||||
valueString := kv[1]
|
||||
keyField := GetKeyField(keyString, containerValue)
|
||||
if !keyField.IsValid() {
|
||||
unknownArgs = append(unknownArgs, pair)
|
||||
continue
|
||||
}
|
||||
|
||||
u := keyField.Addr().Interface().(encoding.TextUnmarshaler)
|
||||
err := u.UnmarshalText([]byte(valueString))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err)
|
||||
}
|
||||
}
|
||||
|
||||
isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool()
|
||||
if len(unknownArgs) > 0 && !isIgnoreUnknown {
|
||||
return fmt.Errorf("ARGS: unknown args %q", unknownArgs)
|
||||
}
|
||||
return nil
|
||||
}
|
300
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
Normal file
300
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
Normal file
@ -0,0 +1,300 @@
|
||||
// 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 current
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/020"
|
||||
)
|
||||
|
||||
const ImplementedSpecVersion string = "0.3.1"
|
||||
|
||||
var SupportedVersions = []string{"0.3.0", ImplementedSpecVersion}
|
||||
|
||||
func NewResult(data []byte) (types.Result, error) {
|
||||
result := &Result{}
|
||||
if err := json.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetResult(r types.Result) (*Result, error) {
|
||||
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, ok := resultCurrent.(*Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert result")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var resultConverters = []struct {
|
||||
versions []string
|
||||
convert func(types.Result) (*Result, error)
|
||||
}{
|
||||
{types020.SupportedVersions, convertFrom020},
|
||||
{SupportedVersions, convertFrom030},
|
||||
}
|
||||
|
||||
func convertFrom020(result types.Result) (*Result, error) {
|
||||
oldResult, err := types020.GetResult(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newResult := &Result{
|
||||
CNIVersion: ImplementedSpecVersion,
|
||||
DNS: oldResult.DNS,
|
||||
Routes: []*types.Route{},
|
||||
}
|
||||
|
||||
if oldResult.IP4 != nil {
|
||||
newResult.IPs = append(newResult.IPs, &IPConfig{
|
||||
Version: "4",
|
||||
Address: oldResult.IP4.IP,
|
||||
Gateway: oldResult.IP4.Gateway,
|
||||
})
|
||||
for _, route := range oldResult.IP4.Routes {
|
||||
gw := route.GW
|
||||
if gw == nil {
|
||||
gw = oldResult.IP4.Gateway
|
||||
}
|
||||
newResult.Routes = append(newResult.Routes, &types.Route{
|
||||
Dst: route.Dst,
|
||||
GW: gw,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if oldResult.IP6 != nil {
|
||||
newResult.IPs = append(newResult.IPs, &IPConfig{
|
||||
Version: "6",
|
||||
Address: oldResult.IP6.IP,
|
||||
Gateway: oldResult.IP6.Gateway,
|
||||
})
|
||||
for _, route := range oldResult.IP6.Routes {
|
||||
gw := route.GW
|
||||
if gw == nil {
|
||||
gw = oldResult.IP6.Gateway
|
||||
}
|
||||
newResult.Routes = append(newResult.Routes, &types.Route{
|
||||
Dst: route.Dst,
|
||||
GW: gw,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(newResult.IPs) == 0 {
|
||||
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
||||
}
|
||||
|
||||
return newResult, nil
|
||||
}
|
||||
|
||||
func convertFrom030(result types.Result) (*Result, error) {
|
||||
newResult, ok := result.(*Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert result")
|
||||
}
|
||||
newResult.CNIVersion = ImplementedSpecVersion
|
||||
return newResult, nil
|
||||
}
|
||||
|
||||
func NewResultFromResult(result types.Result) (*Result, error) {
|
||||
version := result.Version()
|
||||
for _, converter := range resultConverters {
|
||||
for _, supportedVersion := range converter.versions {
|
||||
if version == supportedVersion {
|
||||
return converter.convert(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported CNI result22 version %q", version)
|
||||
}
|
||||
|
||||
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||
type Result struct {
|
||||
CNIVersion string `json:"cniVersion,omitempty"`
|
||||
Interfaces []*Interface `json:"interfaces,omitempty"`
|
||||
IPs []*IPConfig `json:"ips,omitempty"`
|
||||
Routes []*types.Route `json:"routes,omitempty"`
|
||||
DNS types.DNS `json:"dns,omitempty"`
|
||||
}
|
||||
|
||||
// Convert to the older 0.2.0 CNI spec Result type
|
||||
func (r *Result) convertTo020() (*types020.Result, error) {
|
||||
oldResult := &types020.Result{
|
||||
CNIVersion: types020.ImplementedSpecVersion,
|
||||
DNS: r.DNS,
|
||||
}
|
||||
|
||||
for _, ip := range r.IPs {
|
||||
// Only convert the first IP address of each version as 0.2.0
|
||||
// and earlier cannot handle multiple IP addresses
|
||||
if ip.Version == "4" && oldResult.IP4 == nil {
|
||||
oldResult.IP4 = &types020.IPConfig{
|
||||
IP: ip.Address,
|
||||
Gateway: ip.Gateway,
|
||||
}
|
||||
} else if ip.Version == "6" && oldResult.IP6 == nil {
|
||||
oldResult.IP6 = &types020.IPConfig{
|
||||
IP: ip.Address,
|
||||
Gateway: ip.Gateway,
|
||||
}
|
||||
}
|
||||
|
||||
if oldResult.IP4 != nil && oldResult.IP6 != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, route := range r.Routes {
|
||||
is4 := route.Dst.IP.To4() != nil
|
||||
if is4 && oldResult.IP4 != nil {
|
||||
oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{
|
||||
Dst: route.Dst,
|
||||
GW: route.GW,
|
||||
})
|
||||
} else if !is4 && oldResult.IP6 != nil {
|
||||
oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{
|
||||
Dst: route.Dst,
|
||||
GW: route.GW,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if oldResult.IP4 == nil && oldResult.IP6 == nil {
|
||||
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
||||
}
|
||||
|
||||
return oldResult, nil
|
||||
}
|
||||
|
||||
func (r *Result) Version() string {
|
||||
return ImplementedSpecVersion
|
||||
}
|
||||
|
||||
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
||||
switch version {
|
||||
case "0.3.0", ImplementedSpecVersion:
|
||||
r.CNIVersion = version
|
||||
return r, nil
|
||||
case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
|
||||
return r.convertTo020()
|
||||
}
|
||||
return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version)
|
||||
}
|
||||
|
||||
func (r *Result) Print() error {
|
||||
data, err := json.MarshalIndent(r, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stdout.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
|
||||
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||
func (r *Result) String() string {
|
||||
var str string
|
||||
if len(r.Interfaces) > 0 {
|
||||
str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
|
||||
}
|
||||
if len(r.IPs) > 0 {
|
||||
str += fmt.Sprintf("IP:%+v, ", r.IPs)
|
||||
}
|
||||
if len(r.Routes) > 0 {
|
||||
str += fmt.Sprintf("Routes:%+v, ", r.Routes)
|
||||
}
|
||||
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||
}
|
||||
|
||||
// Convert this old version result to the current CNI version result
|
||||
func (r *Result) Convert() (*Result, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Interface contains values about the created interfaces
|
||||
type Interface struct {
|
||||
Name string `json:"name"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
Sandbox string `json:"sandbox,omitempty"`
|
||||
}
|
||||
|
||||
func (i *Interface) String() string {
|
||||
return fmt.Sprintf("%+v", *i)
|
||||
}
|
||||
|
||||
// Int returns a pointer to the int value passed in. Used to
|
||||
// set the IPConfig.Interface field.
|
||||
func Int(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
// IPConfig contains values necessary to configure an IP address on an interface
|
||||
type IPConfig struct {
|
||||
// IP version, either "4" or "6"
|
||||
Version string
|
||||
// Index into Result structs Interfaces list
|
||||
Interface *int
|
||||
Address net.IPNet
|
||||
Gateway net.IP
|
||||
}
|
||||
|
||||
func (i *IPConfig) String() string {
|
||||
return fmt.Sprintf("%+v", *i)
|
||||
}
|
||||
|
||||
// JSON (un)marshallable types
|
||||
type ipConfig struct {
|
||||
Version string `json:"version"`
|
||||
Interface *int `json:"interface,omitempty"`
|
||||
Address types.IPNet `json:"address"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
}
|
||||
|
||||
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
||||
ipc := ipConfig{
|
||||
Version: c.Version,
|
||||
Interface: c.Interface,
|
||||
Address: types.IPNet(c.Address),
|
||||
Gateway: c.Gateway,
|
||||
}
|
||||
|
||||
return json.Marshal(ipc)
|
||||
}
|
||||
|
||||
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||
ipc := ipConfig{}
|
||||
if err := json.Unmarshal(data, &ipc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Version = ipc.Version
|
||||
c.Interface = ipc.Interface
|
||||
c.Address = net.IPNet(ipc.Address)
|
||||
c.Gateway = ipc.Gateway
|
||||
return nil
|
||||
}
|
189
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
Normal file
189
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
// 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.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// like net.IPNet but adds JSON marshalling and unmarshalling
|
||||
type IPNet net.IPNet
|
||||
|
||||
// ParseCIDR takes a string like "10.2.3.1/24" and
|
||||
// return IPNet with "10.2.3.1" and /24 mask
|
||||
func ParseCIDR(s string) (*net.IPNet, error) {
|
||||
ip, ipn, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipn.IP = ip
|
||||
return ipn, nil
|
||||
}
|
||||
|
||||
func (n IPNet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal((*net.IPNet)(&n).String())
|
||||
}
|
||||
|
||||
func (n *IPNet) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmp, err := ParseCIDR(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*n = IPNet(*tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NetConf describes a network.
|
||||
type NetConf struct {
|
||||
CNIVersion string `json:"cniVersion,omitempty"`
|
||||
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Capabilities map[string]bool `json:"capabilities,omitempty"`
|
||||
IPAM struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
} `json:"ipam,omitempty"`
|
||||
DNS DNS `json:"dns"`
|
||||
}
|
||||
|
||||
// NetConfList describes an ordered list of networks.
|
||||
type NetConfList struct {
|
||||
CNIVersion string `json:"cniVersion,omitempty"`
|
||||
|
||||
Name string `json:"name,omitempty"`
|
||||
Plugins []*NetConf `json:"plugins,omitempty"`
|
||||
}
|
||||
|
||||
type ResultFactoryFunc func([]byte) (Result, error)
|
||||
|
||||
// Result is an interface that provides the result of plugin execution
|
||||
type Result interface {
|
||||
// The highest CNI specification result verison the result supports
|
||||
// without having to convert
|
||||
Version() string
|
||||
|
||||
// Returns the result converted into the requested CNI specification
|
||||
// result version, or an error if conversion failed
|
||||
GetAsVersion(version string) (Result, error)
|
||||
|
||||
// Prints the result in JSON format to stdout
|
||||
Print() error
|
||||
|
||||
// Returns a JSON string representation of the result
|
||||
String() string
|
||||
}
|
||||
|
||||
func PrintResult(result Result, version string) error {
|
||||
newResult, err := result.GetAsVersion(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return newResult.Print()
|
||||
}
|
||||
|
||||
// DNS contains values interesting for DNS resolvers
|
||||
type DNS struct {
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
Search []string `json:"search,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
Dst net.IPNet
|
||||
GW net.IP
|
||||
}
|
||||
|
||||
func (r *Route) String() string {
|
||||
return fmt.Sprintf("%+v", *r)
|
||||
}
|
||||
|
||||
// Well known error codes
|
||||
// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
|
||||
const (
|
||||
ErrUnknown uint = iota // 0
|
||||
ErrIncompatibleCNIVersion // 1
|
||||
ErrUnsupportedField // 2
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
Code uint `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
details := ""
|
||||
if e.Details != "" {
|
||||
details = fmt.Sprintf("; %v", e.Details)
|
||||
}
|
||||
return fmt.Sprintf("%v%v", e.Msg, details)
|
||||
}
|
||||
|
||||
func (e *Error) Print() error {
|
||||
return prettyPrint(e)
|
||||
}
|
||||
|
||||
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||
// for our custom IPNet type
|
||||
|
||||
// JSON (un)marshallable types
|
||||
type route struct {
|
||||
Dst IPNet `json:"dst"`
|
||||
GW net.IP `json:"gw,omitempty"`
|
||||
}
|
||||
|
||||
func (r *Route) UnmarshalJSON(data []byte) error {
|
||||
rt := route{}
|
||||
if err := json.Unmarshal(data, &rt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Dst = net.IPNet(rt.Dst)
|
||||
r.GW = rt.GW
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Route) MarshalJSON() ([]byte, error) {
|
||||
rt := route{
|
||||
Dst: IPNet(r.Dst),
|
||||
GW: r.GW,
|
||||
}
|
||||
|
||||
return json.Marshal(rt)
|
||||
}
|
||||
|
||||
func prettyPrint(obj interface{}) error {
|
||||
data, err := json.MarshalIndent(obj, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stdout.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// NotImplementedError is used to indicate that a method is not implemented for the given platform
|
||||
var NotImplementedError = errors.New("Not Implemented")
|
37
vendor/github.com/containernetworking/cni/pkg/version/conf.go
generated
vendored
Normal file
37
vendor/github.com/containernetworking/cni/pkg/version/conf.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ConfigDecoder can decode the CNI version available in network config data
|
||||
type ConfigDecoder struct{}
|
||||
|
||||
func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) {
|
||||
var conf struct {
|
||||
CNIVersion string `json:"cniVersion"`
|
||||
}
|
||||
err := json.Unmarshal(jsonBytes, &conf)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("decoding version from network config: %s", err)
|
||||
}
|
||||
if conf.CNIVersion == "" {
|
||||
return "0.1.0", nil
|
||||
}
|
||||
return conf.CNIVersion, nil
|
||||
}
|
81
vendor/github.com/containernetworking/cni/pkg/version/plugin.go
generated
vendored
Normal file
81
vendor/github.com/containernetworking/cni/pkg/version/plugin.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PluginInfo reports information about CNI versioning
|
||||
type PluginInfo interface {
|
||||
// SupportedVersions returns one or more CNI spec versions that the plugin
|
||||
// supports. If input is provided in one of these versions, then the plugin
|
||||
// promises to use the same CNI version in its response
|
||||
SupportedVersions() []string
|
||||
|
||||
// Encode writes this CNI version information as JSON to the given Writer
|
||||
Encode(io.Writer) error
|
||||
}
|
||||
|
||||
type pluginInfo struct {
|
||||
CNIVersion_ string `json:"cniVersion"`
|
||||
SupportedVersions_ []string `json:"supportedVersions,omitempty"`
|
||||
}
|
||||
|
||||
// pluginInfo implements the PluginInfo interface
|
||||
var _ PluginInfo = &pluginInfo{}
|
||||
|
||||
func (p *pluginInfo) Encode(w io.Writer) error {
|
||||
return json.NewEncoder(w).Encode(p)
|
||||
}
|
||||
|
||||
func (p *pluginInfo) SupportedVersions() []string {
|
||||
return p.SupportedVersions_
|
||||
}
|
||||
|
||||
// PluginSupports returns a new PluginInfo that will report the given versions
|
||||
// as supported
|
||||
func PluginSupports(supportedVersions ...string) PluginInfo {
|
||||
if len(supportedVersions) < 1 {
|
||||
panic("programmer error: you must support at least one version")
|
||||
}
|
||||
return &pluginInfo{
|
||||
CNIVersion_: Current(),
|
||||
SupportedVersions_: supportedVersions,
|
||||
}
|
||||
}
|
||||
|
||||
// PluginDecoder can decode the response returned by a plugin's VERSION command
|
||||
type PluginDecoder struct{}
|
||||
|
||||
func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) {
|
||||
var info pluginInfo
|
||||
err := json.Unmarshal(jsonBytes, &info)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding version info: %s", err)
|
||||
}
|
||||
if info.CNIVersion_ == "" {
|
||||
return nil, fmt.Errorf("decoding version info: missing field cniVersion")
|
||||
}
|
||||
if len(info.SupportedVersions_) == 0 {
|
||||
if info.CNIVersion_ == "0.2.0" {
|
||||
return PluginSupports("0.1.0", "0.2.0"), nil
|
||||
}
|
||||
return nil, fmt.Errorf("decoding version info: missing field supportedVersions")
|
||||
}
|
||||
return &info, nil
|
||||
}
|
49
vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
generated
vendored
Normal file
49
vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
// 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 "fmt"
|
||||
|
||||
type ErrorIncompatible struct {
|
||||
Config string
|
||||
Supported []string
|
||||
}
|
||||
|
||||
func (e *ErrorIncompatible) Details() string {
|
||||
return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported)
|
||||
}
|
||||
|
||||
func (e *ErrorIncompatible) Error() string {
|
||||
return fmt.Sprintf("incompatible CNI versions: %s", e.Details())
|
||||
}
|
||||
|
||||
type Reconciler struct{}
|
||||
|
||||
func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible {
|
||||
return r.CheckRaw(configVersion, pluginInfo.SupportedVersions())
|
||||
}
|
||||
|
||||
func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible {
|
||||
for _, supportedVersion := range supportedVersions {
|
||||
if configVersion == supportedVersion {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return &ErrorIncompatible{
|
||||
Config: configVersion,
|
||||
Supported: supportedVersions,
|
||||
}
|
||||
}
|
61
vendor/github.com/containernetworking/cni/pkg/version/version.go
generated
vendored
Normal file
61
vendor/github.com/containernetworking/cni/pkg/version/version.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/020"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
)
|
||||
|
||||
// Current reports the version of the CNI spec implemented by this library
|
||||
func Current() string {
|
||||
return "0.3.1"
|
||||
}
|
||||
|
||||
// Legacy PluginInfo describes a plugin that is backwards compatible with the
|
||||
// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0
|
||||
// library ought to work correctly with a plugin that reports support for
|
||||
// Legacy versions.
|
||||
//
|
||||
// Any future CNI spec versions which meet this definition should be added to
|
||||
// this list.
|
||||
var Legacy = PluginSupports("0.1.0", "0.2.0")
|
||||
var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1")
|
||||
|
||||
var resultFactories = []struct {
|
||||
supportedVersions []string
|
||||
newResult types.ResultFactoryFunc
|
||||
}{
|
||||
{current.SupportedVersions, current.NewResult},
|
||||
{types020.SupportedVersions, types020.NewResult},
|
||||
}
|
||||
|
||||
// Finds a Result object matching the requested version (if any) and asks
|
||||
// that object to parse the plugin result, returning an error if parsing failed.
|
||||
func NewResult(version string, resultBytes []byte) (types.Result, error) {
|
||||
reconciler := &Reconciler{}
|
||||
for _, resultFactory := range resultFactories {
|
||||
err := reconciler.CheckRaw(version, resultFactory.supportedVersions)
|
||||
if err == nil {
|
||||
// Result supports this version
|
||||
return resultFactory.newResult(resultBytes)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported CNI result version %q", version)
|
||||
}
|
201
vendor/github.com/containernetworking/plugins/LICENSE
generated
vendored
Normal file
201
vendor/github.com/containernetworking/plugins/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
40
vendor/github.com/containernetworking/plugins/pkg/ns/README.md
generated
vendored
Normal file
40
vendor/github.com/containernetworking/plugins/pkg/ns/README.md
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
### Namespaces, Threads, and Go
|
||||
On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code.
|
||||
|
||||
### Namespace Switching
|
||||
Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads.
|
||||
|
||||
Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in.
|
||||
|
||||
For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly.
|
||||
|
||||
### Do() The Recommended Thing
|
||||
The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
|
||||
|
||||
```go
|
||||
targetNs, err := ns.NewNS()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = targetNs.Do(func(hostNs ns.NetNS) error {
|
||||
dummy := &netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: "dummy0",
|
||||
},
|
||||
}
|
||||
return netlink.LinkAdd(dummy)
|
||||
})
|
||||
```
|
||||
|
||||
Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem.
|
||||
|
||||
Also: If the runtime spawns a new OS thread, it will inherit the network namespace of the parent thread, which may have been temporarily switched, and thus the new OS thread will be permanently "stuck in the wrong namespace".
|
||||
|
||||
In short, **there is no safe way to change network namespaces from within a long-lived, multithreaded Go process**. If your daemon process needs to be namespace aware, consider spawning a separate process (like a CNI plugin) for each namespace.
|
||||
|
||||
### Further Reading
|
||||
- https://github.com/golang/go/wiki/LockOSThread
|
||||
- http://morsmachine.dk/go-scheduler
|
||||
- https://github.com/containernetworking/cni/issues/262
|
||||
- https://golang.org/pkg/runtime/
|
||||
- https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix
|
178
vendor/github.com/containernetworking/plugins/pkg/ns/ns.go
generated
vendored
Normal file
178
vendor/github.com/containernetworking/plugins/pkg/ns/ns.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
// 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.
|
||||
|
||||
package ns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type NetNS interface {
|
||||
// Executes the passed closure in this object's network namespace,
|
||||
// attempting to restore the original namespace before returning.
|
||||
// However, since each OS thread can have a different network namespace,
|
||||
// and Go's thread scheduling is highly variable, callers cannot
|
||||
// guarantee any specific namespace is set unless operations that
|
||||
// require that namespace are wrapped with Do(). Also, no code called
|
||||
// from Do() should call runtime.UnlockOSThread(), or the risk
|
||||
// of executing code in an incorrect namespace will be greater. See
|
||||
// https://github.com/golang/go/wiki/LockOSThread for further details.
|
||||
Do(toRun func(NetNS) error) error
|
||||
|
||||
// Sets the current network namespace to this object's network namespace.
|
||||
// Note that since Go's thread scheduling is highly variable, callers
|
||||
// cannot guarantee the requested namespace will be the current namespace
|
||||
// after this function is called; to ensure this wrap operations that
|
||||
// require the namespace with Do() instead.
|
||||
Set() error
|
||||
|
||||
// Returns the filesystem path representing this object's network namespace
|
||||
Path() string
|
||||
|
||||
// Returns a file descriptor representing this object's network namespace
|
||||
Fd() uintptr
|
||||
|
||||
// Cleans up this instance of the network namespace; if this instance
|
||||
// is the last user the namespace will be destroyed
|
||||
Close() error
|
||||
}
|
||||
|
||||
type netNS struct {
|
||||
file *os.File
|
||||
mounted bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
// netNS implements the NetNS interface
|
||||
var _ NetNS = &netNS{}
|
||||
|
||||
const (
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
|
||||
NSFS_MAGIC = 0x6e736673
|
||||
PROCFS_MAGIC = 0x9fa0
|
||||
)
|
||||
|
||||
type NSPathNotExistErr struct{ msg string }
|
||||
|
||||
func (e NSPathNotExistErr) Error() string { return e.msg }
|
||||
|
||||
type NSPathNotNSErr struct{ msg string }
|
||||
|
||||
func (e NSPathNotNSErr) Error() string { return e.msg }
|
||||
|
||||
func IsNSorErr(nspath string) error {
|
||||
stat := syscall.Statfs_t{}
|
||||
if err := syscall.Statfs(nspath, &stat); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)}
|
||||
} else {
|
||||
err = fmt.Errorf("failed to Statfs %q: %v", nspath, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
switch stat.Type {
|
||||
case PROCFS_MAGIC, NSFS_MAGIC:
|
||||
return nil
|
||||
default:
|
||||
return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an object representing the namespace referred to by @path
|
||||
func GetNS(nspath string) (NetNS, error) {
|
||||
err := IsNSorErr(nspath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fd, err := os.Open(nspath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &netNS{file: fd}, nil
|
||||
}
|
||||
|
||||
func (ns *netNS) Path() string {
|
||||
return ns.file.Name()
|
||||
}
|
||||
|
||||
func (ns *netNS) Fd() uintptr {
|
||||
return ns.file.Fd()
|
||||
}
|
||||
|
||||
func (ns *netNS) errorIfClosed() error {
|
||||
if ns.closed {
|
||||
return fmt.Errorf("%q has already been closed", ns.file.Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *netNS) Do(toRun func(NetNS) error) error {
|
||||
if err := ns.errorIfClosed(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containedCall := func(hostNS NetNS) error {
|
||||
threadNS, err := GetCurrentNS()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open current netns: %v", err)
|
||||
}
|
||||
defer threadNS.Close()
|
||||
|
||||
// switch to target namespace
|
||||
if err = ns.Set(); err != nil {
|
||||
return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
|
||||
}
|
||||
defer threadNS.Set() // switch back
|
||||
|
||||
return toRun(hostNS)
|
||||
}
|
||||
|
||||
// save a handle to current network namespace
|
||||
hostNS, err := GetCurrentNS()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to open current namespace: %v", err)
|
||||
}
|
||||
defer hostNS.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
var innerError error
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
runtime.LockOSThread()
|
||||
innerError = containedCall(hostNS)
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
return innerError
|
||||
}
|
||||
|
||||
// WithNetNSPath executes the passed closure under the given network
|
||||
// namespace, restoring the original namespace afterwards.
|
||||
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
|
||||
ns, err := GetNS(nspath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
return ns.Do(toRun)
|
||||
}
|
149
vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go
generated
vendored
Normal file
149
vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright 2015-2017 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ns
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Returns an object representing the current OS thread's network namespace
|
||||
func GetCurrentNS() (NetNS, error) {
|
||||
return GetNS(getCurrentThreadNetNSPath())
|
||||
}
|
||||
|
||||
func getCurrentThreadNetNSPath() string {
|
||||
// /proc/self/ns/net returns the namespace of the main thread, not
|
||||
// of whatever thread this goroutine is running on. Make sure we
|
||||
// use the thread's net namespace since the thread is switching around
|
||||
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
|
||||
}
|
||||
|
||||
// Creates a new persistent network namespace and returns an object
|
||||
// representing that namespace, without switching to it
|
||||
func NewNS() (NetNS, error) {
|
||||
const nsRunDir = "/var/run/netns"
|
||||
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Reader.Read(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(nsRunDir, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create an empty file at the mount point
|
||||
nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
nsPath := path.Join(nsRunDir, nsName)
|
||||
mountPointFd, err := os.Create(nsPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mountPointFd.Close()
|
||||
|
||||
// Ensure the mount point is cleaned up on errors; if the namespace
|
||||
// was successfully mounted this will have no effect because the file
|
||||
// is in-use
|
||||
defer os.RemoveAll(nsPath)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
// do namespace work in a dedicated goroutine, so that we can safely
|
||||
// Lock/Unlock OSThread without upsetting the lock/unlock state of
|
||||
// the caller of this function
|
||||
var fd *os.File
|
||||
go (func() {
|
||||
defer wg.Done()
|
||||
runtime.LockOSThread()
|
||||
|
||||
var origNS NetNS
|
||||
origNS, err = GetNS(getCurrentThreadNetNSPath())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer origNS.Close()
|
||||
|
||||
// create a new netns on the current thread
|
||||
err = unix.Unshare(unix.CLONE_NEWNET)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer origNS.Set()
|
||||
|
||||
// bind mount the new netns from the current thread onto the mount point
|
||||
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fd, err = os.Open(nsPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})()
|
||||
wg.Wait()
|
||||
|
||||
if err != nil {
|
||||
unix.Unmount(nsPath, unix.MNT_DETACH)
|
||||
return nil, fmt.Errorf("failed to create namespace: %v", err)
|
||||
}
|
||||
|
||||
return &netNS{file: fd, mounted: true}, nil
|
||||
}
|
||||
|
||||
func (ns *netNS) Close() error {
|
||||
if err := ns.errorIfClosed(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ns.file.Close(); err != nil {
|
||||
return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
|
||||
}
|
||||
ns.closed = true
|
||||
|
||||
if ns.mounted {
|
||||
if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
|
||||
return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
|
||||
}
|
||||
if err := os.RemoveAll(ns.file.Name()); err != nil {
|
||||
return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
|
||||
}
|
||||
ns.mounted = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *netNS) Set() error {
|
||||
if err := ns.errorIfClosed(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil {
|
||||
return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
36
vendor/github.com/containernetworking/plugins/pkg/ns/ns_unspecified.go
generated
vendored
Normal file
36
vendor/github.com/containernetworking/plugins/pkg/ns/ns_unspecified.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2015-2017 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package ns
|
||||
|
||||
import "github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
// Returns an object representing the current OS thread's network namespace
|
||||
func GetCurrentNS() (NetNS, error) {
|
||||
return nil, types.NotImplementedError
|
||||
}
|
||||
|
||||
func NewNS() (NetNS, error) {
|
||||
return nil, types.NotImplementedError
|
||||
}
|
||||
|
||||
func (ns *netNS) Close() error {
|
||||
return types.NotImplementedError
|
||||
}
|
||||
|
||||
func (ns *netNS) Set() error {
|
||||
return types.NotImplementedError
|
||||
}
|
126
vendor/github.com/docker/distribution/uuid/uuid.go
generated
vendored
Normal file
126
vendor/github.com/docker/distribution/uuid/uuid.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
// Package uuid provides simple UUID generation. Only version 4 style UUIDs
|
||||
// can be generated.
|
||||
//
|
||||
// Please see http://tools.ietf.org/html/rfc4122 for details on UUIDs.
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Bits is the number of bits in a UUID
|
||||
Bits = 128
|
||||
|
||||
// Size is the number of bytes in a UUID
|
||||
Size = Bits / 8
|
||||
|
||||
format = "%08x-%04x-%04x-%04x-%012x"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUUIDInvalid indicates a parsed string is not a valid uuid.
|
||||
ErrUUIDInvalid = fmt.Errorf("invalid uuid")
|
||||
|
||||
// Loggerf can be used to override the default logging destination. Such
|
||||
// log messages in this library should be logged at warning or higher.
|
||||
Loggerf = func(format string, args ...interface{}) {}
|
||||
)
|
||||
|
||||
// UUID represents a UUID value. UUIDs can be compared and set to other values
|
||||
// and accessed by byte.
|
||||
type UUID [Size]byte
|
||||
|
||||
// Generate creates a new, version 4 uuid.
|
||||
func Generate() (u UUID) {
|
||||
const (
|
||||
// ensures we backoff for less than 450ms total. Use the following to
|
||||
// select new value, in units of 10ms:
|
||||
// n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2
|
||||
maxretries = 9
|
||||
backoff = time.Millisecond * 10
|
||||
)
|
||||
|
||||
var (
|
||||
totalBackoff time.Duration
|
||||
count int
|
||||
retries int
|
||||
)
|
||||
|
||||
for {
|
||||
// This should never block but the read may fail. Because of this,
|
||||
// we just try to read the random number generator until we get
|
||||
// something. This is a very rare condition but may happen.
|
||||
b := time.Duration(retries) * backoff
|
||||
time.Sleep(b)
|
||||
totalBackoff += b
|
||||
|
||||
n, err := io.ReadFull(rand.Reader, u[count:])
|
||||
if err != nil {
|
||||
if retryOnError(err) && retries < maxretries {
|
||||
count += n
|
||||
retries++
|
||||
Loggerf("error generating version 4 uuid, retrying: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Any other errors represent a system problem. What did someone
|
||||
// do to /dev/urandom?
|
||||
panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err))
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
u[6] = (u[6] & 0x0f) | 0x40 // set version byte
|
||||
u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
// Parse attempts to extract a uuid from the string or returns an error.
|
||||
func Parse(s string) (u UUID, err error) {
|
||||
if len(s) != 36 {
|
||||
return UUID{}, ErrUUIDInvalid
|
||||
}
|
||||
|
||||
// create stack addresses for each section of the uuid.
|
||||
p := make([][]byte, 5)
|
||||
|
||||
if _, err := fmt.Sscanf(s, format, &p[0], &p[1], &p[2], &p[3], &p[4]); err != nil {
|
||||
return u, err
|
||||
}
|
||||
|
||||
copy(u[0:4], p[0])
|
||||
copy(u[4:6], p[1])
|
||||
copy(u[6:8], p[2])
|
||||
copy(u[8:10], p[3])
|
||||
copy(u[10:16], p[4])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (u UUID) String() string {
|
||||
return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:])
|
||||
}
|
||||
|
||||
// retryOnError tries to detect whether or not retrying would be fruitful.
|
||||
func retryOnError(err error) bool {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return retryOnError(err.Err) // unpack the target error
|
||||
case syscall.Errno:
|
||||
if err == syscall.EPERM {
|
||||
// EPERM represents an entropy pool exhaustion, a condition under
|
||||
// which we backoff and retry.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
3
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/doc.go
generated
vendored
Normal file
3
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
// Package portsbinding provides information and interaction with the port
|
||||
// binding extension for the OpenStack Networking service.
|
||||
package portsbinding
|
117
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/requests.go
generated
vendored
Normal file
117
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/requests.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
package portsbinding
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||
)
|
||||
|
||||
// Get retrieves a specific port based on its unique ID.
|
||||
func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
_, r.Err = c.Get(getURL(c, id), &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateOpts represents the attributes used when creating a new
|
||||
// port with extended attributes.
|
||||
type CreateOpts struct {
|
||||
// CreateOptsBuilder is the interface options structs have to satisfy in order
|
||||
// to be used in the main Create operation in this package.
|
||||
ports.CreateOptsBuilder `json:"-"`
|
||||
// The ID of the host where the port is allocated
|
||||
HostID string `json:"binding:host_id,omitempty"`
|
||||
// The dns name
|
||||
DNSName string `json:"dns_name,omitempty"`
|
||||
// The virtual network interface card (vNIC) type that is bound to the
|
||||
// neutron port
|
||||
VNICType string `json:"binding:vnic_type,omitempty"`
|
||||
// A dictionary that enables the application running on the specified
|
||||
// host to pass and receive virtual network interface (VIF) port-specific
|
||||
// information to the plug-in
|
||||
Profile map[string]string `json:"binding:profile,omitempty"`
|
||||
}
|
||||
|
||||
// ToPortCreateMap casts a CreateOpts struct to a map.
|
||||
func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
|
||||
b1, err := opts.CreateOptsBuilder.ToPortCreateMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b2, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port := b1["port"].(map[string]interface{})
|
||||
|
||||
for k, v := range b2 {
|
||||
port[k] = v
|
||||
}
|
||||
|
||||
return map[string]interface{}{"port": port}, nil
|
||||
}
|
||||
|
||||
// Create accepts a CreateOpts struct and creates a new port with extended attributes.
|
||||
// You must remember to provide a NetworkID value.
|
||||
func Create(c *gophercloud.ServiceClient, opts ports.CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToPortCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = c.Post(createURL(c), b, &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOpts represents the attributes used when updating an existing port.
|
||||
type UpdateOpts struct {
|
||||
// UpdateOptsBuilder is the interface options structs have to satisfy in order
|
||||
// to be used in the main Update operation in this package.
|
||||
ports.UpdateOptsBuilder `json:"-"`
|
||||
// The ID of the host where the port is allocated
|
||||
HostID string `json:"binding:host_id,omitempty"`
|
||||
// The dns name
|
||||
DNSName string `json:"dns_name,omitempty"`
|
||||
// The virtual network interface card (vNIC) type that is bound to the
|
||||
// neutron port
|
||||
VNICType string `json:"binding:vnic_type,omitempty"`
|
||||
// A dictionary that enables the application running on the specified
|
||||
// host to pass and receive virtual network interface (VIF) port-specific
|
||||
// information to the plug-in
|
||||
Profile map[string]string `json:"binding:profile,omitempty"`
|
||||
}
|
||||
|
||||
// ToPortUpdateMap casts an UpdateOpts struct to a map.
|
||||
func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
|
||||
b1, err := opts.UpdateOptsBuilder.ToPortUpdateMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b2, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port := b1["port"].(map[string]interface{})
|
||||
|
||||
for k, v := range b2 {
|
||||
port[k] = v
|
||||
}
|
||||
|
||||
return map[string]interface{}{"port": port}, nil
|
||||
}
|
||||
|
||||
// Update accepts a UpdateOpts struct and updates an existing port using the
|
||||
// values provided.
|
||||
func Update(c *gophercloud.ServiceClient, id string, opts ports.UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToPortUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return r
|
||||
}
|
||||
_, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 201},
|
||||
})
|
||||
return
|
||||
}
|
73
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/results.go
generated
vendored
Normal file
73
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/results.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
package portsbinding
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a function that accepts a result and extracts a port resource.
|
||||
func (r commonResult) Extract() (*Port, error) {
|
||||
var s struct {
|
||||
Port *Port `json:"port"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Port, err
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult represents the result of a get operation.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// UpdateResult represents the result of an update operation.
|
||||
type UpdateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// IP is a sub-struct that represents an individual IP.
|
||||
type IP struct {
|
||||
SubnetID string `json:"subnet_id"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
}
|
||||
|
||||
// Port represents a Neutron port. See package documentation for a top-level
|
||||
// description of what this is.
|
||||
type Port struct {
|
||||
ports.Port
|
||||
// The ID of the host where the port is allocated
|
||||
HostID string `json:"binding:host_id"`
|
||||
// A dictionary that enables the application to pass information about
|
||||
// functions that the Networking API provides.
|
||||
VIFDetails map[string]interface{} `json:"binding:vif_details"`
|
||||
// The VIF type for the port.
|
||||
VIFType string `json:"binding:vif_type"`
|
||||
// The virtual network interface card (vNIC) type that is bound to the
|
||||
// neutron port
|
||||
VNICType string `json:"binding:vnic_type"`
|
||||
// A dictionary that enables the application running on the specified
|
||||
// host to pass and receive virtual network interface (VIF) port-specific
|
||||
// information to the plug-in
|
||||
Profile map[string]string `json:"binding:profile"`
|
||||
}
|
||||
|
||||
// ExtractPorts accepts a Page struct, specifically a PortPage struct,
|
||||
// and extracts the elements into a slice of Port structs. In other words,
|
||||
// a generic collection is mapped into a relevant slice.
|
||||
func ExtractPorts(r pagination.Page) ([]Port, error) {
|
||||
var s struct {
|
||||
Ports []Port `json:"ports"`
|
||||
}
|
||||
err := (r.(ports.PortPage)).ExtractInto(&s)
|
||||
return s.Ports, err
|
||||
}
|
23
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/urls.go
generated
vendored
Normal file
23
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding/urls.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package portsbinding
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL("ports", id)
|
||||
}
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("ports")
|
||||
}
|
||||
|
||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return resourceURL(c, id)
|
||||
}
|
||||
|
||||
func createURL(c *gophercloud.ServiceClient) string {
|
||||
return rootURL(c)
|
||||
}
|
||||
|
||||
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return resourceURL(c, id)
|
||||
}
|
138
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go
generated
vendored
Normal file
138
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOpts allows the filtering and sorting of paginated collections through
|
||||
// the API. Filtering is achieved by passing in struct field values that map to
|
||||
// the floating IP attributes you want to see returned. SortKey allows you to
|
||||
// sort by a particular network attribute. SortDir sets the direction, and is
|
||||
// either `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
ID string `q:"id"`
|
||||
Name string `q:"name"`
|
||||
TenantID string `q:"tenant_id"`
|
||||
Limit int `q:"limit"`
|
||||
Marker string `q:"marker"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// List returns a Pager which allows you to iterate over a collection of
|
||||
// security groups. It accepts a ListOpts struct, which allows you to filter
|
||||
// and sort the returned collection for greater efficiency.
|
||||
func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
|
||||
q, err := gophercloud.BuildQueryString(&opts)
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
u := rootURL(c) + q.String()
|
||||
return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
|
||||
return SecGroupPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
type CreateOptsBuilder interface {
|
||||
ToSecGroupCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts contains all the values needed to create a new security group.
|
||||
type CreateOpts struct {
|
||||
// Required. Human-readable name for the Security Group. Does not have to be unique.
|
||||
Name string `json:"name" required:"true"`
|
||||
// Required for admins. Indicates the owner of the Security Group.
|
||||
TenantID string `json:"tenant_id,omitempty"`
|
||||
// Optional. Describes the security group.
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "security_group")
|
||||
}
|
||||
|
||||
// Create is an operation which provisions a new security group with default
|
||||
// security group rules for the IPv4 and IPv6 ether types.
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToSecGroupCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateOptsBuilder interface {
|
||||
ToSecGroupUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts contains all the values needed to update an existing security group.
|
||||
type UpdateOpts struct {
|
||||
// Human-readable name for the Security Group. Does not have to be unique.
|
||||
Name string `json:"name,omitempty"`
|
||||
// Optional. Describes the security group.
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "security_group")
|
||||
}
|
||||
|
||||
// Update is an operation which updates an existing security group.
|
||||
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToSecGroupUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
_, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get retrieves a particular security group based on its unique ID.
|
||||
func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete will permanently delete a particular security group based on its unique ID.
|
||||
func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||
_, r.Err = c.Delete(resourceURL(c, id), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// IDFromName is a convenience function that returns a security group's ID given its name.
|
||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||
count := 0
|
||||
id := ""
|
||||
pages, err := List(client, ListOpts{}).AllPages()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
all, err := ExtractGroups(pages)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, s := range all {
|
||||
if s.Name == name {
|
||||
count++
|
||||
id = s.ID
|
||||
}
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0:
|
||||
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "security group"}
|
||||
case 1:
|
||||
return id, nil
|
||||
default:
|
||||
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "security group"}
|
||||
}
|
||||
}
|
99
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/results.go
generated
vendored
Normal file
99
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/results.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// SecGroup represents a container for security group rules.
|
||||
type SecGroup struct {
|
||||
// The UUID for the security group.
|
||||
ID string
|
||||
|
||||
// Human-readable name for the security group. Might not be unique. Cannot be
|
||||
// named "default" as that is automatically created for a tenant.
|
||||
Name string
|
||||
|
||||
// The security group description.
|
||||
Description string
|
||||
|
||||
// A slice of security group rules that dictate the permitted behaviour for
|
||||
// traffic entering and leaving the group.
|
||||
Rules []rules.SecGroupRule `json:"security_group_rules"`
|
||||
|
||||
// Owner of the security group. Only admin users can specify a TenantID
|
||||
// other than their own.
|
||||
TenantID string `json:"tenant_id"`
|
||||
}
|
||||
|
||||
// SecGroupPage is the page returned by a pager when traversing over a
|
||||
// collection of security groups.
|
||||
type SecGroupPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of security groups has
|
||||
// reached the end of a page and the pager seeks to traverse over a new one. In
|
||||
// order to do this, it needs to construct the next page's URL.
|
||||
func (r SecGroupPage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links []gophercloud.Link `json:"security_groups_links"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gophercloud.ExtractNextURL(s.Links)
|
||||
}
|
||||
|
||||
// IsEmpty checks whether a SecGroupPage struct is empty.
|
||||
func (r SecGroupPage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractGroups(r)
|
||||
return len(is) == 0, err
|
||||
}
|
||||
|
||||
// ExtractGroups accepts a Page struct, specifically a SecGroupPage struct,
|
||||
// and extracts the elements into a slice of SecGroup structs. In other words,
|
||||
// a generic collection is mapped into a relevant slice.
|
||||
func ExtractGroups(r pagination.Page) ([]SecGroup, error) {
|
||||
var s struct {
|
||||
SecGroups []SecGroup `json:"security_groups"`
|
||||
}
|
||||
err := (r.(SecGroupPage)).ExtractInto(&s)
|
||||
return s.SecGroups, err
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a function that accepts a result and extracts a security group.
|
||||
func (r commonResult) Extract() (*SecGroup, error) {
|
||||
var s struct {
|
||||
SecGroup *SecGroup `json:"security_group"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.SecGroup, err
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// UpdateResult represents the result of an update operation.
|
||||
type UpdateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult represents the result of a get operation.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// DeleteResult represents the result of a delete operation.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
13
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go
generated
vendored
Normal file
13
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package groups
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
const rootPath = "security-groups"
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(rootPath)
|
||||
}
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(rootPath, id)
|
||||
}
|
145
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go
generated
vendored
Normal file
145
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOpts allows the filtering and sorting of paginated collections through
|
||||
// the API. Filtering is achieved by passing in struct field values that map to
|
||||
// the security group attributes you want to see returned. SortKey allows you to
|
||||
// sort by a particular network attribute. SortDir sets the direction, and is
|
||||
// either `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
Direction string `q:"direction"`
|
||||
EtherType string `q:"ethertype"`
|
||||
ID string `q:"id"`
|
||||
PortRangeMax int `q:"port_range_max"`
|
||||
PortRangeMin int `q:"port_range_min"`
|
||||
Protocol string `q:"protocol"`
|
||||
RemoteGroupID string `q:"remote_group_id"`
|
||||
RemoteIPPrefix string `q:"remote_ip_prefix"`
|
||||
SecGroupID string `q:"security_group_id"`
|
||||
TenantID string `q:"tenant_id"`
|
||||
Limit int `q:"limit"`
|
||||
Marker string `q:"marker"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
}
|
||||
|
||||
// List returns a Pager which allows you to iterate over a collection of
|
||||
// security group rules. It accepts a ListOpts struct, which allows you to filter
|
||||
// and sort the returned collection for greater efficiency.
|
||||
func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
|
||||
q, err := gophercloud.BuildQueryString(&opts)
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
u := rootURL(c) + q.String()
|
||||
return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
|
||||
return SecGroupRulePage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
type RuleDirection string
|
||||
type RuleProtocol string
|
||||
type RuleEtherType string
|
||||
|
||||
// Constants useful for CreateOpts
|
||||
const (
|
||||
DirIngress RuleDirection = "ingress"
|
||||
DirEgress RuleDirection = "egress"
|
||||
EtherType4 RuleEtherType = "IPv4"
|
||||
EtherType6 RuleEtherType = "IPv6"
|
||||
ProtocolAH RuleProtocol = "ah"
|
||||
ProtocolDCCP RuleProtocol = "dccp"
|
||||
ProtocolEGP RuleProtocol = "egp"
|
||||
ProtocolESP RuleProtocol = "esp"
|
||||
ProtocolGRE RuleProtocol = "gre"
|
||||
ProtocolICMP RuleProtocol = "icmp"
|
||||
ProtocolIGMP RuleProtocol = "igmp"
|
||||
ProtocolIPv6Encap RuleProtocol = "ipv6-encap"
|
||||
ProtocolIPv6Frag RuleProtocol = "ipv6-frag"
|
||||
ProtocolIPv6ICMP RuleProtocol = "ipv6-icmp"
|
||||
ProtocolIPv6NoNxt RuleProtocol = "ipv6-nonxt"
|
||||
ProtocolIPv6Opts RuleProtocol = "ipv6-opts"
|
||||
ProtocolIPv6Route RuleProtocol = "ipv6-route"
|
||||
ProtocolOSPF RuleProtocol = "ospf"
|
||||
ProtocolPGM RuleProtocol = "pgm"
|
||||
ProtocolRSVP RuleProtocol = "rsvp"
|
||||
ProtocolSCTP RuleProtocol = "sctp"
|
||||
ProtocolTCP RuleProtocol = "tcp"
|
||||
ProtocolUDP RuleProtocol = "udp"
|
||||
ProtocolUDPLite RuleProtocol = "udplite"
|
||||
ProtocolVRRP RuleProtocol = "vrrp"
|
||||
)
|
||||
|
||||
// CreateOptsBuilder is what types must satisfy to be used as Create
|
||||
// options.
|
||||
type CreateOptsBuilder interface {
|
||||
ToSecGroupRuleCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts contains all the values needed to create a new security group rule.
|
||||
type CreateOpts struct {
|
||||
// Required. Must be either "ingress" or "egress": the direction in which the
|
||||
// security group rule is applied.
|
||||
Direction RuleDirection `json:"direction" required:"true"`
|
||||
// Required. Must be "IPv4" or "IPv6", and addresses represented in CIDR must
|
||||
// match the ingress or egress rules.
|
||||
EtherType RuleEtherType `json:"ethertype" required:"true"`
|
||||
// Required. The security group ID to associate with this security group rule.
|
||||
SecGroupID string `json:"security_group_id" required:"true"`
|
||||
// Optional. The maximum port number in the range that is matched by the
|
||||
// security group rule. The PortRangeMin attribute constrains the PortRangeMax
|
||||
// attribute. If the protocol is ICMP, this value must be an ICMP type.
|
||||
PortRangeMax int `json:"port_range_max,omitempty"`
|
||||
// Optional. The minimum port number in the range that is matched by the
|
||||
// security group rule. If the protocol is TCP or UDP, this value must be
|
||||
// less than or equal to the value of the PortRangeMax attribute. If the
|
||||
// protocol is ICMP, this value must be an ICMP type.
|
||||
PortRangeMin int `json:"port_range_min,omitempty"`
|
||||
// Optional. The protocol that is matched by the security group rule. Valid
|
||||
// values are "tcp", "udp", "icmp" or an empty string.
|
||||
Protocol RuleProtocol `json:"protocol,omitempty"`
|
||||
// Optional. The remote group ID to be associated with this security group
|
||||
// rule. You can specify either RemoteGroupID or RemoteIPPrefix.
|
||||
RemoteGroupID string `json:"remote_group_id,omitempty"`
|
||||
// Optional. The remote IP prefix to be associated with this security group
|
||||
// rule. You can specify either RemoteGroupID or RemoteIPPrefix. This
|
||||
// attribute matches the specified IP prefix as the source IP address of the
|
||||
// IP packet.
|
||||
RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
|
||||
// Required for admins. Indicates the owner of the VIP.
|
||||
TenantID string `json:"tenant_id,omitempty"`
|
||||
}
|
||||
|
||||
// ToSecGroupRuleCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
|
||||
// interface
|
||||
func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "security_group_rule")
|
||||
}
|
||||
|
||||
// Create is an operation which adds a new security group rule and associates it
|
||||
// with an existing security group (whose ID is specified in CreateOpts).
|
||||
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToSecGroupRuleCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get retrieves a particular security group rule based on its unique ID.
|
||||
func Get(c *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
_, r.Err = c.Get(resourceURL(c, id), &r.Body, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete will permanently delete a particular security group rule based on its unique ID.
|
||||
func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||
_, r.Err = c.Delete(resourceURL(c, id), nil)
|
||||
return
|
||||
}
|
118
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/results.go
generated
vendored
Normal file
118
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/results.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// SecGroupRule represents a rule to dictate the behaviour of incoming or
|
||||
// outgoing traffic for a particular security group.
|
||||
type SecGroupRule struct {
|
||||
// The UUID for this security group rule.
|
||||
ID string
|
||||
|
||||
// The direction in which the security group rule is applied. The only values
|
||||
// allowed are "ingress" or "egress". For a compute instance, an ingress
|
||||
// security group rule is applied to incoming (ingress) traffic for that
|
||||
// instance. An egress rule is applied to traffic leaving the instance.
|
||||
Direction string
|
||||
|
||||
// Must be IPv4 or IPv6, and addresses represented in CIDR must match the
|
||||
// ingress or egress rules.
|
||||
EtherType string `json:"ethertype"`
|
||||
|
||||
// The security group ID to associate with this security group rule.
|
||||
SecGroupID string `json:"security_group_id"`
|
||||
|
||||
// The minimum port number in the range that is matched by the security group
|
||||
// rule. If the protocol is TCP or UDP, this value must be less than or equal
|
||||
// to the value of the PortRangeMax attribute. If the protocol is ICMP, this
|
||||
// value must be an ICMP type.
|
||||
PortRangeMin int `json:"port_range_min"`
|
||||
|
||||
// The maximum port number in the range that is matched by the security group
|
||||
// rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If
|
||||
// the protocol is ICMP, this value must be an ICMP type.
|
||||
PortRangeMax int `json:"port_range_max"`
|
||||
|
||||
// The protocol that is matched by the security group rule. Valid values are
|
||||
// "tcp", "udp", "icmp" or an empty string.
|
||||
Protocol string
|
||||
|
||||
// The remote group ID to be associated with this security group rule. You
|
||||
// can specify either RemoteGroupID or RemoteIPPrefix.
|
||||
RemoteGroupID string `json:"remote_group_id"`
|
||||
|
||||
// The remote IP prefix to be associated with this security group rule. You
|
||||
// can specify either RemoteGroupID or RemoteIPPrefix . This attribute
|
||||
// matches the specified IP prefix as the source IP address of the IP packet.
|
||||
RemoteIPPrefix string `json:"remote_ip_prefix"`
|
||||
|
||||
// The owner of this security group rule.
|
||||
TenantID string `json:"tenant_id"`
|
||||
}
|
||||
|
||||
// SecGroupRulePage is the page returned by a pager when traversing over a
|
||||
// collection of security group rules.
|
||||
type SecGroupRulePage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// NextPageURL is invoked when a paginated collection of security group rules has
|
||||
// reached the end of a page and the pager seeks to traverse over a new one. In
|
||||
// order to do this, it needs to construct the next page's URL.
|
||||
func (r SecGroupRulePage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links []gophercloud.Link `json:"security_group_rules_links"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gophercloud.ExtractNextURL(s.Links)
|
||||
}
|
||||
|
||||
// IsEmpty checks whether a SecGroupRulePage struct is empty.
|
||||
func (r SecGroupRulePage) IsEmpty() (bool, error) {
|
||||
is, err := ExtractRules(r)
|
||||
return len(is) == 0, err
|
||||
}
|
||||
|
||||
// ExtractRules accepts a Page struct, specifically a SecGroupRulePage struct,
|
||||
// and extracts the elements into a slice of SecGroupRule structs. In other words,
|
||||
// a generic collection is mapped into a relevant slice.
|
||||
func ExtractRules(r pagination.Page) ([]SecGroupRule, error) {
|
||||
var s struct {
|
||||
SecGroupRules []SecGroupRule `json:"security_group_rules"`
|
||||
}
|
||||
err := (r.(SecGroupRulePage)).ExtractInto(&s)
|
||||
return s.SecGroupRules, err
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a function that accepts a result and extracts a security rule.
|
||||
func (r commonResult) Extract() (*SecGroupRule, error) {
|
||||
var s struct {
|
||||
SecGroupRule *SecGroupRule `json:"security_group_rule"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.SecGroupRule, err
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult represents the result of a get operation.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// DeleteResult represents the result of a delete operation.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
13
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go
generated
vendored
Normal file
13
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package rules
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
const rootPath = "security-group-rules"
|
||||
|
||||
func rootURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL(rootPath)
|
||||
}
|
||||
|
||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL(rootPath, id)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user