VMware vSphere Integrated Containers provider (#206)
* Add Virtual Kubelet provider for VIC Initial virtual kubelet provider for VMware VIC. This provider currently handles creating and starting of a pod VM via the VIC portlayer and persona server. Image store handling via the VIC persona server. This provider currently requires the feature/wolfpack branch of VIC. * Added pod stop and delete. Also added node capacity. Added the ability to stop and delete pod VMs via VIC. Also retrieve node capacity information from the VCH. * Cleanup and readme file Some file clean up and added a Readme.md markdown file for the VIC provider. * Cleaned up errors, added function comments, moved operation code 1. Cleaned up error handling. Set standard for creating errors. 2. Added method prototype comments for all interface functions. 3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder. * Add mocking code and unit tests for podcache, podcreator, and podstarter Used the unit test framework used in VIC to handle assertions in the provider's unit test. Mocking code generated using OSS project mockery, which is compatible with the testify assertion framework. * Vendored packages for the VIC provider Requires feature/wolfpack branch of VIC and a few specific commit sha of projects used within VIC. * Implementation of POD Stopper and Deleter unit tests (#4) * Updated files for initial PR
This commit is contained in:
35
vendor/github.com/docker/libnetwork/ipams/builtin/builtin.go
generated
vendored
Normal file
35
vendor/github.com/docker/libnetwork/ipams/builtin/builtin.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipam"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
)
|
||||
|
||||
// Init registers the built-in ipam service with libnetwork
|
||||
func Init(ic ipamapi.Callback, l, g interface{}) error {
|
||||
var (
|
||||
ok bool
|
||||
localDs, globalDs datastore.DataStore
|
||||
)
|
||||
|
||||
if l != nil {
|
||||
if localDs, ok = l.(datastore.DataStore); !ok {
|
||||
return fmt.Errorf("incorrect local datastore passed to built-in ipam init")
|
||||
}
|
||||
}
|
||||
|
||||
if g != nil {
|
||||
if globalDs, ok = g.(datastore.DataStore); !ok {
|
||||
return fmt.Errorf("incorrect global datastore passed to built-in ipam init")
|
||||
}
|
||||
}
|
||||
a, err := ipam.NewAllocator(localDs, globalDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ic.RegisterIpamDriver(ipamapi.DefaultIPAM, a)
|
||||
}
|
||||
90
vendor/github.com/docker/libnetwork/ipams/remote/api/api.go
generated
vendored
Normal file
90
vendor/github.com/docker/libnetwork/ipams/remote/api/api.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Package api defines the data structure to be used in the request/response
|
||||
// messages between libnetwork and the remote ipam plugin
|
||||
package api
|
||||
|
||||
import "github.com/docker/libnetwork/ipamapi"
|
||||
|
||||
// Response is the basic response structure used in all responses
|
||||
type Response struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
// IsSuccess returns wheter the plugin response is successful
|
||||
func (r *Response) IsSuccess() bool {
|
||||
return r.Error == ""
|
||||
}
|
||||
|
||||
// GetError returns the error from the response, if any.
|
||||
func (r *Response) GetError() string {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
// GetCapabilityResponse is the response of GetCapability request
|
||||
type GetCapabilityResponse struct {
|
||||
Response
|
||||
RequiresMACAddress bool
|
||||
}
|
||||
|
||||
// ToCapability converts the capability response into the internal ipam driver capaility structure
|
||||
func (capRes GetCapabilityResponse) ToCapability() *ipamapi.Capability {
|
||||
return &ipamapi.Capability{RequiresMACAddress: capRes.RequiresMACAddress}
|
||||
}
|
||||
|
||||
// GetAddressSpacesResponse is the response to the ``get default address spaces`` request message
|
||||
type GetAddressSpacesResponse struct {
|
||||
Response
|
||||
LocalDefaultAddressSpace string
|
||||
GlobalDefaultAddressSpace string
|
||||
}
|
||||
|
||||
// RequestPoolRequest represents the expected data in a ``request address pool`` request message
|
||||
type RequestPoolRequest struct {
|
||||
AddressSpace string
|
||||
Pool string
|
||||
SubPool string
|
||||
Options map[string]string
|
||||
V6 bool
|
||||
}
|
||||
|
||||
// RequestPoolResponse represents the response message to a ``request address pool`` request
|
||||
type RequestPoolResponse struct {
|
||||
Response
|
||||
PoolID string
|
||||
Pool string // CIDR format
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
// ReleasePoolRequest represents the expected data in a ``release address pool`` request message
|
||||
type ReleasePoolRequest struct {
|
||||
PoolID string
|
||||
}
|
||||
|
||||
// ReleasePoolResponse represents the response message to a ``release address pool`` request
|
||||
type ReleasePoolResponse struct {
|
||||
Response
|
||||
}
|
||||
|
||||
// RequestAddressRequest represents the expected data in a ``request address`` request message
|
||||
type RequestAddressRequest struct {
|
||||
PoolID string
|
||||
Address string
|
||||
Options map[string]string
|
||||
}
|
||||
|
||||
// RequestAddressResponse represents the expected data in the response message to a ``request address`` request
|
||||
type RequestAddressResponse struct {
|
||||
Response
|
||||
Address string // in CIDR format
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
// ReleaseAddressRequest represents the expected data in a ``release address`` request message
|
||||
type ReleaseAddressRequest struct {
|
||||
PoolID string
|
||||
Address string
|
||||
}
|
||||
|
||||
// ReleaseAddressResponse represents the response message to a ``release address`` request
|
||||
type ReleaseAddressResponse struct {
|
||||
Response
|
||||
}
|
||||
126
vendor/github.com/docker/libnetwork/ipams/remote/remote.go
generated
vendored
Normal file
126
vendor/github.com/docker/libnetwork/ipams/remote/remote.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
type allocator struct {
|
||||
endpoint *plugins.Client
|
||||
name string
|
||||
}
|
||||
|
||||
// PluginResponse is the interface for the plugin request responses
|
||||
type PluginResponse interface {
|
||||
IsSuccess() bool
|
||||
GetError() string
|
||||
}
|
||||
|
||||
func newAllocator(name string, client *plugins.Client) ipamapi.Ipam {
|
||||
a := &allocator{name: name, endpoint: client}
|
||||
return a
|
||||
}
|
||||
|
||||
// Init registers a remote ipam when its plugin is activated
|
||||
func Init(cb ipamapi.Callback, l, g interface{}) error {
|
||||
plugins.Handle(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) {
|
||||
a := newAllocator(name, client)
|
||||
if cps, err := a.(*allocator).getCapabilities(); err == nil {
|
||||
if err := cb.RegisterIpamDriverWithCapabilities(name, a, cps); err != nil {
|
||||
log.Errorf("error registering remote ipam driver %s due to %v", name, err)
|
||||
}
|
||||
} else {
|
||||
log.Infof("remote ipam driver %s does not support capabilities", name)
|
||||
log.Debug(err)
|
||||
if err := cb.RegisterIpamDriver(name, a); err != nil {
|
||||
log.Errorf("error registering remote ipam driver %s due to %v", name, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *allocator) call(methodName string, arg interface{}, retVal PluginResponse) error {
|
||||
method := ipamapi.PluginEndpointType + "." + methodName
|
||||
err := a.endpoint.Call(method, arg, retVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !retVal.IsSuccess() {
|
||||
return fmt.Errorf("remote: %s", retVal.GetError())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *allocator) getCapabilities() (*ipamapi.Capability, error) {
|
||||
var res api.GetCapabilityResponse
|
||||
if err := a.call("GetCapabilities", nil, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.ToCapability(), nil
|
||||
}
|
||||
|
||||
// GetDefaultAddressSpaces returns the local and global default address spaces
|
||||
func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
res := &api.GetAddressSpacesResponse{}
|
||||
if err := a.call("GetDefaultAddressSpaces", nil, res); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return res.LocalDefaultAddressSpace, res.GlobalDefaultAddressSpace, nil
|
||||
}
|
||||
|
||||
// RequestPool requests an address pool in the specified address space
|
||||
func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
||||
req := &api.RequestPoolRequest{AddressSpace: addressSpace, Pool: pool, SubPool: subPool, Options: options, V6: v6}
|
||||
res := &api.RequestPoolResponse{}
|
||||
if err := a.call("RequestPool", req, res); err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
retPool, err := types.ParseCIDR(res.Pool)
|
||||
return res.PoolID, retPool, res.Data, err
|
||||
}
|
||||
|
||||
// ReleasePool removes an address pool from the specified address space
|
||||
func (a *allocator) ReleasePool(poolID string) error {
|
||||
req := &api.ReleasePoolRequest{PoolID: poolID}
|
||||
res := &api.ReleasePoolResponse{}
|
||||
return a.call("ReleasePool", req, res)
|
||||
}
|
||||
|
||||
// RequestAddress requests an address from the address pool
|
||||
func (a *allocator) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) {
|
||||
var (
|
||||
prefAddress string
|
||||
retAddress *net.IPNet
|
||||
err error
|
||||
)
|
||||
if address != nil {
|
||||
prefAddress = address.String()
|
||||
}
|
||||
req := &api.RequestAddressRequest{PoolID: poolID, Address: prefAddress, Options: options}
|
||||
res := &api.RequestAddressResponse{}
|
||||
if err := a.call("RequestAddress", req, res); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if res.Address != "" {
|
||||
retAddress, err = types.ParseCIDR(res.Address)
|
||||
}
|
||||
return retAddress, res.Data, err
|
||||
}
|
||||
|
||||
// ReleaseAddress releases the address from the specified address pool
|
||||
func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
|
||||
var relAddress string
|
||||
if address != nil {
|
||||
relAddress = address.String()
|
||||
}
|
||||
req := &api.ReleaseAddressRequest{PoolID: poolID, Address: relAddress}
|
||||
res := &api.ReleaseAddressResponse{}
|
||||
return a.call("ReleaseAddress", req, res)
|
||||
}
|
||||
292
vendor/github.com/docker/libnetwork/ipams/remote/remote_test.go
generated
vendored
Normal file
292
vendor/github.com/docker/libnetwork/ipams/remote/remote_test.go
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
package remote
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
_ "github.com/docker/libnetwork/testutils"
|
||||
)
|
||||
|
||||
func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
|
||||
err = json.NewDecoder(r.Body).Decode(&res)
|
||||
return
|
||||
}
|
||||
|
||||
func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) {
|
||||
mux.HandleFunc(fmt.Sprintf("/%s.%s", ipamapi.PluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) {
|
||||
ask, err := decodeToMap(r)
|
||||
if err != nil && err != io.EOF {
|
||||
t.Fatal(err)
|
||||
}
|
||||
answer := h(ask)
|
||||
err = json.NewEncoder(w).Encode(&answer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
|
||||
if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server := httptest.NewServer(mux)
|
||||
if server == nil {
|
||||
t.Fatal("Failed to start a HTTP Server")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(fmt.Sprintf("/etc/docker/plugins/%s.spec", name), []byte(server.URL), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||
fmt.Fprintf(w, `{"Implements": ["%s"]}`, ipamapi.PluginEndpointType)
|
||||
})
|
||||
|
||||
return func() {
|
||||
if err := os.RemoveAll("/etc/docker/plugins"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCapabilities(t *testing.T) {
|
||||
var plugin = "test-ipam-driver-capabilities"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
defer setupPlugin(t, plugin, mux)()
|
||||
|
||||
handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} {
|
||||
return map[string]interface{}{
|
||||
"RequiresMACAddress": true,
|
||||
}
|
||||
})
|
||||
|
||||
p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := newAllocator(plugin, p.Client)
|
||||
|
||||
caps, err := d.(*allocator).getCapabilities()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !caps.RequiresMACAddress {
|
||||
t.Fatalf("Unexpected capability: %v", caps)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCapabilitiesFromLegacyDriver(t *testing.T) {
|
||||
var plugin = "test-ipam-legacy-driver"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
defer setupPlugin(t, plugin, mux)()
|
||||
|
||||
p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := newAllocator(plugin, p.Client)
|
||||
|
||||
if _, err := d.(*allocator).getCapabilities(); err == nil {
|
||||
t.Fatalf("Expected error, but got Success %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultAddressSpaces(t *testing.T) {
|
||||
var plugin = "test-ipam-driver-addr-spaces"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
defer setupPlugin(t, plugin, mux)()
|
||||
|
||||
handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} {
|
||||
return map[string]interface{}{
|
||||
"LocalDefaultAddressSpace": "white",
|
||||
"GlobalDefaultAddressSpace": "blue",
|
||||
}
|
||||
})
|
||||
|
||||
p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := newAllocator(plugin, p.Client)
|
||||
|
||||
l, g, err := d.(*allocator).GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if l != "white" || g != "blue" {
|
||||
t.Fatalf("Unexpected default local and global address spaces: %s, %s", l, g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteDriver(t *testing.T) {
|
||||
var plugin = "test-ipam-driver"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
defer setupPlugin(t, plugin, mux)()
|
||||
|
||||
handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} {
|
||||
return map[string]interface{}{
|
||||
"LocalDefaultAddressSpace": "white",
|
||||
"GlobalDefaultAddressSpace": "blue",
|
||||
}
|
||||
})
|
||||
|
||||
handle(t, mux, "RequestPool", func(msg map[string]interface{}) interface{} {
|
||||
as := "white"
|
||||
if v, ok := msg["AddressSpace"]; ok && v.(string) != "" {
|
||||
as = v.(string)
|
||||
}
|
||||
|
||||
pl := "172.18.0.0/16"
|
||||
sp := ""
|
||||
if v, ok := msg["Pool"]; ok && v.(string) != "" {
|
||||
pl = v.(string)
|
||||
}
|
||||
if v, ok := msg["SubPool"]; ok && v.(string) != "" {
|
||||
sp = v.(string)
|
||||
}
|
||||
pid := fmt.Sprintf("%s/%s", as, pl)
|
||||
if sp != "" {
|
||||
pid = fmt.Sprintf("%s/%s", pid, sp)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"PoolID": pid,
|
||||
"Pool": pl,
|
||||
"Data": map[string]string{"DNS": "8.8.8.8"},
|
||||
}
|
||||
})
|
||||
|
||||
handle(t, mux, "ReleasePool", func(msg map[string]interface{}) interface{} {
|
||||
if _, ok := msg["PoolID"]; !ok {
|
||||
t.Fatalf("Missing PoolID in Release request")
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
})
|
||||
|
||||
handle(t, mux, "RequestAddress", func(msg map[string]interface{}) interface{} {
|
||||
if _, ok := msg["PoolID"]; !ok {
|
||||
t.Fatalf("Missing PoolID in address request")
|
||||
}
|
||||
prefAddr := ""
|
||||
if v, ok := msg["Address"]; ok {
|
||||
prefAddr = v.(string)
|
||||
}
|
||||
ip := prefAddr
|
||||
if ip == "" {
|
||||
ip = "172.20.0.34"
|
||||
}
|
||||
ip = fmt.Sprintf("%s/16", ip)
|
||||
return map[string]interface{}{
|
||||
"Address": ip,
|
||||
}
|
||||
})
|
||||
|
||||
handle(t, mux, "ReleaseAddress", func(msg map[string]interface{}) interface{} {
|
||||
if _, ok := msg["PoolID"]; !ok {
|
||||
t.Fatalf("Missing PoolID in address request")
|
||||
}
|
||||
if _, ok := msg["Address"]; !ok {
|
||||
t.Fatalf("Missing Address in release address request")
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
})
|
||||
|
||||
p, err := plugins.Get(plugin, ipamapi.PluginEndpointType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := newAllocator(plugin, p.Client)
|
||||
|
||||
l, g, err := d.(*allocator).GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if l != "white" || g != "blue" {
|
||||
t.Fatalf("Unexpected default local/global address spaces: %s, %s", l, g)
|
||||
}
|
||||
|
||||
// Request any pool
|
||||
poolID, pool, _, err := d.RequestPool("white", "", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if poolID != "white/172.18.0.0/16" {
|
||||
t.Fatalf("Unexpected pool id: %s", poolID)
|
||||
}
|
||||
if pool == nil || pool.String() != "172.18.0.0/16" {
|
||||
t.Fatalf("Unexpected pool: %s", pool)
|
||||
}
|
||||
|
||||
// Request specific pool
|
||||
poolID2, pool2, ops, err := d.RequestPool("white", "172.20.0.0/16", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if poolID2 != "white/172.20.0.0/16" {
|
||||
t.Fatalf("Unexpected pool id: %s", poolID2)
|
||||
}
|
||||
if pool2 == nil || pool2.String() != "172.20.0.0/16" {
|
||||
t.Fatalf("Unexpected pool: %s", pool2)
|
||||
}
|
||||
if dns, ok := ops["DNS"]; !ok || dns != "8.8.8.8" {
|
||||
t.Fatalf("Missing options")
|
||||
}
|
||||
|
||||
// Request specific pool and subpool
|
||||
poolID3, pool3, _, err := d.RequestPool("white", "172.20.0.0/16", "172.20.3.0/24" /*nil*/, map[string]string{"culo": "yes"}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if poolID3 != "white/172.20.0.0/16/172.20.3.0/24" {
|
||||
t.Fatalf("Unexpected pool id: %s", poolID3)
|
||||
}
|
||||
if pool3 == nil || pool3.String() != "172.20.0.0/16" {
|
||||
t.Fatalf("Unexpected pool: %s", pool3)
|
||||
}
|
||||
|
||||
// Request any address
|
||||
addr, _, err := d.RequestAddress(poolID2, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if addr == nil || addr.String() != "172.20.0.34/16" {
|
||||
t.Fatalf("Unexpected address: %s", addr)
|
||||
}
|
||||
|
||||
// Request specific address
|
||||
addr2, _, err := d.RequestAddress(poolID2, net.ParseIP("172.20.1.45"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if addr2 == nil || addr2.String() != "172.20.1.45/16" {
|
||||
t.Fatalf("Unexpected address: %s", addr2)
|
||||
}
|
||||
|
||||
// Release address
|
||||
err = d.ReleaseAddress(poolID, net.ParseIP("172.18.1.45"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user