Skip to content

Commit

Permalink
Add Swarm management backend
Browse files Browse the repository at this point in the history
As described in our ROADMAP.md, introduce new Swarm management API
endpoints relying on swarmkit to deploy services. It currently vendors
docker/engine-api changes.

This PR is fully backward compatible (joining a Swarm is an optional
feature of the Engine, and existing commands are not impacted).

Signed-off-by: Tonis Tiigi <[email protected]>
Signed-off-by: Victor Vieux <[email protected]>
Signed-off-by: Daniel Nephin <[email protected]>
Signed-off-by: Jana Radhakrishnan <[email protected]>
Signed-off-by: Madhu Venugopal <[email protected]>
  • Loading branch information
tonistiigi committed Jun 14, 2016
1 parent 4479304 commit 534a90a
Show file tree
Hide file tree
Showing 42 changed files with 4,045 additions and 52 deletions.
4 changes: 3 additions & 1 deletion api/server/httputils/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/versions"
"github.com/gorilla/mux"
"google.golang.org/grpc"
)

// httpStatusError is an interface
Expand Down Expand Up @@ -58,6 +59,7 @@ func GetHTTPErrorStatusCode(err error) int {
"wrong login/password": http.StatusUnauthorized,
"unauthorized": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
"this node": http.StatusNotAcceptable,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
Expand Down Expand Up @@ -85,7 +87,7 @@ func MakeErrorHandler(err error) http.HandlerFunc {
}
WriteJSON(w, statusCode, response)
} else {
http.Error(w, err.Error(), statusCode)
http.Error(w, grpc.ErrorDesc(err), statusCode)
}
}
}
3 changes: 1 addition & 2 deletions api/server/router/network/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package network

import (
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
"github.com/docker/engine-api/types/network"
"github.com/docker/libnetwork"
)
Expand All @@ -13,7 +12,7 @@ type Backend interface {
FindNetwork(idName string) (libnetwork.Network, error)
GetNetworkByName(idName string) (libnetwork.Network, error)
GetNetworksByID(partialID string) []libnetwork.Network
FilterNetworks(netFilters filters.Args) ([]libnetwork.Network, error)
GetNetworks() []libnetwork.Network
CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error)
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error
Expand Down
98 changes: 98 additions & 0 deletions api/server/router/network/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package network

import (
"fmt"

"github.com/docker/docker/runconfig"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
)

type filterHandler func([]types.NetworkResource, string) ([]types.NetworkResource, error)

var (
// AcceptedFilters is an acceptable filters for validation
AcceptedFilters = map[string]bool{
"driver": true,
"type": true,
"name": true,
"id": true,
"label": true,
}
)

func filterNetworkByType(nws []types.NetworkResource, netType string) (retNws []types.NetworkResource, err error) {
switch netType {
case "builtin":
for _, nw := range nws {
if runconfig.IsPreDefinedNetwork(nw.Name) {
retNws = append(retNws, nw)
}
}
case "custom":
for _, nw := range nws {
if !runconfig.IsPreDefinedNetwork(nw.Name) {
retNws = append(retNws, nw)
}
}
default:
return nil, fmt.Errorf("Invalid filter: 'type'='%s'", netType)
}
return retNws, nil
}

// filterNetworks filters network list according to user specified filter
// and returns user chosen networks
func filterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.NetworkResource, error) {
// if filter is empty, return original network list
if filter.Len() == 0 {
return nws, nil
}

if err := filter.Validate(AcceptedFilters); err != nil {
return nil, err
}

var displayNet []types.NetworkResource
for _, nw := range nws {
if filter.Include("driver") {
if !filter.ExactMatch("driver", nw.Driver) {
continue
}
}
if filter.Include("name") {
if !filter.Match("name", nw.Name) {
continue
}
}
if filter.Include("id") {
if !filter.Match("id", nw.ID) {
continue
}
}
if filter.Include("label") {
if !filter.MatchKVList("label", nw.Labels) {
continue
}
}
displayNet = append(displayNet, nw)
}

if filter.Include("type") {
var typeNet []types.NetworkResource
errFilter := filter.WalkValues("type", func(fval string) error {
passList, err := filterNetworkByType(displayNet, fval)
if err != nil {
return err
}
typeNet = append(typeNet, passList...)
return nil
})
if errFilter != nil {
return nil, errFilter
}
displayNet = typeNet
}

return displayNet, nil
}
15 changes: 10 additions & 5 deletions api/server/router/network/network.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package network

import "github.com/docker/docker/api/server/router"
import (
"github.com/docker/docker/api/server/router"
"github.com/docker/docker/daemon/cluster"
)

// networkRouter is a router to talk with the network controller
type networkRouter struct {
backend Backend
routes []router.Route
backend Backend
clusterProvider *cluster.Cluster
routes []router.Route
}

// NewRouter initializes a new network router
func NewRouter(b Backend) router.Router {
func NewRouter(b Backend, c *cluster.Cluster) router.Router {
r := &networkRouter{
backend: b,
backend: b,
clusterProvider: c,
}
r.initRoutes()
return r
Expand Down
51 changes: 42 additions & 9 deletions api/server/router/network/network_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,30 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit
return err
}

list := []*types.NetworkResource{}
list := []types.NetworkResource{}

nwList, err := n.backend.FilterNetworks(netFilters)
if err != nil {
return err
if nr, err := n.clusterProvider.GetNetworks(); err == nil {
for _, nw := range nr {
list = append(list, nw)
}
}

for _, nw := range nwList {
list = append(list, buildNetworkResource(nw))
// Combine the network list returned by Docker daemon if it is not already
// returned by the cluster manager
SKIP:
for _, nw := range n.backend.GetNetworks() {
for _, nl := range list {
if nl.ID == nw.ID() {
continue SKIP
}
}
list = append(list, *n.buildNetworkResource(nw))
}

list, err = filterNetworks(list, netFilters)
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, list)
}

Expand All @@ -45,9 +58,12 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r

nw, err := n.backend.FindNetwork(vars["id"])
if err != nil {
if nr, err := n.clusterProvider.GetNetwork(vars["id"]); err == nil {
return httputils.WriteJSON(w, http.StatusOK, nr)
}
return err
}
return httputils.WriteJSON(w, http.StatusOK, buildNetworkResource(nw))
return httputils.WriteJSON(w, http.StatusOK, n.buildNetworkResource(nw))
}

func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
Expand All @@ -67,7 +83,14 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr

nw, err := n.backend.CreateNetwork(create)
if err != nil {
return err
if _, ok := err.(libnetwork.ManagerRedirectError); !ok {
return err
}
id, err := n.clusterProvider.CreateNetwork(create)
if err != nil {
return err
}
nw = &types.NetworkCreateResponse{ID: id}
}

return httputils.WriteJSON(w, http.StatusCreated, nw)
Expand Down Expand Up @@ -121,14 +144,17 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter
if err := httputils.ParseForm(r); err != nil {
return err
}
if _, err := n.clusterProvider.GetNetwork(vars["id"]); err == nil {
return n.clusterProvider.RemoveNetwork(vars["id"])
}
if err := n.backend.DeleteNetwork(vars["id"]); err != nil {
return err
}
w.WriteHeader(http.StatusNoContent)
return nil
}

func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
r := &types.NetworkResource{}
if nw == nil {
return r
Expand All @@ -138,6 +164,13 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
r.Name = nw.Name()
r.ID = nw.ID()
r.Scope = info.Scope()
if n.clusterProvider.IsManager() {
if _, err := n.clusterProvider.GetNetwork(nw.Name()); err == nil {
r.Scope = "swarm"
}
} else if info.Dynamic() {
r.Scope = "swarm"
}
r.Driver = nw.Type()
r.EnableIPv6 = info.IPv6Enabled()
r.Internal = info.Internal()
Expand Down
26 changes: 26 additions & 0 deletions api/server/router/swarm/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package swarm

import (
basictypes "github.com/docker/engine-api/types"
types "github.com/docker/engine-api/types/swarm"
)

// Backend abstracts an swarm commands manager.
type Backend interface {
Init(req types.InitRequest) (string, error)
Join(req types.JoinRequest) error
Leave(force bool) error
Inspect() (types.Swarm, error)
Update(uint64, types.Spec) error
GetServices(basictypes.ServiceListOptions) ([]types.Service, error)
GetService(string) (types.Service, error)
CreateService(types.ServiceSpec) (string, error)
UpdateService(string, uint64, types.ServiceSpec) error
RemoveService(string) error
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
GetNode(string) (types.Node, error)
UpdateNode(string, uint64, types.NodeSpec) error
RemoveNode(string) error
GetTasks(basictypes.TaskListOptions) ([]types.Task, error)
GetTask(string) (types.Task, error)
}
44 changes: 44 additions & 0 deletions api/server/router/swarm/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package swarm

import "github.com/docker/docker/api/server/router"

// buildRouter is a router to talk with the build controller
type swarmRouter struct {
backend Backend
routes []router.Route
}

// NewRouter initializes a new build router
func NewRouter(b Backend) router.Router {
r := &swarmRouter{
backend: b,
}
r.initRoutes()
return r
}

// Routes returns the available routers to the swarm controller
func (sr *swarmRouter) Routes() []router.Route {
return sr.routes
}

func (sr *swarmRouter) initRoutes() {
sr.routes = []router.Route{
router.NewPostRoute("/swarm/init", sr.initCluster),
router.NewPostRoute("/swarm/join", sr.joinCluster),
router.NewPostRoute("/swarm/leave", sr.leaveCluster),
router.NewGetRoute("/swarm", sr.inspectCluster),
router.NewPostRoute("/swarm/update", sr.updateCluster),
router.NewGetRoute("/services", sr.getServices),
router.NewGetRoute("/services/{id:.*}", sr.getService),
router.NewPostRoute("/services/create", sr.createService),
router.NewPostRoute("/services/{id:.*}/update", sr.updateService),
router.NewDeleteRoute("/services/{id:.*}", sr.removeService),
router.NewGetRoute("/nodes", sr.getNodes),
router.NewGetRoute("/nodes/{id:.*}", sr.getNode),
router.NewDeleteRoute("/nodes/{id:.*}", sr.removeNode),
router.NewPostRoute("/nodes/{id:.*}/update", sr.updateNode),
router.NewGetRoute("/tasks", sr.getTasks),
router.NewGetRoute("/tasks/{id:.*}", sr.getTask),
}
}
Loading

0 comments on commit 534a90a

Please sign in to comment.