2018-02-05 21:05:59 +00:00
package daemon // import "github.com/docker/docker/daemon"
2015-09-25 10:19:17 +00:00
import (
2018-04-19 22:30:59 +00:00
"context"
2023-07-28 08:14:41 +00:00
"errors"
2015-10-09 18:21:48 +00:00
"fmt"
"net"
2016-07-23 17:57:53 +00:00
"sort"
2018-05-10 20:44:09 +00:00
"strconv"
2015-09-25 10:19:17 +00:00
"strings"
2017-03-09 19:52:25 +00:00
"sync"
2015-09-25 10:19:17 +00:00
2023-09-13 15:41:45 +00:00
"github.com/containerd/log"
2016-09-06 18:18:12 +00:00
"github.com/docker/docker/api/types"
2023-12-06 01:21:21 +00:00
"github.com/docker/docker/api/types/backend"
2018-05-10 20:44:09 +00:00
containertypes "github.com/docker/docker/api/types/container"
2023-08-26 13:24:46 +00:00
"github.com/docker/docker/api/types/events"
2018-05-23 14:03:02 +00:00
"github.com/docker/docker/api/types/filters"
2016-09-06 18:18:12 +00:00
"github.com/docker/docker/api/types/network"
2018-05-10 20:44:09 +00:00
"github.com/docker/docker/container"
2016-06-14 02:52:49 +00:00
clustertypes "github.com/docker/docker/daemon/cluster/provider"
2022-08-17 21:13:49 +00:00
"github.com/docker/docker/daemon/config"
2018-05-10 20:44:09 +00:00
internalnetwork "github.com/docker/docker/daemon/network"
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"
lncluster "github.com/docker/docker/libnetwork/cluster"
"github.com/docker/docker/libnetwork/driverapi"
"github.com/docker/docker/libnetwork/ipamapi"
"github.com/docker/docker/libnetwork/netlabel"
"github.com/docker/docker/libnetwork/networkdb"
"github.com/docker/docker/libnetwork/options"
networktypes "github.com/docker/docker/libnetwork/types"
2021-05-28 00:15:56 +00:00
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/plugingetter"
"github.com/docker/docker/runconfig"
"github.com/docker/go-connections/nat"
2015-09-25 10:19:17 +00:00
)
2018-02-15 04:23:35 +00:00
// PredefinedNetworkError is returned when user tries to create predefined network that already exists.
type PredefinedNetworkError string
func ( pnr PredefinedNetworkError ) Error ( ) string {
return fmt . Sprintf ( "operation is not permitted on predefined %s network " , string ( pnr ) )
}
// Forbidden denotes the type of this error
func ( pnr PredefinedNetworkError ) Forbidden ( ) { }
2015-10-16 22:45:54 +00:00
// NetworkControllerEnabled checks if the networking stack is enabled.
2015-12-13 16:00:39 +00:00
// This feature depends on OS primitives and it's disabled in systems like Windows.
2015-10-16 22:45:54 +00:00
func ( daemon * Daemon ) NetworkControllerEnabled ( ) bool {
return daemon . netController != nil
}
2018-08-02 21:24:34 +00:00
// NetworkController returns the network controller created by the daemon.
2023-01-11 22:43:32 +00:00
func ( daemon * Daemon ) NetworkController ( ) * libnetwork . Controller {
2018-08-02 21:24:34 +00:00
return daemon . netController
}
2018-01-15 17:26:43 +00:00
// FindNetwork returns a network based on:
2017-10-31 19:46:53 +00:00
// 1. Full ID
// 2. Full Name
// 3. Partial ID
// as long as there is no ambiguity
2023-07-21 22:38:57 +00:00
func ( daemon * Daemon ) FindNetwork ( term string ) ( * libnetwork . Network , error ) {
2023-07-23 12:52:42 +00:00
var listByFullName , listByPartialID [ ] * libnetwork . Network
2018-05-10 20:44:09 +00:00
for _ , nw := range daemon . getAllNetworks ( ) {
2023-07-23 12:52:42 +00:00
nwID := nw . ID ( )
if nwID == term {
2017-10-31 19:46:53 +00:00
return nw , nil
}
if strings . HasPrefix ( nw . ID ( ) , term ) {
listByPartialID = append ( listByPartialID , nw )
}
2023-07-23 12:52:42 +00:00
if nw . Name ( ) == term {
listByFullName = append ( listByFullName , nw )
}
2017-08-14 20:23:57 +00:00
}
2017-10-31 19:46:53 +00:00
switch {
case len ( listByFullName ) == 1 :
return listByFullName [ 0 ] , nil
case len ( listByFullName ) > 1 :
2023-07-28 08:14:41 +00:00
return nil , errdefs . InvalidParameter ( fmt . Errorf ( "network %s is ambiguous (%d matches found on name)" , term , len ( listByFullName ) ) )
2017-10-31 19:46:53 +00:00
case len ( listByPartialID ) == 1 :
return listByPartialID [ 0 ] , nil
case len ( listByPartialID ) > 1 :
2023-07-28 08:14:41 +00:00
return nil , errdefs . InvalidParameter ( fmt . 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
// Be very careful to change the error type here, the
// libnetwork.ErrNoSuchNetwork error is used by the controller
// to retry the creation of the network as managed through the swarm manager
return nil , errdefs . NotFound ( libnetwork . ErrNoSuchNetwork ( term ) )
2015-09-25 10:19:17 +00:00
}
2017-08-14 20:23:57 +00:00
// GetNetworkByID function returns a network whose ID matches the given ID.
// It fails with an error if no matching network is found.
2023-07-21 22:38:57 +00:00
func ( daemon * Daemon ) GetNetworkByID ( id string ) ( * libnetwork . Network , error ) {
2017-08-14 20:23:57 +00:00
c := daemon . netController
if c == nil {
2023-07-28 08:14:41 +00:00
return nil , fmt . Errorf ( "netcontroller is nil: %w" , libnetwork . ErrNoSuchNetwork ( id ) )
2015-12-30 17:20:41 +00:00
}
2017-08-14 20:23:57 +00:00
return c . NetworkByID ( id )
2015-12-30 17:20:41 +00:00
}
2015-09-25 10:19:17 +00:00
2015-12-30 17:20:41 +00:00
// GetNetworkByName function returns a network for a given network name.
2016-07-14 13:07:00 +00:00
// If no network name is given, the default network is returned.
2023-07-21 22:38:57 +00:00
func ( daemon * Daemon ) GetNetworkByName ( name string ) ( * libnetwork . Network , error ) {
2015-12-30 17:20:41 +00:00
c := daemon . netController
2016-06-14 16:13:53 +00:00
if c == nil {
return nil , libnetwork . ErrNoSuchNetwork ( name )
}
2015-12-30 17:20:41 +00:00
if name == "" {
2022-09-23 17:40:11 +00:00
name = c . Config ( ) . DefaultNetwork
2015-09-25 10:19:17 +00:00
}
2015-12-30 17:20:41 +00:00
return c . NetworkByName ( name )
2015-09-25 10:19:17 +00:00
}
2017-08-14 20:23:57 +00:00
// GetNetworksByIDPrefix returns a list of networks whose ID partially matches zero or more networks
2023-07-21 22:38:57 +00:00
func ( daemon * Daemon ) GetNetworksByIDPrefix ( partialID string ) [ ] * libnetwork . Network {
2015-09-25 10:19:17 +00:00
c := daemon . netController
2016-06-14 16:13:53 +00:00
if c == nil {
return nil
}
2023-07-21 22:38:57 +00:00
list := [ ] * libnetwork . Network { }
l := func ( nw * libnetwork . Network ) bool {
2015-09-25 10:19:17 +00:00
if strings . HasPrefix ( nw . ID ( ) , partialID ) {
list = append ( list , nw )
}
return false
}
c . WalkNetworks ( l )
return list
}
2016-03-28 17:41:06 +00:00
// getAllNetworks returns a list containing all networks
2023-07-21 22:38:57 +00:00
func ( daemon * Daemon ) getAllNetworks ( ) [ ] * libnetwork . Network {
2017-10-31 20:05:24 +00:00
c := daemon . netController
if c == nil {
return nil
}
2023-09-10 00:05:05 +00:00
ctx := context . TODO ( )
return c . Networks ( ctx )
2015-11-10 08:57:06 +00:00
}
2017-03-09 19:52:25 +00:00
type ingressJob struct {
2017-03-31 21:07:55 +00:00
create * clustertypes . NetworkCreateRequest
ip net . IP
jobDone chan struct { }
2016-06-14 02:52:49 +00:00
}
2017-03-09 19:52:25 +00:00
var (
ingressWorkerOnce sync . Once
ingressJobsChannel chan * ingressJob
ingressID string
)
func ( daemon * Daemon ) startIngressWorker ( ) {
ingressJobsChannel = make ( chan * ingressJob , 100 )
go func ( ) {
2021-05-31 09:39:04 +00:00
//nolint: gosimple
2017-03-09 19:52:25 +00:00
for {
select {
case r := <- ingressJobsChannel :
if r . create != nil {
2022-08-31 20:12:30 +00:00
daemon . setupIngress ( & daemon . config ( ) . Config , r . create , r . ip , ingressID )
2017-03-09 19:52:25 +00:00
ingressID = r . create . ID
} else {
daemon . releaseIngress ( ingressID )
ingressID = ""
}
2017-03-31 21:07:55 +00:00
close ( r . jobDone )
2017-03-09 19:52:25 +00:00
}
}
} ( )
}
2016-06-14 02:52:49 +00:00
2017-03-09 19:52:25 +00:00
// enqueueIngressJob adds a ingress add/rm request to the worker queue.
// It guarantees the worker is started.
func ( daemon * Daemon ) enqueueIngressJob ( job * ingressJob ) {
ingressWorkerOnce . Do ( daemon . startIngressWorker )
ingressJobsChannel <- job
2016-06-14 02:52:49 +00:00
}
// SetupIngress setups ingress networking.
2017-03-31 21:07:55 +00:00
// The function returns a channel which will signal the caller when the programming is completed.
func ( daemon * Daemon ) SetupIngress ( create clustertypes . NetworkCreateRequest , nodeIP string ) ( <- chan struct { } , error ) {
2016-06-14 02:52:49 +00:00
ip , _ , err := net . ParseCIDR ( nodeIP )
if err != nil {
2017-03-31 21:07:55 +00:00
return nil , err
2016-06-14 02:52:49 +00:00
}
2017-03-31 21:07:55 +00:00
done := make ( chan struct { } )
daemon . enqueueIngressJob ( & ingressJob { & create , ip , done } )
return done , nil
2017-03-09 19:52:25 +00:00
}
2016-06-14 02:52:49 +00:00
2017-03-09 19:52:25 +00:00
// ReleaseIngress releases the ingress networking.
2017-03-31 21:07:55 +00:00
// The function returns a channel which will signal the caller when the programming is completed.
func ( daemon * Daemon ) ReleaseIngress ( ) ( <- chan struct { } , error ) {
done := make ( chan struct { } )
daemon . enqueueIngressJob ( & ingressJob { nil , nil , done } )
return done , nil
2017-03-09 19:52:25 +00:00
}
2016-06-14 02:52:49 +00:00
2022-08-17 21:13:49 +00:00
func ( daemon * Daemon ) setupIngress ( cfg * config . Config , create * clustertypes . NetworkCreateRequest , ip net . IP , staleID string ) {
2017-03-09 19:52:25 +00:00
controller := daemon . netController
controller . AgentInitWait ( )
2016-06-14 02:52:49 +00:00
2017-03-09 19:52:25 +00:00
if staleID != "" && staleID != create . ID {
daemon . releaseIngress ( staleID )
}
2016-07-12 18:27:58 +00:00
2022-08-17 21:13:49 +00:00
if _ , err := daemon . createNetwork ( cfg , create . NetworkCreateRequest , create . ID , true ) ; err != nil {
2017-03-09 19:52:25 +00:00
// If it is any other error other than already
// exists error log error and return.
if _ , ok := err . ( libnetwork . NetworkNameError ) ; ! ok {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Errorf ( "Failed creating ingress network: %v" , err )
2017-03-09 19:52:25 +00:00
return
2016-06-14 02:52:49 +00:00
}
2017-03-09 19:52:25 +00:00
// Otherwise continue down the call to create or recreate sandbox.
}
2016-06-14 02:52:49 +00:00
2017-11-06 06:02:07 +00:00
_ , err := daemon . GetNetworkByID ( create . ID )
2017-03-09 19:52:25 +00:00
if err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Errorf ( "Failed getting ingress network by id after creating: %v" , err )
2017-03-09 19:52:25 +00:00
}
}
2016-06-14 02:52:49 +00:00
2017-03-09 19:52:25 +00:00
func ( daemon * Daemon ) releaseIngress ( id string ) {
controller := daemon . netController
if id == "" {
return
}
n , err := controller . NetworkByID ( id )
if err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Errorf ( "failed to retrieve ingress network %s: %v" , id , err )
2017-03-09 19:52:25 +00:00
return
}
2018-04-19 15:39:51 +00:00
if err := n . Delete ( libnetwork . NetworkDeleteOptionRemoveLB ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Errorf ( "Failed to delete ingress network %s: %v" , n . ID ( ) , err )
2017-03-09 19:52:25 +00:00
return
}
2016-06-14 02:52:49 +00:00
}
// SetNetworkBootstrapKeys sets the bootstrap keys.
func ( daemon * Daemon ) SetNetworkBootstrapKeys ( keys [ ] * networktypes . EncryptionKey ) error {
2023-09-27 09:35:43 +00:00
if err := daemon . netController . SetKeys ( keys ) ; err != nil {
return err
2017-04-30 21:51:43 +00:00
}
2023-09-27 09:35:43 +00:00
// Upon successful key setting dispatch the keys available event
daemon . cluster . SendClusterEvent ( lncluster . EventNetworkKeysAvailable )
return nil
2016-06-14 02:52:49 +00:00
}
2016-08-23 23:50:15 +00:00
// UpdateAttachment notifies the attacher about the attachment config.
func ( daemon * Daemon ) UpdateAttachment ( networkName , networkID , containerID string , config * network . NetworkingConfig ) error {
if daemon . clusterProvider == nil {
return fmt . Errorf ( "cluster provider is not initialized" )
}
if err := daemon . clusterProvider . UpdateAttachment ( networkName , containerID , config ) ; err != nil {
return daemon . clusterProvider . UpdateAttachment ( networkID , containerID , config )
}
return nil
}
// WaitForDetachment makes the cluster manager wait for detachment of
// the container from the network.
func ( daemon * Daemon ) WaitForDetachment ( ctx context . Context , networkName , networkID , taskID , containerID string ) error {
if daemon . clusterProvider == nil {
return fmt . Errorf ( "cluster provider is not initialized" )
}
return daemon . clusterProvider . WaitForDetachment ( ctx , networkName , networkID , taskID , containerID )
}
2016-06-14 02:52:49 +00:00
// CreateManagedNetwork creates an agent network.
func ( daemon * Daemon ) CreateManagedNetwork ( create clustertypes . NetworkCreateRequest ) error {
2022-08-31 20:12:30 +00:00
_ , err := daemon . createNetwork ( & daemon . config ( ) . Config , create . NetworkCreateRequest , create . ID , true )
2016-06-14 02:52:49 +00:00
return err
}
2015-09-25 10:19:17 +00:00
// CreateNetwork creates a network with the given name, driver and other optional parameters
2016-04-13 08:33:46 +00:00
func ( daemon * Daemon ) CreateNetwork ( create types . NetworkCreateRequest ) ( * types . NetworkCreateResponse , error ) {
2022-08-31 20:12:30 +00:00
return daemon . createNetwork ( & daemon . config ( ) . Config , create , "" , false )
2016-06-14 02:52:49 +00:00
}
2022-08-17 21:13:49 +00:00
func ( daemon * Daemon ) createNetwork ( cfg * config . Config , create types . NetworkCreateRequest , id string , agent bool ) ( * types . NetworkCreateResponse , error ) {
2018-02-15 04:23:35 +00:00
if runconfig . IsPreDefinedNetwork ( create . Name ) {
return nil , PredefinedNetworkError ( create . Name )
2016-03-28 17:41:06 +00:00
}
2023-07-08 13:19:04 +00:00
c := daemon . netController
driver := create . Driver
if driver == "" {
driver = c . Config ( ) . DefaultDriver
}
if driver == "overlay" && ! daemon . cluster . IsManager ( ) && ! agent {
2023-07-08 13:31:27 +00:00
return nil , errdefs . Forbidden ( errors . New ( ` This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again. ` ) )
2023-07-08 13:19:04 +00:00
}
2022-01-27 20:13:45 +00:00
networkOptions := make ( map [ string ] string )
for k , v := range create . Options {
networkOptions [ k ] = v
}
2022-08-17 21:13:49 +00:00
if defaultOpts , ok := cfg . DefaultNetworkOpts [ driver ] ; create . ConfigFrom == nil && ok {
2022-01-27 20:13:45 +00:00
for k , v := range defaultOpts {
if _ , ok := networkOptions [ k ] ; ! ok {
2023-07-30 15:18:56 +00:00
log . G ( context . TODO ( ) ) . WithFields ( log . Fields { "driver" : driver , "network" : id , k : v } ) . Debug ( "Applying network default option" )
2022-01-27 20:13:45 +00:00
networkOptions [ k ] = v
}
}
}
2016-03-16 21:52:34 +00:00
nwOptions := [ ] libnetwork . NetworkOption {
2016-03-28 17:41:06 +00:00
libnetwork . NetworkOptionEnableIPv6 ( create . EnableIPv6 ) ,
2022-01-27 20:13:45 +00:00
libnetwork . NetworkOptionDriverOpts ( networkOptions ) ,
2016-03-28 17:41:06 +00:00
libnetwork . NetworkOptionLabels ( create . Labels ) ,
2016-11-22 19:33:00 +00:00
libnetwork . NetworkOptionAttachable ( create . Attachable ) ,
2017-03-09 19:52:25 +00:00
libnetwork . NetworkOptionIngress ( create . Ingress ) ,
2017-05-01 21:44:05 +00:00
libnetwork . NetworkOptionScope ( create . Scope ) ,
2016-03-16 21:52:34 +00:00
}
2016-08-31 15:25:14 +00:00
2017-04-07 18:18:51 +00:00
if create . ConfigOnly {
nwOptions = append ( nwOptions , libnetwork . NetworkOptionConfigOnly ( ) )
}
2024-02-02 09:01:17 +00:00
if err := network . ValidateIPAM ( create . IPAM , create . EnableIPv6 ) ; err != nil {
2024-02-08 17:40:54 +00:00
if agent {
// This function is called with agent=false for all networks. For swarm-scoped
// networks, the configuration is validated but ManagerRedirectError is returned
// and the network is not created. Then, each time a swarm-scoped network is
// needed, this function is called again with agent=true.
//
// Non-swarm networks created before ValidateIPAM was introduced continue to work
// as they did before-upgrade, even if they would fail the new checks on creation
// (for example, by having host-bits set in their subnet). Those networks are not
// seen again here.
//
// By dropping errors for agent networks, existing swarm-scoped networks also
// continue to behave as they did before upgrade - but new networks are still
// validated.
log . G ( context . TODO ( ) ) . WithFields ( log . Fields {
"error" : err ,
"network" : create . Name ,
} ) . Warn ( "Continuing with validation errors in agent IPAM" )
} else {
return nil , errdefs . InvalidParameter ( err )
}
2023-06-13 08:52:59 +00:00
}
2023-09-11 14:20:44 +00:00
2016-08-31 15:25:14 +00:00
if create . IPAM != nil {
ipam := create . IPAM
v4Conf , v6Conf , err := getIpamConfig ( ipam . Config )
if err != nil {
return nil , err
}
nwOptions = append ( nwOptions , libnetwork . NetworkOptionIpam ( ipam . Driver , "" , v4Conf , v6Conf , ipam . Options ) )
}
2016-03-28 17:41:06 +00:00
if create . Internal {
2015-12-28 02:15:50 +00:00
nwOptions = append ( nwOptions , libnetwork . NetworkOptionInternalNetwork ( ) )
}
2016-06-14 02:52:49 +00:00
if agent {
nwOptions = append ( nwOptions , libnetwork . NetworkOptionDynamic ( ) )
nwOptions = append ( nwOptions , libnetwork . NetworkOptionPersist ( false ) )
}
2017-04-07 18:18:51 +00:00
if create . ConfigFrom != nil {
nwOptions = append ( nwOptions , libnetwork . NetworkOptionConfigFrom ( create . ConfigFrom . Network ) )
}
2018-04-19 15:39:51 +00:00
if agent && driver == "overlay" {
2017-11-06 06:02:07 +00:00
nodeIP , exists := daemon . GetAttachmentStore ( ) . GetIPForNetwork ( id )
if ! exists {
2022-07-04 08:07:58 +00:00
return nil , fmt . Errorf ( "failed to find a load balancer IP to use for network: %v" , id )
2017-11-06 06:02:07 +00:00
}
nwOptions = append ( nwOptions , libnetwork . NetworkOptionLBEndpoint ( nodeIP ) )
}
2016-06-14 02:52:49 +00:00
n , err := c . NewNetwork ( driver , create . Name , id , nwOptions ... )
2015-12-22 04:35:30 +00:00
if err != nil {
return nil , err
}
2016-12-20 02:18:43 +00:00
daemon . pluginRefCount ( driver , driverapi . NetworkPluginEndpointType , plugingetter . Acquire )
2016-12-16 20:53:22 +00:00
if create . IPAM != nil {
2016-12-20 02:18:43 +00:00
daemon . pluginRefCount ( create . IPAM . Driver , ipamapi . PluginEndpointType , plugingetter . Acquire )
2016-12-16 20:53:22 +00:00
}
2023-08-26 13:24:46 +00:00
daemon . LogNetworkEvent ( n , events . ActionCreate )
2016-07-20 23:11:28 +00:00
2016-03-28 17:41:06 +00:00
return & types . NetworkCreateResponse {
libnet: Make sure network names are unique
Fixes #18864, #20648, #33561, #40901.
[This GH comment][1] makes clear network name uniqueness has never been
enforced due to the eventually consistent nature of Classic Swarm
datastores:
> there is no guaranteed way to check for duplicates across a cluster of
> docker hosts.
And this is further confirmed by other comments made by @mrjana in that
same issue, eg. [this one][2]:
> we want to adopt a schema which can pave the way in the future for a
> completely decentralized cluster of docker hosts (if scalability is
> needed).
This decentralized model is what Classic Swarm was trying to be. It's
been superseded since then by Docker Swarm, which has a centralized
control plane.
To circumvent this drawback, the `NetworkCreate` endpoint accepts a
`CheckDuplicate` flag. However it's not perfectly reliable as it won't
catch concurrent requests.
Due to this design decision, API clients like Compose have to implement
workarounds to make sure names are really unique (eg.
docker/compose#9585). And the daemon itself has seen a string of issues
due to that decision, including some that aren't fixed to this day (for
instance moby/moby#40901):
> The problem is, that if you specify a network for a container using
> the ID, it will add that network to the container but it will then
> change it to reference the network by using the name.
To summarize, this "feature" is broken, has no practical use and is a
source of pain for Docker users and API consumers. So let's just remove
it for _all_ API versions.
[1]: https://github.com/moby/moby/issues/18864#issuecomment-167201414
[2]: https://github.com/moby/moby/issues/18864#issuecomment-167202589
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2023-08-16 18:11:10 +00:00
ID : n . ID ( ) ,
2016-03-28 17:41:06 +00:00
} , nil
2015-10-09 18:21:48 +00:00
}
2016-12-16 20:53:22 +00:00
func ( daemon * Daemon ) pluginRefCount ( driver , capability string , mode int ) {
var builtinDrivers [ ] string
if capability == driverapi . NetworkPluginEndpointType {
builtinDrivers = daemon . netController . BuiltinDrivers ( )
} else if capability == ipamapi . PluginEndpointType {
builtinDrivers = daemon . netController . BuiltinIPAMDrivers ( )
}
for _ , d := range builtinDrivers {
if d == driver {
return
}
}
if daemon . PluginStore != nil {
_ , err := daemon . PluginStore . Get ( driver , capability , mode )
if err != nil {
2023-07-30 15:18:56 +00:00
log . G ( context . TODO ( ) ) . WithError ( err ) . WithFields ( log . Fields { "mode" : mode , "driver" : driver } ) . Error ( "Error handling plugin refcount operation" )
2016-12-16 20:53:22 +00:00
}
}
}
2015-10-09 18:21:48 +00:00
func getIpamConfig ( data [ ] network . IPAMConfig ) ( [ ] * libnetwork . IpamConf , [ ] * libnetwork . IpamConf , error ) {
ipamV4Cfg := [ ] * libnetwork . IpamConf { }
ipamV6Cfg := [ ] * libnetwork . IpamConf { }
for _ , d := range data {
iCfg := libnetwork . IpamConf { }
iCfg . PreferredPool = d . Subnet
iCfg . SubPool = d . IPRange
iCfg . Gateway = d . Gateway
iCfg . AuxAddresses = d . AuxAddress
ip , _ , err := net . ParseCIDR ( d . Subnet )
if err != nil {
return nil , nil , fmt . Errorf ( "Invalid subnet %s : %v" , d . Subnet , err )
}
if ip . To4 ( ) != nil {
ipamV4Cfg = append ( ipamV4Cfg , & iCfg )
} else {
ipamV6Cfg = append ( ipamV6Cfg , & iCfg )
}
}
return ipamV4Cfg , ipamV6Cfg , nil
2015-09-25 10:19:17 +00:00
}
2015-10-13 22:44:23 +00:00
2016-06-14 02:52:49 +00:00
// UpdateContainerServiceConfig updates a service configuration.
func ( daemon * Daemon ) UpdateContainerServiceConfig ( containerName string , serviceConfig * clustertypes . ServiceConfig ) error {
2019-08-09 12:10:07 +00:00
ctr , err := daemon . GetContainer ( containerName )
2016-06-14 02:52:49 +00:00
if err != nil {
return err
}
2019-08-09 12:10:07 +00:00
ctr . NetworkSettings . Service = serviceConfig
2016-06-14 02:52:49 +00:00
return nil
}
2015-10-13 22:44:23 +00:00
// ConnectContainerToNetwork connects the given container to the given
// network. If either cannot be found, an err is returned. If the
// network cannot be set up, an err is returned.
2016-01-08 00:18:34 +00:00
func ( daemon * Daemon ) ConnectContainerToNetwork ( containerName , networkName string , endpointConfig * network . EndpointSettings ) error {
2019-08-09 12:10:07 +00:00
ctr , err := daemon . GetContainer ( containerName )
2015-10-13 22:44:23 +00:00
if err != nil {
return err
}
2019-08-09 12:10:07 +00:00
return daemon . ConnectToNetwork ( ctr , networkName , endpointConfig )
2015-10-13 22:44:23 +00:00
}
// DisconnectContainerFromNetwork disconnects the given container from
// the given network. If either cannot be found, an err is returned.
2016-08-26 20:08:28 +00:00
func ( daemon * Daemon ) DisconnectContainerFromNetwork ( containerName string , networkName string , force bool ) error {
2019-08-09 12:10:07 +00:00
ctr , err := daemon . GetContainer ( containerName )
2015-10-13 22:44:23 +00:00
if err != nil {
2016-01-13 04:56:36 +00:00
if force {
2016-08-26 20:08:28 +00:00
return daemon . ForceEndpointDelete ( containerName , networkName )
2016-01-13 04:56:36 +00:00
}
2015-10-13 22:44:23 +00:00
return err
}
2019-08-09 12:10:07 +00:00
return daemon . DisconnectFromNetwork ( ctr , networkName , force )
2015-10-13 22:44:23 +00:00
}
2015-10-23 06:08:26 +00:00
// GetNetworkDriverList returns the list of plugins drivers
// registered for network.
2023-09-10 00:05:05 +00:00
func ( daemon * Daemon ) GetNetworkDriverList ( ctx context . Context ) [ ] string {
2015-11-19 19:02:25 +00:00
if ! daemon . NetworkControllerEnabled ( ) {
return nil
}
2016-09-25 11:30:52 +00:00
2016-10-15 05:40:28 +00:00
pluginList := daemon . netController . BuiltinDrivers ( )
2017-01-04 20:38:14 +00:00
managedPlugins := daemon . PluginStore . GetAllManagedPluginsByCap ( driverapi . NetworkPluginEndpointType )
for _ , plugin := range managedPlugins {
pluginList = append ( pluginList , plugin . Name ( ) )
}
2016-10-15 05:40:28 +00:00
pluginMap := make ( map [ string ] bool )
2016-10-24 22:21:14 +00:00
for _ , plugin := range pluginList {
pluginMap [ plugin ] = true
}
2016-09-25 11:30:52 +00:00
2023-09-10 00:05:05 +00:00
networks := daemon . netController . Networks ( ctx )
2015-10-23 06:08:26 +00:00
2019-08-09 12:10:07 +00:00
for _ , nw := range networks {
if ! pluginMap [ nw . Type ( ) ] {
pluginList = append ( pluginList , nw . Type ( ) )
pluginMap [ nw . Type ( ) ] = true
2016-07-23 17:57:53 +00:00
}
2015-10-23 06:08:26 +00:00
}
2016-07-23 17:57:53 +00:00
sort . Strings ( pluginList )
2015-10-23 06:08:26 +00:00
return pluginList
}
2015-12-22 04:35:30 +00:00
2016-06-14 02:52:49 +00:00
// DeleteManagedNetwork deletes an agent network.
2017-10-31 19:46:53 +00:00
// The requirement of networkID is enforced.
2016-06-14 02:52:49 +00:00
func ( daemon * Daemon ) DeleteManagedNetwork ( networkID string ) error {
2017-10-31 19:46:53 +00:00
n , err := daemon . GetNetworkByID ( networkID )
if err != nil {
return err
}
return daemon . deleteNetwork ( n , true )
2016-06-14 02:52:49 +00:00
}
2015-12-22 04:35:30 +00:00
// DeleteNetwork destroys a network unless it's one of docker's predefined networks.
func ( daemon * Daemon ) DeleteNetwork ( networkID string ) error {
2017-10-31 19:46:53 +00:00
n , err := daemon . GetNetworkByID ( networkID )
2015-12-22 04:35:30 +00:00
if err != nil {
2023-07-28 08:14:41 +00:00
return fmt . Errorf ( "could not find network by ID: %w" , err )
2015-12-22 04:35:30 +00:00
}
2017-10-31 19:46:53 +00:00
return daemon . deleteNetwork ( n , false )
}
2015-12-22 04:35:30 +00:00
2023-07-21 22:38:57 +00:00
func ( daemon * Daemon ) deleteNetwork ( nw * libnetwork . Network , dynamic bool ) error {
2016-06-14 02:52:49 +00:00
if runconfig . IsPreDefinedNetwork ( nw . Name ( ) ) && ! dynamic {
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 15:53:35 +00:00
err := fmt . Errorf ( "%s is a pre-defined network and cannot be removed" , nw . Name ( ) )
2017-11-29 04:09:37 +00:00
return errdefs . Forbidden ( err )
2015-12-22 04:35:30 +00:00
}
2023-07-25 15:37:19 +00:00
if dynamic && ! nw . Dynamic ( ) {
2017-05-01 23:44:04 +00:00
if runconfig . IsPreDefinedNetwork ( nw . Name ( ) ) {
// Predefined networks now support swarm services. Make this
// a no-op when cluster requests to remove the predefined network.
return nil
}
err := fmt . Errorf ( "%s is not a dynamic network" , nw . Name ( ) )
2017-11-29 04:09:37 +00:00
return errdefs . Forbidden ( err )
2017-05-01 23:44:04 +00:00
}
2015-12-22 04:35:30 +00:00
if err := nw . Delete ( ) ; err != nil {
2023-07-28 08:14:41 +00:00
return fmt . Errorf ( "error while removing network: %w" , err )
2015-12-22 04:35:30 +00:00
}
2017-04-07 18:18:51 +00:00
// If this is not a configuration only network, we need to
// update the corresponding remote drivers' reference counts
2023-07-25 15:37:19 +00:00
if ! nw . ConfigOnly ( ) {
2017-04-07 18:18:51 +00:00
daemon . pluginRefCount ( nw . Type ( ) , driverapi . NetworkPluginEndpointType , plugingetter . Release )
2023-07-25 15:37:19 +00:00
ipamType , _ , _ , _ := nw . IpamConfig ( )
2017-04-07 18:18:51 +00:00
daemon . pluginRefCount ( ipamType , ipamapi . PluginEndpointType , plugingetter . Release )
2023-08-26 13:24:46 +00:00
daemon . LogNetworkEvent ( nw , events . ActionDestroy )
2017-04-07 18:18:51 +00:00
}
2015-12-22 04:35:30 +00:00
return nil
}
2016-03-28 17:41:06 +00:00
2016-06-14 02:52:49 +00:00
// GetNetworks returns a list of all networks
2023-12-06 01:21:21 +00:00
func ( daemon * Daemon ) GetNetworks ( filter filters . Args , config backend . NetworkListConfig ) ( networks [ ] types . NetworkResource , err error ) {
2023-07-21 22:38:57 +00:00
var idx map [ string ] * libnetwork . Network
2018-05-23 14:03:02 +00:00
if config . Detailed {
2023-07-21 22:38:57 +00:00
idx = make ( map [ string ] * libnetwork . Network )
2018-05-23 14:03:02 +00:00
}
2023-07-23 10:20:46 +00:00
allNetworks := daemon . getAllNetworks ( )
networks = make ( [ ] types . NetworkResource , 0 , len ( allNetworks ) )
for _ , n := range allNetworks {
2018-05-23 14:03:02 +00:00
nr := buildNetworkResource ( n )
2023-07-23 10:20:46 +00:00
networks = append ( networks , nr )
2018-05-23 14:03:02 +00:00
if config . Detailed {
idx [ nr . ID ] = n
}
}
2023-07-23 10:20:46 +00:00
networks , err = internalnetwork . FilterNetworks ( networks , filter )
2018-05-23 14:03:02 +00:00
if err != nil {
return nil , err
}
if config . Detailed {
2023-07-23 10:20:46 +00:00
for i , nw := range networks {
networks [ i ] . Containers = buildContainerAttachments ( idx [ nw . ID ] )
if config . Verbose {
networks [ i ] . Services = buildServiceAttachments ( idx [ nw . ID ] )
}
2018-05-23 14:03:02 +00:00
}
}
2023-07-23 10:20:46 +00:00
return networks , nil
2018-05-23 14:03:02 +00:00
}
2023-07-23 10:49:36 +00:00
// buildNetworkResource builds a [types.NetworkResource] from the given
// [libnetwork.Network], to be returned by the API.
2023-07-21 22:38:57 +00:00
func buildNetworkResource ( nw * libnetwork . Network ) types . NetworkResource {
2018-05-23 14:03:02 +00:00
if nw == nil {
2023-07-23 10:49:36 +00:00
return types . NetworkResource { }
2018-05-23 14:03:02 +00:00
}
2023-07-23 10:49:36 +00:00
return types . NetworkResource {
Name : nw . Name ( ) ,
ID : nw . ID ( ) ,
2023-07-25 15:37:19 +00:00
Created : nw . Created ( ) ,
Scope : nw . Scope ( ) ,
2023-07-23 10:49:36 +00:00
Driver : nw . Type ( ) ,
2023-07-25 15:37:19 +00:00
EnableIPv6 : nw . IPv6Enabled ( ) ,
IPAM : buildIPAMResources ( nw ) ,
Internal : nw . Internal ( ) ,
Attachable : nw . Attachable ( ) ,
Ingress : nw . Ingress ( ) ,
ConfigFrom : network . ConfigReference { Network : nw . ConfigFrom ( ) } ,
ConfigOnly : nw . ConfigOnly ( ) ,
2023-07-23 10:49:36 +00:00
Containers : map [ string ] types . EndpointResource { } ,
2023-07-25 15:37:19 +00:00
Options : nw . DriverOptions ( ) ,
Labels : nw . Labels ( ) ,
Peers : buildPeerInfoResources ( nw . Peers ( ) ) ,
2023-07-23 10:49:36 +00:00
}
2018-05-23 14:03:02 +00:00
}
2023-07-23 10:20:46 +00:00
// buildContainerAttachments creates a [types.EndpointResource] map of all
// containers attached to the network. It is used when listing networks in
// detailed mode.
func buildContainerAttachments ( nw * libnetwork . Network ) map [ string ] types . EndpointResource {
containers := make ( map [ string ] types . EndpointResource )
2023-07-23 09:40:32 +00:00
for _ , e := range nw . Endpoints ( ) {
2018-05-23 14:03:02 +00:00
ei := e . Info ( )
if ei == nil {
continue
}
2023-07-23 09:40:32 +00:00
if sb := ei . Sandbox ( ) ; sb != nil {
2023-07-23 10:20:46 +00:00
containers [ sb . ContainerID ( ) ] = buildEndpointResource ( e , ei )
2023-07-23 09:40:32 +00:00
} else {
2023-07-23 10:20:46 +00:00
containers [ "ep-" + e . ID ( ) ] = buildEndpointResource ( e , ei )
2018-05-23 14:03:02 +00:00
}
}
2023-07-23 10:20:46 +00:00
return containers
}
2023-07-23 09:40:32 +00:00
2023-07-23 10:20:46 +00:00
// buildServiceAttachments creates a [network.ServiceInfo] map of all services
// attached to the network. It is used when listing networks in "verbose" mode.
func buildServiceAttachments ( nw * libnetwork . Network ) map [ string ] network . ServiceInfo {
services := make ( map [ string ] network . ServiceInfo )
2023-07-25 15:37:19 +00:00
for name , service := range nw . Services ( ) {
2023-07-23 10:20:46 +00:00
tasks := make ( [ ] network . Task , 0 , len ( service . Tasks ) )
2018-05-23 14:03:02 +00:00
for _ , t := range service . Tasks {
tasks = append ( tasks , network . Task {
Name : t . Name ,
EndpointID : t . EndpointID ,
EndpointIP : t . EndpointIP ,
Info : t . Info ,
} )
}
2023-07-23 10:20:46 +00:00
services [ name ] = network . ServiceInfo {
2018-05-23 14:03:02 +00:00
VIP : service . VIP ,
Ports : service . Ports ,
Tasks : tasks ,
LocalLBIndex : service . LocalLBIndex ,
}
}
2023-07-23 10:20:46 +00:00
return services
2018-05-23 14:03:02 +00:00
}
2023-07-23 08:52:13 +00:00
// buildPeerInfoResources converts a list of [networkdb.PeerInfo] to a
// [network.PeerInfo] for inclusion in API responses. It returns nil if
// the list of peers is empty.
2018-05-23 14:03:02 +00:00
func buildPeerInfoResources ( peers [ ] networkdb . PeerInfo ) [ ] network . PeerInfo {
2023-07-23 08:52:13 +00:00
if len ( peers ) == 0 {
return nil
}
2018-05-23 14:03:02 +00:00
peerInfo := make ( [ ] network . PeerInfo , 0 , len ( peers ) )
for _ , peer := range peers {
2023-07-23 08:52:13 +00:00
peerInfo = append ( peerInfo , network . PeerInfo ( peer ) )
2018-05-23 14:03:02 +00:00
}
return peerInfo
}
2023-07-23 09:23:36 +00:00
// buildIPAMResources constructs a [network.IPAM] from the network's
// IPAM information for inclusion in API responses.
2023-07-25 15:37:19 +00:00
func buildIPAMResources ( nw * libnetwork . Network ) network . IPAM {
2023-07-23 09:23:36 +00:00
var ipamConfig [ ] network . IPAMConfig
2018-05-23 14:03:02 +00:00
2023-07-23 09:23:36 +00:00
ipamDriver , ipamOptions , ipv4Conf , ipv6Conf := nw . IpamConfig ( )
2018-05-23 14:03:02 +00:00
2023-07-23 09:23:36 +00:00
hasIPv4Config := false
for _ , cfg := range ipv4Conf {
if cfg . PreferredPool == "" {
2018-05-23 14:03:02 +00:00
continue
}
2023-07-23 09:23:36 +00:00
hasIPv4Config = true
ipamConfig = append ( ipamConfig , network . IPAMConfig {
Subnet : cfg . PreferredPool ,
IPRange : cfg . SubPool ,
Gateway : cfg . Gateway ,
AuxAddress : cfg . AuxAddresses ,
} )
2018-05-23 14:03:02 +00:00
}
2023-07-23 09:23:36 +00:00
hasIPv6Config := false
for _ , cfg := range ipv6Conf {
if cfg . PreferredPool == "" {
2018-05-23 14:03:02 +00:00
continue
}
2023-07-23 09:23:36 +00:00
hasIPv6Config = true
ipamConfig = append ( ipamConfig , network . IPAMConfig {
Subnet : cfg . PreferredPool ,
IPRange : cfg . SubPool ,
Gateway : cfg . Gateway ,
AuxAddress : cfg . AuxAddresses ,
} )
2018-05-23 14:03:02 +00:00
}
2023-07-23 09:23:36 +00:00
if ! hasIPv4Config || ! hasIPv6Config {
ipv4Info , ipv6Info := nw . IpamInfo ( )
if ! hasIPv4Config {
for _ , info := range ipv4Info {
var gw string
if info . IPAMData . Gateway != nil {
gw = info . IPAMData . Gateway . IP . String ( )
}
ipamConfig = append ( ipamConfig , network . IPAMConfig {
Subnet : info . IPAMData . Pool . String ( ) ,
Gateway : gw ,
} )
2018-05-23 14:03:02 +00:00
}
}
2023-07-23 09:23:36 +00:00
if ! hasIPv6Config {
for _ , info := range ipv6Info {
if info . IPAMData . Pool == nil {
continue
}
ipamConfig = append ( ipamConfig , network . IPAMConfig {
Subnet : info . IPAMData . Pool . String ( ) ,
Gateway : info . IPAMData . Gateway . String ( ) ,
} )
2018-05-23 14:03:02 +00:00
}
}
}
2023-07-23 09:23:36 +00:00
return network . IPAM {
Driver : ipamDriver ,
Options : ipamOptions ,
Config : ipamConfig ,
2018-05-23 14:03:02 +00:00
}
}
2023-07-23 09:40:32 +00:00
// buildEndpointResource combines information from the endpoint and additional
// endpoint-info into a [types.EndpointResource].
func buildEndpointResource ( ep * libnetwork . Endpoint , info libnetwork . EndpointInfo ) types . EndpointResource {
er := types . EndpointResource {
EndpointID : ep . ID ( ) ,
Name : ep . Name ( ) ,
2018-05-23 14:03:02 +00:00
}
2023-07-23 09:40:32 +00:00
if iface := info . Iface ( ) ; iface != nil {
2018-05-23 14:03:02 +00:00
if mac := iface . MacAddress ( ) ; mac != nil {
er . MacAddress = mac . String ( )
}
if ip := iface . Address ( ) ; ip != nil && len ( ip . IP ) > 0 {
er . IPv4Address = ip . String ( )
}
2023-07-23 09:40:32 +00:00
if ip := iface . AddressIPv6 ( ) ; ip != nil && len ( ip . IP ) > 0 {
er . IPv6Address = ip . String ( )
2018-05-23 14:03:02 +00:00
}
}
return er
2016-03-28 17:41:06 +00:00
}
2017-01-14 04:14:03 +00:00
// clearAttachableNetworks removes the attachable networks
// after disconnecting any connected container
func ( daemon * Daemon ) clearAttachableNetworks ( ) {
2018-05-10 20:44:09 +00:00
for _ , n := range daemon . getAllNetworks ( ) {
2023-07-25 15:37:19 +00:00
if ! n . Attachable ( ) {
2017-01-14 04:14:03 +00:00
continue
}
for _ , ep := range n . Endpoints ( ) {
epInfo := ep . Info ( )
if epInfo == nil {
continue
}
sb := epInfo . Sandbox ( )
if sb == nil {
continue
}
containerID := sb . ContainerID ( )
if err := daemon . DisconnectContainerFromNetwork ( containerID , n . ID ( ) , true ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Warnf ( "Failed to disconnect container %s from swarm network %s on cluster leave: %v" ,
2017-01-14 04:14:03 +00:00
containerID , n . Name ( ) , err )
}
}
if err := daemon . DeleteManagedNetwork ( n . ID ( ) ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Warnf ( "Failed to remove swarm network %s on cluster leave: %v" , n . Name ( ) , err )
2017-01-14 04:14:03 +00:00
}
}
}
2018-05-10 20:44:09 +00:00
// buildCreateEndpointOptions builds endpoint options from a given network.
Only restore a configured MAC addr on restart.
The API's EndpointConfig struct has a MacAddress field that's used for
both the configured address, and the current address (which may be generated).
A configured address must be restored when a container is restarted, but a
generated address must not.
The previous attempt to differentiate between the two, without adding a field
to the API's EndpointConfig that would show up in 'inspect' output, was a
field in the daemon's version of EndpointSettings, MACOperational. It did
not work, MACOperational was set to true when a configured address was
used. So, while it ensured addresses were regenerated, it failed to preserve
a configured address.
So, this change removes that code, and adds DesiredMacAddress to the wrapped
version of EndpointSettings, where it is persisted but does not appear in
'inspect' results. Its value is copied from MacAddress (the API field) when
a container is created.
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-01-26 18:00:32 +00:00
func buildCreateEndpointOptions ( c * container . Container , n * libnetwork . Network , epConfig * internalnetwork . EndpointSettings , sb * libnetwork . Sandbox , daemonDNS [ ] string ) ( [ ] libnetwork . EndpointOption , error ) {
2023-07-23 13:10:29 +00:00
var createOptions [ ] libnetwork . EndpointOption
2023-07-04 21:48:58 +00:00
var genericOptions = make ( options . Generic )
2018-05-10 20:44:09 +00:00
2023-07-23 15:02:34 +00:00
nwName := n . Name ( )
2018-05-10 20:44:09 +00:00
if epConfig != nil {
2023-07-23 13:10:29 +00:00
if ipam := epConfig . IPAMConfig ; ipam != nil {
var ipList [ ] net . IP
2018-05-10 20:44:09 +00:00
for _ , ips := range ipam . LinkLocalIPs {
2023-07-23 13:10:29 +00:00
linkIP := net . ParseIP ( ips )
if linkIP == nil && ips != "" {
2023-08-08 15:40:19 +00:00
return nil , fmt . Errorf ( "invalid link-local IP address: %s" , ipam . LinkLocalIPs )
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:10:29 +00:00
ipList = append ( ipList , linkIP )
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:10:29 +00:00
ip := net . ParseIP ( ipam . IPv4Address )
if ip == nil && ipam . IPv4Address != "" {
2023-08-08 15:40:19 +00:00
return nil , fmt . Errorf ( "invalid IPv4 address: %s" , ipam . IPv4Address )
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:10:29 +00:00
ip6 := net . ParseIP ( ipam . IPv6Address )
if ip6 == nil && ipam . IPv6Address != "" {
2023-08-08 15:40:19 +00:00
return nil , fmt . Errorf ( "invalid IPv6 address: %s" , ipam . IPv6Address )
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:10:29 +00:00
createOptions = append ( createOptions , libnetwork . CreateOptionIpam ( ip , ip6 , ipList , nil ) )
2018-05-10 20:44:09 +00:00
}
2023-11-04 13:12:20 +00:00
createOptions = append ( createOptions , libnetwork . CreateOptionDNSNames ( epConfig . DNSNames ) )
2018-05-10 20:44:09 +00:00
for k , v := range epConfig . DriverOpts {
createOptions = append ( createOptions , libnetwork . EndpointOptionGeneric ( options . Generic { k : v } ) )
}
2023-07-04 21:48:58 +00:00
Only restore a configured MAC addr on restart.
The API's EndpointConfig struct has a MacAddress field that's used for
both the configured address, and the current address (which may be generated).
A configured address must be restored when a container is restarted, but a
generated address must not.
The previous attempt to differentiate between the two, without adding a field
to the API's EndpointConfig that would show up in 'inspect' output, was a
field in the daemon's version of EndpointSettings, MACOperational. It did
not work, MACOperational was set to true when a configured address was
used. So, while it ensured addresses were regenerated, it failed to preserve
a configured address.
So, this change removes that code, and adds DesiredMacAddress to the wrapped
version of EndpointSettings, where it is persisted but does not appear in
'inspect' results. Its value is copied from MacAddress (the API field) when
a container is created.
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-01-26 18:00:32 +00:00
if epConfig . DesiredMacAddress != "" {
mac , err := net . ParseMAC ( epConfig . DesiredMacAddress )
2023-07-04 21:48:58 +00:00
if err != nil {
return nil , err
}
genericOptions [ netlabel . MacAddress ] = mac
}
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:10:29 +00:00
if svcCfg := c . NetworkSettings . Service ; svcCfg != nil {
2023-07-23 13:25:00 +00:00
nwID := n . ID ( )
var vip net . IP
if virtualAddress := svcCfg . VirtualAddresses [ nwID ] ; virtualAddress != nil {
vip = net . ParseIP ( virtualAddress . IPv4 )
2018-05-10 20:44:09 +00:00
}
var portConfigs [ ] * libnetwork . PortConfig
for _ , portConfig := range svcCfg . ExposedPorts {
portConfigs = append ( portConfigs , & libnetwork . PortConfig {
Name : portConfig . Name ,
Protocol : libnetwork . PortConfig_Protocol ( portConfig . Protocol ) ,
TargetPort : portConfig . TargetPort ,
PublishedPort : portConfig . PublishedPort ,
} )
}
2023-07-23 13:25:00 +00:00
createOptions = append ( createOptions , libnetwork . CreateOptionService ( svcCfg . Name , svcCfg . ID , vip , portConfigs , svcCfg . Aliases [ nwID ] ) )
2018-05-10 20:44:09 +00:00
}
2023-07-23 15:02:34 +00:00
if ! containertypes . NetworkMode ( nwName ) . IsUserDefined ( ) {
2018-05-10 20:44:09 +00:00
createOptions = append ( createOptions , libnetwork . CreateOptionDisableResolution ( ) )
}
2023-11-21 15:51:51 +00:00
opts , err := buildPortsRelatedCreateEndpointOptions ( c , n , sb )
if err != nil {
return nil , err
}
createOptions = append ( createOptions , opts ... )
// On Windows, DNS config is a per-adapter config option whereas on Linux, it's a sandbox-wide parameter; hence why
// we're dealing with DNS config both here and in buildSandboxOptions. Following DNS options are only honored by
// Windows netdrivers, whereas DNS options in buildSandboxOptions are only honored by Linux netdrivers.
2023-11-23 17:09:35 +00:00
if ! n . Internal ( ) {
2023-11-21 15:51:51 +00:00
if len ( c . HostConfig . DNS ) > 0 {
createOptions = append ( createOptions , libnetwork . CreateOptionDNS ( c . HostConfig . DNS ) )
} else if len ( daemonDNS ) > 0 {
createOptions = append ( createOptions , libnetwork . CreateOptionDNS ( daemonDNS ) )
}
}
createOptions = append ( createOptions , libnetwork . EndpointOptionGeneric ( genericOptions ) )
return createOptions , nil
}
// buildPortsRelatedCreateEndpointOptions returns the appropriate endpoint options to apply config related to port
// mapping and exposed ports.
func buildPortsRelatedCreateEndpointOptions ( c * container . Container , n * libnetwork . Network , sb * libnetwork . Sandbox ) ( [ ] libnetwork . EndpointOption , error ) {
2023-07-23 13:04:17 +00:00
// Port-mapping rules belong to the container & applicable only to non-internal networks.
//
// TODO(thaJeztah): Look if we can provide a more minimal function for getPortMapInfo, as it does a lot, and we only need the "length".
2023-07-25 15:37:19 +00:00
if n . Internal ( ) || len ( getPortMapInfo ( sb ) ) > 0 {
2023-11-21 15:51:51 +00:00
return nil , nil
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:10:29 +00:00
bindings := make ( nat . PortMap )
2018-05-10 20:44:09 +00:00
if c . HostConfig . PortBindings != nil {
for p , b := range c . HostConfig . PortBindings {
bindings [ p ] = [ ] nat . PortBinding { }
for _ , bb := range b {
bindings [ p ] = append ( bindings [ p ] , nat . PortBinding {
HostIP : bb . HostIP ,
HostPort : bb . HostPort ,
} )
}
}
}
2023-07-23 13:34:34 +00:00
// TODO(thaJeztah): Move this code to a method on nat.PortSet.
ports := make ( [ ] nat . Port , 0 , len ( c . Config . ExposedPorts ) )
for p := range c . Config . ExposedPorts {
ports = append ( ports , p )
2018-05-10 20:44:09 +00:00
}
nat . SortPortMap ( ports , bindings )
2023-07-23 13:10:29 +00:00
var (
exposedPorts [ ] networktypes . TransportPort
publishedPorts [ ] networktypes . PortBinding
)
2018-05-10 20:44:09 +00:00
for _ , port := range ports {
2023-07-23 13:34:34 +00:00
portProto := networktypes . ParseProtocol ( port . Proto ( ) )
portNum := uint16 ( port . Int ( ) )
exposedPorts = append ( exposedPorts , networktypes . TransportPort {
Proto : portProto ,
Port : portNum ,
} )
2018-05-10 20:44:09 +00:00
2023-07-23 13:58:15 +00:00
for _ , binding := range bindings [ port ] {
newP , err := nat . NewPort ( nat . SplitProtoPort ( binding . HostPort ) )
2018-05-10 20:44:09 +00:00
var portStart , portEnd int
if err == nil {
portStart , portEnd , err = newP . Range ( )
}
if err != nil {
2023-08-08 15:40:19 +00:00
return nil , fmt . Errorf ( "error parsing HostPort value (%s): %w" , binding . HostPort , err )
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:52:56 +00:00
publishedPorts = append ( publishedPorts , networktypes . PortBinding {
Proto : portProto ,
Port : portNum ,
2023-07-23 13:58:15 +00:00
HostIP : net . ParseIP ( binding . HostIP ) ,
2023-07-23 13:52:56 +00:00
HostPort : uint16 ( portStart ) ,
HostPortEnd : uint16 ( portEnd ) ,
} )
2018-05-10 20:44:09 +00:00
}
2023-07-23 13:58:15 +00:00
if c . HostConfig . PublishAllPorts && len ( bindings [ port ] ) == 0 {
2023-07-23 13:52:56 +00:00
publishedPorts = append ( publishedPorts , networktypes . PortBinding {
Proto : portProto ,
Port : portNum ,
} )
2018-05-10 20:44:09 +00:00
}
}
2023-11-21 15:51:51 +00:00
return [ ] libnetwork . EndpointOption {
2023-07-04 21:48:58 +00:00
libnetwork . CreateOptionPortMapping ( publishedPorts ) ,
libnetwork . CreateOptionExposedPorts ( exposedPorts ) ,
2023-11-21 15:51:51 +00:00
} , nil
2018-05-10 20:44:09 +00:00
}
2022-09-26 18:41:46 +00:00
// getPortMapInfo retrieves the current port-mapping programmed for the given sandbox
2023-01-12 01:10:09 +00:00
func getPortMapInfo ( sb * libnetwork . Sandbox ) nat . PortMap {
2018-05-10 20:44:09 +00:00
pm := nat . PortMap { }
if sb == nil {
return pm
}
for _ , ep := range sb . Endpoints ( ) {
pm , _ = getEndpointPortMapInfo ( ep )
if len ( pm ) > 0 {
break
}
}
return pm
}
2023-01-12 01:42:24 +00:00
func getEndpointPortMapInfo ( ep * libnetwork . Endpoint ) ( nat . PortMap , error ) {
2018-05-10 20:44:09 +00:00
pm := nat . PortMap { }
driverInfo , err := ep . DriverInfo ( )
if err != nil {
return pm , err
}
if driverInfo == nil {
// It is not an error for epInfo to be nil
return pm , nil
}
if expData , ok := driverInfo [ netlabel . ExposedPorts ] ; ok {
if exposedPorts , ok := expData . ( [ ] networktypes . TransportPort ) ; ok {
for _ , tp := range exposedPorts {
natPort , err := nat . NewPort ( tp . Proto . String ( ) , strconv . Itoa ( int ( tp . Port ) ) )
if err != nil {
return pm , fmt . Errorf ( "Error parsing Port value(%v):%v" , tp . Port , err )
}
pm [ natPort ] = nil
}
}
}
mapData , ok := driverInfo [ netlabel . PortMap ]
if ! ok {
return pm , nil
}
if portMapping , ok := mapData . ( [ ] networktypes . PortBinding ) ; ok {
for _ , pp := range portMapping {
natPort , err := nat . NewPort ( pp . Proto . String ( ) , strconv . Itoa ( int ( pp . Port ) ) )
if err != nil {
return pm , err
}
natBndg := nat . PortBinding { HostIP : pp . HostIP . String ( ) , HostPort : strconv . Itoa ( int ( pp . HostPort ) ) }
pm [ natPort ] = append ( pm [ natPort ] , natBndg )
}
}
return pm , nil
}
// buildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint.
2023-07-21 22:38:57 +00:00
func buildEndpointInfo ( networkSettings * internalnetwork . Settings , n * libnetwork . Network , ep * libnetwork . Endpoint ) error {
2018-05-10 20:44:09 +00:00
if ep == nil {
return errors . New ( "endpoint cannot be nil" )
}
if networkSettings == nil {
return errors . New ( "network cannot be nil" )
}
epInfo := ep . Info ( )
if epInfo == nil {
// It is not an error to get an empty endpoint info
return nil
}
2023-07-23 21:00:22 +00:00
nwName := n . Name ( )
if _ , ok := networkSettings . Networks [ nwName ] ; ! ok {
networkSettings . Networks [ nwName ] = & internalnetwork . EndpointSettings {
2018-05-10 20:44:09 +00:00
EndpointSettings : & network . EndpointSettings { } ,
}
}
2023-07-23 21:00:22 +00:00
networkSettings . Networks [ nwName ] . NetworkID = n . ID ( )
networkSettings . Networks [ nwName ] . EndpointID = ep . ID ( )
2018-05-10 20:44:09 +00:00
iface := epInfo . Iface ( )
if iface == nil {
return nil
}
if iface . MacAddress ( ) != nil {
2023-07-23 21:00:22 +00:00
networkSettings . Networks [ nwName ] . MacAddress = iface . MacAddress ( ) . String ( )
2018-05-10 20:44:09 +00:00
}
if iface . Address ( ) != nil {
ones , _ := iface . Address ( ) . Mask . Size ( )
2023-07-23 21:00:22 +00:00
networkSettings . Networks [ nwName ] . IPAddress = iface . Address ( ) . IP . String ( )
networkSettings . Networks [ nwName ] . IPPrefixLen = ones
2018-05-10 20:44:09 +00:00
}
if iface . AddressIPv6 ( ) != nil && iface . AddressIPv6 ( ) . IP . To16 ( ) != nil {
onesv6 , _ := iface . AddressIPv6 ( ) . Mask . Size ( )
2023-07-23 21:00:22 +00:00
networkSettings . Networks [ nwName ] . GlobalIPv6Address = iface . AddressIPv6 ( ) . IP . String ( )
networkSettings . Networks [ nwName ] . GlobalIPv6PrefixLen = onesv6
2018-05-10 20:44:09 +00:00
}
return nil
}
// buildJoinOptions builds endpoint Join options from a given network.
2022-01-20 13:25:24 +00:00
func buildJoinOptions ( networkSettings * internalnetwork . Settings , n interface { Name ( ) string } ) ( [ ] libnetwork . EndpointOption , error ) {
2018-05-10 20:44:09 +00:00
var joinOptions [ ] libnetwork . EndpointOption
if epConfig , ok := networkSettings . Networks [ n . Name ( ) ] ; ok {
for _ , str := range epConfig . Links {
name , alias , err := opts . ParseLink ( str )
if err != nil {
return nil , err
}
joinOptions = append ( joinOptions , libnetwork . CreateOptionAlias ( name , alias ) )
}
for k , v := range epConfig . DriverOpts {
joinOptions = append ( joinOptions , libnetwork . EndpointOptionGeneric ( options . Generic { k : v } ) )
}
}
return joinOptions , nil
}