mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
03504cab65
1. replaced --net option for service UI with SERVICE.[NETWORK] format 2. Making using of the default network/driver backend support 3. NetworkName and NetworkType from the UI/API can be empty string and it will be replaced with DefaultNetwork and DefaultDriver As per the design goals, we wanted to keep libnetwork core free of handling defaults. Rather, the clients (docker & dnet) must handle the defaultness of these entities. Also, since there is no API to get these Default values from the backend, UI will not handle the default values either. Hence, this falls under the responsibility of the API layer to handle this specific case. Signed-off-by: Madhu Venugopal <madhu@docker.com>
384 lines
8.5 KiB
Go
384 lines
8.5 KiB
Go
package libnetwork
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sync"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/pkg/stringid"
|
|
"github.com/docker/libnetwork/config"
|
|
"github.com/docker/libnetwork/datastore"
|
|
"github.com/docker/libnetwork/driverapi"
|
|
"github.com/docker/libnetwork/netlabel"
|
|
"github.com/docker/libnetwork/options"
|
|
"github.com/docker/libnetwork/types"
|
|
)
|
|
|
|
// A Network represents a logical connectivity zone that containers may
|
|
// join using the Link method. A Network is managed by a specific driver.
|
|
type Network interface {
|
|
// A user chosen name for this network.
|
|
Name() string
|
|
|
|
// A system generated id for this network.
|
|
ID() string
|
|
|
|
// The type of network, which corresponds to its managing driver.
|
|
Type() string
|
|
|
|
// Create a new endpoint to this network symbolically identified by the
|
|
// specified unique name. The options parameter carry driver specific options.
|
|
// Labels support will be added in the near future.
|
|
CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error)
|
|
|
|
// Delete the network.
|
|
Delete() error
|
|
|
|
// Endpoints returns the list of Endpoint(s) in this network.
|
|
Endpoints() []Endpoint
|
|
|
|
// WalkEndpoints uses the provided function to walk the Endpoints
|
|
WalkEndpoints(walker EndpointWalker)
|
|
|
|
// EndpointByName returns the Endpoint which has the passed name. If not found, the error ErrNoSuchEndpoint is returned.
|
|
EndpointByName(name string) (Endpoint, error)
|
|
|
|
// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
|
|
EndpointByID(id string) (Endpoint, error)
|
|
}
|
|
|
|
// EndpointWalker is a client provided function which will be used to walk the Endpoints.
|
|
// When the function returns true, the walk will stop.
|
|
type EndpointWalker func(ep Endpoint) bool
|
|
|
|
type network struct {
|
|
ctrlr *controller
|
|
name string
|
|
networkType string
|
|
id types.UUID
|
|
driver driverapi.Driver
|
|
enableIPv6 bool
|
|
endpointCnt uint64
|
|
endpoints endpointTable
|
|
generic options.Generic
|
|
dbIndex uint64
|
|
sync.Mutex
|
|
}
|
|
|
|
func (n *network) Name() string {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
|
|
return n.name
|
|
}
|
|
|
|
func (n *network) ID() string {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
|
|
return string(n.id)
|
|
}
|
|
|
|
func (n *network) Type() string {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
|
|
if n.driver == nil {
|
|
return ""
|
|
}
|
|
|
|
return n.driver.Type()
|
|
}
|
|
|
|
func (n *network) Key() []string {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
return []string{datastore.NetworkKeyPrefix, string(n.id)}
|
|
}
|
|
|
|
func (n *network) KeyPrefix() []string {
|
|
return []string{datastore.NetworkKeyPrefix}
|
|
}
|
|
|
|
func (n *network) Value() []byte {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
b, err := json.Marshal(n)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (n *network) Index() uint64 {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
return n.dbIndex
|
|
}
|
|
|
|
func (n *network) SetIndex(index uint64) {
|
|
n.Lock()
|
|
n.dbIndex = index
|
|
n.Unlock()
|
|
}
|
|
|
|
func (n *network) EndpointCnt() uint64 {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
return n.endpointCnt
|
|
}
|
|
|
|
func (n *network) IncEndpointCnt() {
|
|
n.Lock()
|
|
n.endpointCnt++
|
|
n.Unlock()
|
|
}
|
|
|
|
func (n *network) DecEndpointCnt() {
|
|
n.Lock()
|
|
n.endpointCnt--
|
|
n.Unlock()
|
|
}
|
|
|
|
// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
|
|
func (n *network) MarshalJSON() ([]byte, error) {
|
|
netMap := make(map[string]interface{})
|
|
netMap["name"] = n.name
|
|
netMap["id"] = string(n.id)
|
|
netMap["networkType"] = n.networkType
|
|
netMap["endpointCnt"] = n.endpointCnt
|
|
netMap["enableIPv6"] = n.enableIPv6
|
|
netMap["generic"] = n.generic
|
|
return json.Marshal(netMap)
|
|
}
|
|
|
|
// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
|
|
func (n *network) UnmarshalJSON(b []byte) (err error) {
|
|
var netMap map[string]interface{}
|
|
if err := json.Unmarshal(b, &netMap); err != nil {
|
|
return err
|
|
}
|
|
n.name = netMap["name"].(string)
|
|
n.id = types.UUID(netMap["id"].(string))
|
|
n.networkType = netMap["networkType"].(string)
|
|
n.endpointCnt = uint64(netMap["endpointCnt"].(float64))
|
|
n.enableIPv6 = netMap["enableIPv6"].(bool)
|
|
if netMap["generic"] != nil {
|
|
n.generic = netMap["generic"].(map[string]interface{})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NetworkOption is a option setter function type used to pass varios options to
|
|
// NewNetwork method. The various setter functions of type NetworkOption are
|
|
// provided by libnetwork, they look like NetworkOptionXXXX(...)
|
|
type NetworkOption func(n *network)
|
|
|
|
// NetworkOptionGeneric function returns an option setter for a Generic option defined
|
|
// in a Dictionary of Key-Value pair
|
|
func NetworkOptionGeneric(generic map[string]interface{}) NetworkOption {
|
|
return func(n *network) {
|
|
n.generic = generic
|
|
if _, ok := generic[netlabel.EnableIPv6]; ok {
|
|
n.enableIPv6 = generic[netlabel.EnableIPv6].(bool)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *network) processOptions(options ...NetworkOption) {
|
|
for _, opt := range options {
|
|
if opt != nil {
|
|
opt(n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *network) Delete() error {
|
|
var err error
|
|
|
|
n.Lock()
|
|
ctrlr := n.ctrlr
|
|
n.Unlock()
|
|
|
|
ctrlr.Lock()
|
|
_, ok := ctrlr.networks[n.id]
|
|
ctrlr.Unlock()
|
|
|
|
if !ok {
|
|
return &UnknownNetworkError{name: n.name, id: string(n.id)}
|
|
}
|
|
|
|
numEps := n.EndpointCnt()
|
|
if numEps != 0 {
|
|
return &ActiveEndpointsError{name: n.name, id: string(n.id)}
|
|
}
|
|
|
|
// deleteNetworkFromStore performs an atomic delete operation and the network.endpointCnt field will help
|
|
// prevent any possible race between endpoint join and network delete
|
|
if err = ctrlr.deleteNetworkFromStore(n); err != nil {
|
|
if err == datastore.ErrKeyModified {
|
|
return types.InternalErrorf("operation in progress. delete failed for network %s. Please try again.")
|
|
}
|
|
return err
|
|
}
|
|
|
|
if err = n.deleteNetwork(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *network) deleteNetwork() error {
|
|
n.Lock()
|
|
id := n.id
|
|
d := n.driver
|
|
n.ctrlr.Lock()
|
|
delete(n.ctrlr.networks, id)
|
|
n.ctrlr.Unlock()
|
|
n.Unlock()
|
|
|
|
if err := d.DeleteNetwork(n.id); err != nil {
|
|
// Forbidden Errors should be honored
|
|
if _, ok := err.(types.ForbiddenError); ok {
|
|
n.ctrlr.Lock()
|
|
n.ctrlr.networks[n.id] = n
|
|
n.ctrlr.Unlock()
|
|
return err
|
|
}
|
|
log.Warnf("driver error deleting network %s : %v", n.name, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *network) addEndpoint(ep *endpoint) error {
|
|
var err error
|
|
n.Lock()
|
|
n.endpoints[ep.id] = ep
|
|
d := n.driver
|
|
n.Unlock()
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
n.Lock()
|
|
delete(n.endpoints, ep.id)
|
|
n.Unlock()
|
|
}
|
|
}()
|
|
|
|
err = d.CreateEndpoint(n.id, ep.id, ep, ep.generic)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) {
|
|
var err error
|
|
if !config.IsValidName(name) {
|
|
return nil, ErrInvalidName(name)
|
|
}
|
|
|
|
if _, err = n.EndpointByName(name); err == nil {
|
|
return nil, types.ForbiddenErrorf("service endpoint with name %s already exists", name)
|
|
}
|
|
|
|
ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})}
|
|
ep.id = types.UUID(stringid.GenerateRandomID())
|
|
ep.network = n
|
|
ep.processOptions(options...)
|
|
|
|
n.Lock()
|
|
ctrlr := n.ctrlr
|
|
n.Unlock()
|
|
|
|
n.IncEndpointCnt()
|
|
if err = ctrlr.updateNetworkToStore(n); err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
n.DecEndpointCnt()
|
|
if err = ctrlr.updateNetworkToStore(n); err != nil {
|
|
log.Warnf("endpoint count cleanup failed when updating network for %s : %v", name, err)
|
|
}
|
|
}
|
|
}()
|
|
if err = n.addEndpoint(ep); err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if e := ep.Delete(); ep != nil {
|
|
log.Warnf("cleaning up endpoint failed %s : %v", name, e)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if err = ctrlr.updateEndpointToStore(ep); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ep, nil
|
|
}
|
|
|
|
func (n *network) Endpoints() []Endpoint {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
list := make([]Endpoint, 0, len(n.endpoints))
|
|
for _, e := range n.endpoints {
|
|
list = append(list, e)
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
func (n *network) WalkEndpoints(walker EndpointWalker) {
|
|
for _, e := range n.Endpoints() {
|
|
if walker(e) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *network) EndpointByName(name string) (Endpoint, error) {
|
|
if name == "" {
|
|
return nil, ErrInvalidName(name)
|
|
}
|
|
var e Endpoint
|
|
|
|
s := func(current Endpoint) bool {
|
|
if current.Name() == name {
|
|
e = current
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
n.WalkEndpoints(s)
|
|
|
|
if e == nil {
|
|
return nil, ErrNoSuchEndpoint(name)
|
|
}
|
|
|
|
return e, nil
|
|
}
|
|
|
|
func (n *network) EndpointByID(id string) (Endpoint, error) {
|
|
if id == "" {
|
|
return nil, ErrInvalidID(id)
|
|
}
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
if e, ok := n.endpoints[types.UUID(id)]; ok {
|
|
return e, nil
|
|
}
|
|
return nil, ErrNoSuchEndpoint(id)
|
|
}
|
|
|
|
func (n *network) isGlobalScoped() (bool, error) {
|
|
n.Lock()
|
|
c := n.ctrlr
|
|
n.Unlock()
|
|
return c.isDriverGlobalScoped(n.networkType)
|
|
}
|