2018-02-05 21:05:59 +00:00
package network // import "github.com/docker/docker/api/server/router/network"
2015-09-25 10:19:17 +00:00
import (
2018-04-19 22:30:59 +00:00
"context"
2015-09-25 10:19:17 +00:00
"net/http"
2017-03-09 19:42:10 +00:00
"strconv"
2017-01-19 11:04:26 +00:00
"strings"
2015-09-25 10:19:17 +00:00
"github.com/docker/docker/api/server/httputils"
2016-09-06 18:46:37 +00:00
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
2017-02-02 13:48:53 +00:00
"github.com/docker/docker/api/types/versions"
2018-01-11 19:53:06 +00:00
"github.com/docker/docker/errdefs"
2021-04-06 00:24:47 +00:00
"github.com/docker/docker/libnetwork"
netconst "github.com/docker/docker/libnetwork/datastore"
2017-07-19 14:20:13 +00:00
"github.com/pkg/errors"
2015-09-25 10:19:17 +00:00
)
func ( n * networkRouter ) getNetworksList ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2018-05-23 14:03:02 +00:00
filter , err := filters . FromJSON ( r . Form . Get ( "filters" ) )
2015-09-25 10:19:17 +00:00
if err != nil {
return err
}
2018-05-23 14:03:02 +00:00
if err := network . ValidateFilters ( filter ) ; err != nil {
2022-03-02 12:52:51 +00:00
return err
2016-12-29 08:10:40 +00:00
}
2018-05-23 14:03:02 +00:00
var list [ ] types . NetworkResource
nr , err := n . cluster . GetNetworks ( filter )
if err == nil {
list = nr
2015-09-25 10:19:17 +00:00
}
2015-11-10 08:57:06 +00:00
2016-06-14 02:52:49 +00:00
// Combine the network list returned by Docker daemon if it is not already
// returned by the cluster manager
2018-05-23 14:03:02 +00:00
localNetworks , err := n . backend . GetNetworks ( filter , types . NetworkListConfig { Detailed : versions . LessThan ( httputils . VersionFromContext ( ctx ) , "1.28" ) } )
if err != nil {
return err
}
2017-02-02 13:48:53 +00:00
2018-05-23 14:03:02 +00:00
var idx map [ string ] bool
if len ( list ) > 0 {
idx = make ( map [ string ] bool , len ( list ) )
for _ , n := range list {
idx [ n . ID ] = true
2017-02-02 13:48:53 +00:00
}
2018-05-23 14:03:02 +00:00
}
for _ , n := range localNetworks {
if idx [ n . ID ] {
continue
}
list = append ( list , n )
2015-11-10 08:57:06 +00:00
}
2018-05-23 14:03:02 +00:00
if list == nil {
list = [ ] types . NetworkResource { }
2016-06-14 02:52:49 +00:00
}
2018-05-23 14:03:02 +00:00
2015-09-25 10:19:17 +00:00
return httputils . WriteJSON ( w , http . StatusOK , list )
}
2017-07-19 14:20:13 +00:00
type invalidRequestError struct {
cause error
}
func ( e invalidRequestError ) Error ( ) string {
return e . cause . Error ( )
}
func ( e invalidRequestError ) InvalidParameter ( ) { }
type ambigousResultsError string
func ( e ambigousResultsError ) Error ( ) string {
return "network " + string ( e ) + " is ambiguous"
}
func ( ambigousResultsError ) InvalidParameter ( ) { }
2017-09-29 02:13:44 +00:00
func nameConflict ( name string ) error {
2017-11-29 04:09:37 +00:00
return errdefs . Conflict ( libnetwork . NetworkNameError ( name ) )
2017-09-29 02:13:44 +00:00
}
2015-09-25 10:19:17 +00:00
func ( n * networkRouter ) getNetwork ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2017-01-19 11:04:26 +00:00
term := vars [ "id" ]
2017-03-09 19:42:10 +00:00
var (
verbose bool
err error
)
if v := r . URL . Query ( ) . Get ( "verbose" ) ; v != "" {
if verbose , err = strconv . ParseBool ( v ) ; err != nil {
2017-07-19 14:20:13 +00:00
return errors . Wrapf ( invalidRequestError { err } , "invalid value for verbose: %s" , v )
2017-03-09 19:42:10 +00:00
}
}
2017-06-11 18:04:35 +00:00
scope := r . URL . Query ( ) . Get ( "scope" )
2017-01-19 11:04:26 +00:00
// In case multiple networks have duplicate names, return error.
// TODO (yongtang): should we wrap with version here for backward compatibility?
// First find based on full ID, return immediately once one is found.
// If a network appears both in swarm and local, assume it is in local first
// For full name and partial ID, save the result first, and process later
// in case multiple records was found based on the same term
listByFullName := map [ string ] types . NetworkResource { }
listByPartialID := map [ string ] types . NetworkResource { }
2018-05-23 14:03:02 +00:00
// TODO(@cpuguy83): All this logic for figuring out which network to return does not belong here
// Instead there should be a backend function to just get one network.
filter := filters . NewArgs ( filters . Arg ( "idOrName" , term ) )
if scope != "" {
filter . Add ( "scope" , scope )
}
nw , _ := n . backend . GetNetworks ( filter , types . NetworkListConfig { Detailed : true , Verbose : verbose } )
2017-01-19 11:04:26 +00:00
for _ , network := range nw {
2018-05-23 14:03:02 +00:00
if network . ID == term {
return httputils . WriteJSON ( w , http . StatusOK , network )
2017-01-19 11:04:26 +00:00
}
2018-05-23 14:03:02 +00:00
if network . Name == term {
2017-01-19 11:04:26 +00:00
// No need to check the ID collision here as we are still in
// local scope and the network ID is unique in this scope.
2018-05-23 14:03:02 +00:00
listByFullName [ network . ID ] = network
2017-01-19 11:04:26 +00:00
}
2018-05-23 14:03:02 +00:00
if strings . HasPrefix ( network . ID , term ) {
2017-01-19 11:04:26 +00:00
// No need to check the ID collision here as we are still in
// local scope and the network ID is unique in this scope.
2018-05-23 14:03:02 +00:00
listByPartialID [ network . ID ] = network
2017-01-19 11:04:26 +00:00
}
}
2017-07-28 17:32:10 +00:00
nwk , err := n . cluster . GetNetwork ( term )
if err == nil {
// If the get network is passed with a specific network ID / partial network ID
// or if the get network was passed with a network name and scope as swarm
// return the network. Skipped using isMatchingScope because it is true if the scope
// is not set which would be case if the client API v1.30
if strings . HasPrefix ( nwk . ID , term ) || ( netconst . SwarmScope == scope ) {
2018-01-12 03:05:18 +00:00
// If we have a previous match "backend", return it, we need verbose when enabled
// ex: overlay/partial_ID or name/swarm_scope
if nwv , ok := listByPartialID [ nwk . ID ] ; ok {
nwk = nwv
} else if nwv , ok := listByFullName [ nwk . ID ] ; ok {
nwk = nwv
}
2017-07-28 17:32:10 +00:00
return httputils . WriteJSON ( w , http . StatusOK , nwk )
}
}
2018-05-23 14:03:02 +00:00
nr , _ := n . cluster . GetNetworks ( filter )
2017-01-19 11:04:26 +00:00
for _ , network := range nr {
2018-05-23 14:03:02 +00:00
if network . ID == term {
2017-01-19 11:04:26 +00:00
return httputils . WriteJSON ( w , http . StatusOK , network )
}
2018-05-23 14:03:02 +00:00
if network . Name == term {
2017-01-19 11:04:26 +00:00
// Check the ID collision as we are in swarm scope here, and
// the map (of the listByFullName) may have already had a
// network with the same ID (from local scope previously)
if _ , ok := listByFullName [ network . ID ] ; ! ok {
listByFullName [ network . ID ] = network
}
}
2018-05-23 14:03:02 +00:00
if strings . HasPrefix ( network . ID , term ) {
2017-01-19 11:04:26 +00:00
// Check the ID collision as we are in swarm scope here, and
// the map (of the listByPartialID) may have already had a
// network with the same ID (from local scope previously)
if _ , ok := listByPartialID [ network . ID ] ; ! ok {
listByPartialID [ network . ID ] = network
}
}
}
// Find based on full name, returns true only if no duplicates
if len ( listByFullName ) == 1 {
for _ , v := range listByFullName {
return httputils . WriteJSON ( w , http . StatusOK , v )
2016-06-14 02:52:49 +00:00
}
2015-09-25 10:19:17 +00:00
}
2017-01-19 11:04:26 +00:00
if len ( listByFullName ) > 1 {
2017-07-19 14:20:13 +00:00
return errors . Wrapf ( ambigousResultsError ( term ) , "%d matches found based on name" , len ( listByFullName ) )
2017-01-19 11:04:26 +00:00
}
// Find based on partial ID, returns true only if no duplicates
if len ( listByPartialID ) == 1 {
for _ , v := range listByPartialID {
return httputils . WriteJSON ( w , http . StatusOK , v )
}
}
if len ( listByPartialID ) > 1 {
2017-07-19 14:20:13 +00:00
return errors . Wrapf ( ambigousResultsError ( term ) , "%d matches found based on ID prefix" , len ( listByPartialID ) )
2017-01-19 11:04:26 +00:00
}
return libnetwork . ErrNoSuchNetwork ( term )
2015-09-25 10:19:17 +00:00
}
func ( n * networkRouter ) postNetworkCreate ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2022-04-05 09:43:06 +00:00
var create types . NetworkCreateRequest
if err := httputils . ReadJSON ( r , & create ) ; err != nil {
2015-09-25 10:19:17 +00:00
return err
}
2016-12-26 12:53:13 +00:00
if nws , err := n . cluster . GetNetworksByName ( create . Name ) ; err == nil && len ( nws ) > 0 {
2017-09-29 02:13:44 +00:00
return nameConflict ( create . Name )
2016-06-30 01:08:55 +00:00
}
2016-03-28 17:41:06 +00:00
nw , err := n . backend . CreateNetwork ( create )
2015-09-25 10:19:17 +00:00
if err != nil {
2017-01-19 06:49:10 +00:00
var warning string
if _ , ok := err . ( libnetwork . NetworkNameError ) ; ok {
// check if user defined CheckDuplicate, if set true, return err
// otherwise prepare a warning message
if create . CheckDuplicate {
2017-09-29 02:13:44 +00:00
return nameConflict ( create . Name )
2017-01-19 06:49:10 +00:00
}
warning = libnetwork . NetworkNameError ( create . Name ) . Error ( )
}
2016-06-14 02:52:49 +00:00
if _ , ok := err . ( libnetwork . ManagerRedirectError ) ; ! ok {
return err
}
2016-12-26 12:53:13 +00:00
id , err := n . cluster . CreateNetwork ( create )
2016-06-14 02:52:49 +00:00
if err != nil {
return err
}
2017-01-19 06:49:10 +00:00
nw = & types . NetworkCreateResponse {
ID : id ,
Warning : warning ,
}
2015-09-25 10:19:17 +00:00
}
2016-03-28 17:41:06 +00:00
return httputils . WriteJSON ( w , http . StatusCreated , nw )
2015-09-25 10:19:17 +00:00
}
func ( n * networkRouter ) postNetworkConnect ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2022-04-05 09:43:06 +00:00
var connect types . NetworkConnect
if err := httputils . ReadJSON ( r , & connect ) ; err != nil {
2015-09-25 10:19:17 +00:00
return err
}
2018-01-09 06:41:48 +00:00
// Unlike other operations, we does not check ambiguity of the name/ID here.
// The reason is that, In case of attachable network in swarm scope, the actual local network
// may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork`
// does the ambiguity check anyway. Therefore, passing the name to daemon would be enough.
return n . backend . ConnectContainerToNetwork ( connect . Container , vars [ "id" ] , connect . EndpointConfig )
2015-09-25 10:19:17 +00:00
}
func ( n * networkRouter ) postNetworkDisconnect ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2022-04-05 09:43:06 +00:00
var disconnect types . NetworkDisconnect
if err := httputils . ReadJSON ( r , & disconnect ) ; err != nil {
2015-09-25 10:19:17 +00:00
return err
}
2016-08-26 20:08:28 +00:00
return n . backend . DisconnectContainerFromNetwork ( disconnect . Container , vars [ "id" ] , disconnect . Force )
2015-09-25 10:19:17 +00:00
}
func ( n * networkRouter ) deleteNetwork ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2016-03-05 16:41:51 +00:00
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2017-10-31 19:46:53 +00:00
nw , err := n . findUniqueNetwork ( vars [ "id" ] )
if err != nil {
return err
}
if nw . Scope == "swarm" {
if err = n . cluster . RemoveNetwork ( nw . ID ) ; err != nil {
return err
}
} else {
if err := n . backend . DeleteNetwork ( nw . ID ) ; err != nil {
2016-09-27 18:04:36 +00:00
return err
}
2016-03-05 16:41:51 +00:00
}
w . WriteHeader ( http . StatusNoContent )
return nil
2015-09-25 10:19:17 +00:00
}
2016-10-18 04:36:52 +00:00
func ( n * networkRouter ) postNetworksPrune ( ctx context . Context , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := httputils . ParseForm ( r ) ; err != nil {
return err
}
2017-09-26 11:59:45 +00:00
pruneFilters , err := filters . FromJSON ( r . Form . Get ( "filters" ) )
2016-12-07 22:02:13 +00:00
if err != nil {
return err
}
2017-04-11 19:52:33 +00:00
pruneReport , err := n . backend . NetworksPrune ( ctx , pruneFilters )
2016-10-18 04:36:52 +00:00
if err != nil {
return err
}
return httputils . WriteJSON ( w , http . StatusOK , pruneReport )
}
2017-10-31 19:46:53 +00:00
// findUniqueNetwork will search network across different scopes (both local and swarm).
2018-01-15 17:26:43 +00:00
// NOTE: This findUniqueNetwork is different from FindNetwork in the daemon.
2017-10-31 19:46:53 +00:00
// In case multiple networks have duplicate names, return error.
// First find based on full ID, return immediately once one is found.
// If a network appears both in swarm and local, assume it is in local first
// For full name and partial ID, save the result first, and process later
// in case multiple records was found based on the same term
// TODO (yongtang): should we wrap with version here for backward compatibility?
func ( n * networkRouter ) findUniqueNetwork ( term string ) ( types . NetworkResource , error ) {
listByFullName := map [ string ] types . NetworkResource { }
listByPartialID := map [ string ] types . NetworkResource { }
2018-05-23 14:03:02 +00:00
filter := filters . NewArgs ( filters . Arg ( "idOrName" , term ) )
nw , _ := n . backend . GetNetworks ( filter , types . NetworkListConfig { Detailed : true } )
2017-10-31 19:46:53 +00:00
for _ , network := range nw {
2018-05-23 14:03:02 +00:00
if network . ID == term {
return network , nil
2017-10-31 19:46:53 +00:00
}
2018-05-23 14:03:02 +00:00
if network . Name == term && ! network . Ingress {
2017-10-31 19:46:53 +00:00
// No need to check the ID collision here as we are still in
// local scope and the network ID is unique in this scope.
2018-05-23 14:03:02 +00:00
listByFullName [ network . ID ] = network
2017-10-31 19:46:53 +00:00
}
2018-05-23 14:03:02 +00:00
if strings . HasPrefix ( network . ID , term ) {
2017-10-31 19:46:53 +00:00
// No need to check the ID collision here as we are still in
// local scope and the network ID is unique in this scope.
2018-05-23 14:03:02 +00:00
listByPartialID [ network . ID ] = network
2017-10-31 19:46:53 +00:00
}
}
2018-05-23 14:03:02 +00:00
nr , _ := n . cluster . GetNetworks ( filter )
2017-10-31 19:46:53 +00:00
for _ , network := range nr {
if network . ID == term {
return network , nil
}
if network . Name == term {
// Check the ID collision as we are in swarm scope here, and
// the map (of the listByFullName) may have already had a
// network with the same ID (from local scope previously)
if _ , ok := listByFullName [ network . ID ] ; ! ok {
listByFullName [ network . ID ] = network
}
}
if strings . HasPrefix ( network . ID , term ) {
// Check the ID collision as we are in swarm scope here, and
// the map (of the listByPartialID) may have already had a
// network with the same ID (from local scope previously)
if _ , ok := listByPartialID [ network . ID ] ; ! ok {
listByPartialID [ network . ID ] = network
}
}
}
// Find based on full name, returns true only if no duplicates
if len ( listByFullName ) == 1 {
for _ , v := range listByFullName {
return v , nil
}
}
if len ( listByFullName ) > 1 {
2017-11-29 04:09:37 +00:00
return types . NetworkResource { } , errdefs . InvalidParameter ( errors . Errorf ( "network %s is ambiguous (%d matches found based on name)" , term , len ( listByFullName ) ) )
2017-10-31 19:46:53 +00:00
}
// Find based on partial ID, returns true only if no duplicates
if len ( listByPartialID ) == 1 {
for _ , v := range listByPartialID {
return v , nil
}
}
if len ( listByPartialID ) > 1 {
2017-11-29 04:09:37 +00:00
return types . NetworkResource { } , errdefs . InvalidParameter ( errors . Errorf ( "network %s is ambiguous (%d matches found based on ID prefix)" , term , len ( listByPartialID ) ) )
2017-10-31 19:46:53 +00:00
}
2017-11-29 04:09:37 +00:00
return types . NetworkResource { } , errdefs . NotFound ( libnetwork . ErrNoSuchNetwork ( term ) )
2017-10-31 19:46:53 +00:00
}