Signed-off-by: Albin Kerouanton <albinker@gmail.com>
| ... | ... |
@@ -86,6 +86,7 @@ type Controller struct {
|
| 86 | 86 |
id string |
| 87 | 87 |
drvRegistry drvregistry.Networks |
| 88 | 88 |
ipamRegistry drvregistry.IPAMs |
| 89 |
+ pmRegistry drvregistry.PortMappers |
|
| 89 | 90 |
sandboxes map[string]*Sandbox |
| 90 | 91 |
cfg *config.Config |
| 91 | 92 |
store *datastore.Store |
| ... | ... |
@@ -173,13 +174,20 @@ func New(ctx context.Context, cfgOptions ...config.Option) (_ *Controller, retEr |
| 173 | 173 |
} |
| 174 | 174 |
c.drvRegistry.Notify = c |
| 175 | 175 |
|
| 176 |
+ // Register portmappers before network drivers to make sure they can |
|
| 177 |
+ // restore existing sandboxes (with port mappings) during their |
|
| 178 |
+ // initialization, if the daemon is started in live restore mode. |
|
| 179 |
+ if err := registerPortMappers(ctx, &c.pmRegistry, c.cfg); err != nil {
|
|
| 180 |
+ return nil, err |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 176 | 183 |
// External plugins don't need config passed through daemon. They can |
| 177 | 184 |
// bootstrap themselves. |
| 178 | 185 |
if err := remotedriver.Register(&c.drvRegistry, c.cfg.PluginGetter); err != nil {
|
| 179 | 186 |
return nil, err |
| 180 | 187 |
} |
| 181 | 188 |
|
| 182 |
- if err := registerNetworkDrivers(&c.drvRegistry, c.store, c.makeDriverConfig); err != nil {
|
|
| 189 |
+ if err := registerNetworkDrivers(&c.drvRegistry, c.store, &c.pmRegistry, c.makeDriverConfig); err != nil {
|
|
| 183 | 190 |
return nil, err |
| 184 | 191 |
} |
| 185 | 192 |
|
| ... | ... |
@@ -22,6 +22,7 @@ import ( |
| 22 | 22 |
"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/iptabler" |
| 23 | 23 |
"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/nftabler" |
| 24 | 24 |
"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/rlkclient" |
| 25 |
+ "github.com/docker/docker/daemon/libnetwork/drvregistry" |
|
| 25 | 26 |
"github.com/docker/docker/daemon/libnetwork/internal/netiputil" |
| 26 | 27 |
"github.com/docker/docker/daemon/libnetwork/internal/nftables" |
| 27 | 28 |
"github.com/docker/docker/daemon/libnetwork/iptables" |
| ... | ... |
@@ -178,6 +179,7 @@ type driver struct {
|
| 178 | 178 |
portDriverClient portDriverClient |
| 179 | 179 |
configNetwork sync.Mutex |
| 180 | 180 |
firewaller firewaller.Firewaller |
| 181 |
+ portmappers *drvregistry.PortMappers |
|
| 181 | 182 |
sync.Mutex |
| 182 | 183 |
} |
| 183 | 184 |
|
| ... | ... |
@@ -192,17 +194,18 @@ const ( |
| 192 | 192 |
) |
| 193 | 193 |
|
| 194 | 194 |
// New constructs a new bridge driver |
| 195 |
-func newDriver(store *datastore.Store) *driver {
|
|
| 195 |
+func newDriver(store *datastore.Store, pms *drvregistry.PortMappers) *driver {
|
|
| 196 | 196 |
return &driver{
|
| 197 |
- store: store, |
|
| 198 |
- nlh: ns.NlHandle(), |
|
| 199 |
- networks: map[string]*bridgeNetwork{},
|
|
| 197 |
+ store: store, |
|
| 198 |
+ nlh: ns.NlHandle(), |
|
| 199 |
+ networks: map[string]*bridgeNetwork{},
|
|
| 200 |
+ portmappers: pms, |
|
| 200 | 201 |
} |
| 201 | 202 |
} |
| 202 | 203 |
|
| 203 | 204 |
// Register registers a new instance of bridge driver. |
| 204 |
-func Register(r driverapi.Registerer, store *datastore.Store, config map[string]interface{}) error {
|
|
| 205 |
- d := newDriver(store) |
|
| 205 |
+func Register(r driverapi.Registerer, store *datastore.Store, pms *drvregistry.PortMappers, config map[string]interface{}) error {
|
|
| 206 |
+ d := newDriver(store, pms) |
|
| 206 | 207 |
if err := d.configure(config); err != nil {
|
| 207 | 208 |
return err |
| 208 | 209 |
} |
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
|
| 16 | 16 |
"github.com/docker/docker/daemon/libnetwork/driverapi" |
| 17 | 17 |
"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/firewaller" |
| 18 |
+ "github.com/docker/docker/daemon/libnetwork/drvregistry" |
|
| 18 | 19 |
"github.com/docker/docker/daemon/libnetwork/internal/netiputil" |
| 19 | 20 |
"github.com/docker/docker/daemon/libnetwork/ipamapi" |
| 20 | 21 |
"github.com/docker/docker/daemon/libnetwork/ipams/defaultipam" |
| ... | ... |
@@ -280,7 +281,7 @@ func getIPv6Data(t *testing.T) []driverapi.IPAMData {
|
| 280 | 280 |
|
| 281 | 281 |
func TestCreateFullOptions(t *testing.T) {
|
| 282 | 282 |
defer netnsutils.SetupTestOSContext(t)() |
| 283 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 283 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 284 | 284 |
|
| 285 | 285 |
config := &configuration{
|
| 286 | 286 |
EnableIPForwarding: true, |
| ... | ... |
@@ -335,7 +336,7 @@ func TestCreateFullOptions(t *testing.T) {
|
| 335 | 335 |
|
| 336 | 336 |
func TestCreateNoConfig(t *testing.T) {
|
| 337 | 337 |
defer netnsutils.SetupTestOSContext(t)() |
| 338 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 338 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 339 | 339 |
err := d.configure(nil) |
| 340 | 340 |
assert.NilError(t, err) |
| 341 | 341 |
|
| ... | ... |
@@ -350,7 +351,7 @@ func TestCreateNoConfig(t *testing.T) {
|
| 350 | 350 |
|
| 351 | 351 |
func TestCreateFullOptionsLabels(t *testing.T) {
|
| 352 | 352 |
defer netnsutils.SetupTestOSContext(t)() |
| 353 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 353 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 354 | 354 |
|
| 355 | 355 |
config := &configuration{
|
| 356 | 356 |
EnableIPForwarding: true, |
| ... | ... |
@@ -537,7 +538,7 @@ func TestCreateVeth(t *testing.T) {
|
| 537 | 537 |
func TestCreate(t *testing.T) {
|
| 538 | 538 |
defer netnsutils.SetupTestOSContext(t)() |
| 539 | 539 |
|
| 540 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 540 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 541 | 541 |
|
| 542 | 542 |
if err := d.configure(nil); err != nil {
|
| 543 | 543 |
t.Fatalf("Failed to setup driver config: %v", err)
|
| ... | ... |
@@ -563,7 +564,7 @@ func TestCreate(t *testing.T) {
|
| 563 | 563 |
func TestCreateFail(t *testing.T) {
|
| 564 | 564 |
defer netnsutils.SetupTestOSContext(t)() |
| 565 | 565 |
|
| 566 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 566 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 567 | 567 |
|
| 568 | 568 |
if err := d.configure(nil); err != nil {
|
| 569 | 569 |
t.Fatalf("Failed to setup driver config: %v", err)
|
| ... | ... |
@@ -582,7 +583,7 @@ func TestCreateMultipleNetworks(t *testing.T) {
|
| 582 | 582 |
defer netnsutils.SetupTestOSContext(t)() |
| 583 | 583 |
useStubFirewaller(t) |
| 584 | 584 |
|
| 585 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 585 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 586 | 586 |
|
| 587 | 587 |
checkFirewallerNetworks := func() {
|
| 588 | 588 |
t.Helper() |
| ... | ... |
@@ -795,7 +796,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
|
| 795 | 795 |
defer netnsutils.SetupTestOSContext(t)() |
| 796 | 796 |
useStubFirewaller(t) |
| 797 | 797 |
|
| 798 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 798 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 799 | 799 |
portallocator.Get().ReleaseAll() |
| 800 | 800 |
|
| 801 | 801 |
var proxyBinary string |
| ... | ... |
@@ -909,7 +910,7 @@ func TestLinkContainers(t *testing.T) {
|
| 909 | 909 |
defer netnsutils.SetupTestOSContext(t)() |
| 910 | 910 |
useStubFirewaller(t) |
| 911 | 911 |
|
| 912 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 912 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 913 | 913 |
|
| 914 | 914 |
config := &configuration{
|
| 915 | 915 |
EnableIPTables: true, |
| ... | ... |
@@ -1178,7 +1179,7 @@ func TestValidateFixedCIDRV6(t *testing.T) {
|
| 1178 | 1178 |
func TestSetDefaultGw(t *testing.T) {
|
| 1179 | 1179 |
defer netnsutils.SetupTestOSContext(t)() |
| 1180 | 1180 |
|
| 1181 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 1181 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 1182 | 1182 |
|
| 1183 | 1183 |
if err := d.configure(nil); err != nil {
|
| 1184 | 1184 |
t.Fatalf("Failed to setup driver config: %v", err)
|
| ... | ... |
@@ -1228,7 +1229,7 @@ func TestSetDefaultGw(t *testing.T) {
|
| 1228 | 1228 |
|
| 1229 | 1229 |
func TestCreateWithExistingBridge(t *testing.T) {
|
| 1230 | 1230 |
defer netnsutils.SetupTestOSContext(t)() |
| 1231 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 1231 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 1232 | 1232 |
|
| 1233 | 1233 |
if err := d.configure(nil); err != nil {
|
| 1234 | 1234 |
t.Fatalf("Failed to setup driver config: %v", err)
|
| ... | ... |
@@ -1300,7 +1301,7 @@ func TestCreateParallel(t *testing.T) {
|
| 1300 | 1300 |
c := netnsutils.SetupTestOSContextEx(t) |
| 1301 | 1301 |
defer c.Cleanup(t) |
| 1302 | 1302 |
|
| 1303 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 1303 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 1304 | 1304 |
portallocator.Get().ReleaseAll() |
| 1305 | 1305 |
|
| 1306 | 1306 |
if err := d.configure(nil); err != nil {
|
| ... | ... |
@@ -1351,7 +1352,7 @@ func useStubFirewaller(t *testing.T) {
|
| 1351 | 1351 |
// Regression test for https://github.com/moby/moby/issues/46445 |
| 1352 | 1352 |
func TestSetupIP6TablesWithHostIPv4(t *testing.T) {
|
| 1353 | 1353 |
defer netnsutils.SetupTestOSContext(t)() |
| 1354 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 1354 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 1355 | 1355 |
dc := &configuration{
|
| 1356 | 1356 |
EnableIPTables: true, |
| 1357 | 1357 |
EnableIP6Tables: true, |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"testing" |
| 6 | 6 |
|
| 7 | 7 |
cerrdefs "github.com/containerd/errdefs" |
| 8 |
+ "github.com/docker/docker/daemon/libnetwork/drvregistry" |
|
| 8 | 9 |
"github.com/docker/docker/daemon/libnetwork/netlabel" |
| 9 | 10 |
"github.com/docker/docker/internal/nlwrap" |
| 10 | 11 |
"github.com/docker/docker/internal/testutils/netnsutils" |
| ... | ... |
@@ -15,7 +16,7 @@ import ( |
| 15 | 15 |
|
| 16 | 16 |
func TestLinkCreate(t *testing.T) {
|
| 17 | 17 |
defer netnsutils.SetupTestOSContext(t)() |
| 18 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 18 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 19 | 19 |
err := d.configure(nil) |
| 20 | 20 |
assert.NilError(t, err) |
| 21 | 21 |
|
| ... | ... |
@@ -78,7 +79,7 @@ func TestLinkCreate(t *testing.T) {
|
| 78 | 78 |
|
| 79 | 79 |
func TestLinkCreateTwo(t *testing.T) {
|
| 80 | 80 |
defer netnsutils.SetupTestOSContext(t)() |
| 81 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 81 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 82 | 82 |
err := d.configure(nil) |
| 83 | 83 |
assert.NilError(t, err) |
| 84 | 84 |
|
| ... | ... |
@@ -106,7 +107,7 @@ func TestLinkCreateTwo(t *testing.T) {
|
| 106 | 106 |
|
| 107 | 107 |
func TestLinkCreateNoEnableIPv6(t *testing.T) {
|
| 108 | 108 |
defer netnsutils.SetupTestOSContext(t)() |
| 109 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 109 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 110 | 110 |
err := d.configure(nil) |
| 111 | 111 |
assert.NilError(t, err) |
| 112 | 112 |
|
| ... | ... |
@@ -131,7 +132,7 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
|
| 131 | 131 |
|
| 132 | 132 |
func TestLinkDelete(t *testing.T) {
|
| 133 | 133 |
defer netnsutils.SetupTestOSContext(t)() |
| 134 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 134 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 135 | 135 |
err := d.configure(nil) |
| 136 | 136 |
assert.NilError(t, err) |
| 137 | 137 |
|
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
|
| 15 | 15 |
"github.com/containerd/log" |
| 16 | 16 |
"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/firewaller" |
| 17 |
+ "github.com/docker/docker/daemon/libnetwork/drvregistry" |
|
| 17 | 18 |
"github.com/docker/docker/daemon/libnetwork/netlabel" |
| 18 | 19 |
"github.com/docker/docker/daemon/libnetwork/ns" |
| 19 | 20 |
"github.com/docker/docker/daemon/libnetwork/portallocator" |
| ... | ... |
@@ -31,7 +32,7 @@ func TestPortMappingConfig(t *testing.T) {
|
| 31 | 31 |
defer netnsutils.SetupTestOSContext(t)() |
| 32 | 32 |
useStubFirewaller(t) |
| 33 | 33 |
|
| 34 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 34 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 35 | 35 |
|
| 36 | 36 |
config := &configuration{
|
| 37 | 37 |
EnableIPTables: true, |
| ... | ... |
@@ -116,7 +117,7 @@ func TestPortMappingV6Config(t *testing.T) {
|
| 116 | 116 |
t.Fatalf("Could not bring loopback iface up: %v", err)
|
| 117 | 117 |
} |
| 118 | 118 |
|
| 119 |
- d := newDriver(storeutils.NewTempStore(t)) |
|
| 119 |
+ d := newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{})
|
|
| 120 | 120 |
|
| 121 | 121 |
config := &configuration{
|
| 122 | 122 |
EnableIPTables: true, |
| ... | ... |
@@ -791,7 +792,7 @@ func TestAddPortMappings(t *testing.T) {
|
| 791 | 791 |
GwModeIPv6: tc.gwMode6, |
| 792 | 792 |
}, |
| 793 | 793 |
bridge: &bridgeInterface{},
|
| 794 |
- driver: newDriver(storeutils.NewTempStore(t)), |
|
| 794 |
+ driver: newDriver(storeutils.NewTempStore(t), &drvregistry.PortMappers{}),
|
|
| 795 | 795 |
} |
| 796 | 796 |
genericOption := map[string]interface{}{
|
| 797 | 797 |
netlabel.GenericData: &configuration{
|
| ... | ... |
@@ -1,8 +1,10 @@ |
| 1 | 1 |
package libnetwork |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
|
| 7 |
+ "github.com/docker/docker/daemon/libnetwork/config" |
|
| 6 | 8 |
"github.com/docker/docker/daemon/libnetwork/datastore" |
| 7 | 9 |
"github.com/docker/docker/daemon/libnetwork/driverapi" |
| 8 | 10 |
"github.com/docker/docker/daemon/libnetwork/drivers/bridge" |
| ... | ... |
@@ -11,14 +13,17 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/daemon/libnetwork/drivers/macvlan" |
| 12 | 12 |
"github.com/docker/docker/daemon/libnetwork/drivers/null" |
| 13 | 13 |
"github.com/docker/docker/daemon/libnetwork/drivers/overlay" |
| 14 |
+ "github.com/docker/docker/daemon/libnetwork/drvregistry" |
|
| 14 | 15 |
) |
| 15 | 16 |
|
| 16 |
-func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, driverConfig func(string) map[string]interface{}) error {
|
|
| 17 |
+func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, pms *drvregistry.PortMappers, driverConfig func(string) map[string]interface{}) error {
|
|
| 17 | 18 |
for _, nr := range []struct {
|
| 18 | 19 |
ntype string |
| 19 | 20 |
register func(driverapi.Registerer, *datastore.Store, map[string]interface{}) error
|
| 20 | 21 |
}{
|
| 21 |
- {ntype: bridge.NetworkType, register: bridge.Register},
|
|
| 22 |
+ {ntype: bridge.NetworkType, register: func(r driverapi.Registerer, store *datastore.Store, cfg map[string]interface{}) error {
|
|
| 23 |
+ return bridge.Register(r, store, pms, cfg) |
|
| 24 |
+ }}, |
|
| 22 | 25 |
{ntype: host.NetworkType, register: func(r driverapi.Registerer, _ *datastore.Store, _ map[string]interface{}) error {
|
| 23 | 26 |
return host.Register(r) |
| 24 | 27 |
}}, |
| ... | ... |
@@ -38,3 +43,7 @@ func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, driv |
| 38 | 38 |
|
| 39 | 39 |
return nil |
| 40 | 40 |
} |
| 41 |
+ |
|
| 42 |
+func registerPortMappers(ctx context.Context, r *drvregistry.PortMappers, cfg *config.Config) error {
|
|
| 43 |
+ return nil |
|
| 44 |
+} |
| ... | ... |
@@ -1,16 +1,19 @@ |
| 1 | 1 |
package libnetwork |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
|
| 7 |
+ "github.com/docker/docker/daemon/libnetwork/config" |
|
| 6 | 8 |
"github.com/docker/docker/daemon/libnetwork/datastore" |
| 7 | 9 |
"github.com/docker/docker/daemon/libnetwork/driverapi" |
| 8 | 10 |
"github.com/docker/docker/daemon/libnetwork/drivers/null" |
| 9 | 11 |
"github.com/docker/docker/daemon/libnetwork/drivers/windows" |
| 10 | 12 |
"github.com/docker/docker/daemon/libnetwork/drivers/windows/overlay" |
| 13 |
+ "github.com/docker/docker/daemon/libnetwork/drvregistry" |
|
| 11 | 14 |
) |
| 12 | 15 |
|
| 13 |
-func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, _ func(string) map[string]interface{}) error {
|
|
| 16 |
+func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, _ *drvregistry.PortMappers, _ func(string) map[string]interface{}) error {
|
|
| 14 | 17 |
for _, nr := range []struct {
|
| 15 | 18 |
ntype string |
| 16 | 19 |
register func(driverapi.Registerer) error |
| ... | ... |
@@ -25,3 +28,7 @@ func registerNetworkDrivers(r driverapi.Registerer, store *datastore.Store, _ fu |
| 25 | 25 |
|
| 26 | 26 |
return windows.RegisterBuiltinLocalDrivers(r, store) |
| 27 | 27 |
} |
| 28 |
+ |
|
| 29 |
+func registerPortMappers(ctx context.Context, r *drvregistry.PortMappers, cfg *config.Config) error {
|
|
| 30 |
+ return nil |
|
| 31 |
+} |
| 28 | 32 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,41 @@ |
| 0 |
+package drvregistry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/daemon/libnetwork/portmapperapi" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type PortMappers struct {
|
|
| 11 |
+ drivers map[string]portmapperapi.PortMapper |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+// Register a portmapper with the registry. |
|
| 15 |
+func (r *PortMappers) Register(name string, pm portmapperapi.PortMapper) error {
|
|
| 16 |
+ if strings.TrimSpace(name) == "" {
|
|
| 17 |
+ return errors.New("portmapper name cannot be empty")
|
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ if _, ok := r.drivers[name]; ok {
|
|
| 21 |
+ return errors.New("portmapper already registered")
|
|
| 22 |
+ } |
|
| 23 |
+ |
|
| 24 |
+ if r.drivers == nil {
|
|
| 25 |
+ r.drivers = make(map[string]portmapperapi.PortMapper) |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ r.drivers[name] = pm |
|
| 29 |
+ |
|
| 30 |
+ return nil |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+// Get retrieves a portmapper by name from the registry. |
|
| 34 |
+func (r *PortMappers) Get(name string) (portmapperapi.PortMapper, error) {
|
|
| 35 |
+ pm, ok := r.drivers[name] |
|
| 36 |
+ if !ok {
|
|
| 37 |
+ return nil, fmt.Errorf("portmapper %s not found", name)
|
|
| 38 |
+ } |
|
| 39 |
+ return pm, nil |
|
| 40 |
+} |
| 0 | 41 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,67 @@ |
| 0 |
+package drvregistry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "testing" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/daemon/libnetwork/portmapperapi" |
|
| 7 |
+ "gotest.tools/v3/assert" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type fakePortMapper struct{}
|
|
| 11 |
+ |
|
| 12 |
+func (f fakePortMapper) MapPorts(_ context.Context, _ []portmapperapi.PortBindingReq, _ portmapperapi.Firewaller) ([]portmapperapi.PortBinding, error) {
|
|
| 13 |
+ return nil, nil |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+func (f fakePortMapper) UnmapPorts(_ context.Context, _ []portmapperapi.PortBinding, _ portmapperapi.Firewaller) error {
|
|
| 17 |
+ return nil |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func TestRegisterPortMappers(t *testing.T) {
|
|
| 21 |
+ t.Run("register port mapper", func(t *testing.T) {
|
|
| 22 |
+ var pms PortMappers |
|
| 23 |
+ |
|
| 24 |
+ pm := fakePortMapper{}
|
|
| 25 |
+ err := pms.Register("test", pm)
|
|
| 26 |
+ assert.NilError(t, err) |
|
| 27 |
+ }) |
|
| 28 |
+ |
|
| 29 |
+ t.Run("empty name", func(t *testing.T) {
|
|
| 30 |
+ var pms PortMappers |
|
| 31 |
+ |
|
| 32 |
+ err := pms.Register("", nil)
|
|
| 33 |
+ assert.ErrorContains(t, err, "portmapper name cannot be empty") |
|
| 34 |
+ }) |
|
| 35 |
+ |
|
| 36 |
+ t.Run("duplicate port mapper", func(t *testing.T) {
|
|
| 37 |
+ var pms PortMappers |
|
| 38 |
+ |
|
| 39 |
+ err := pms.Register("test", nil)
|
|
| 40 |
+ assert.NilError(t, err) |
|
| 41 |
+ |
|
| 42 |
+ err = pms.Register("test", nil)
|
|
| 43 |
+ assert.ErrorContains(t, err, "portmapper already registered") |
|
| 44 |
+ }) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func TestGetPortMapper(t *testing.T) {
|
|
| 48 |
+ t.Run("get existing port mapper", func(t *testing.T) {
|
|
| 49 |
+ var pms PortMappers |
|
| 50 |
+ |
|
| 51 |
+ pm := fakePortMapper{}
|
|
| 52 |
+ err := pms.Register("test", pm)
|
|
| 53 |
+ assert.NilError(t, err) |
|
| 54 |
+ |
|
| 55 |
+ retrieved, err := pms.Get("test")
|
|
| 56 |
+ assert.NilError(t, err) |
|
| 57 |
+ assert.Equal(t, retrieved, pm) |
|
| 58 |
+ }) |
|
| 59 |
+ |
|
| 60 |
+ t.Run("get nonexistent port mapper", func(t *testing.T) {
|
|
| 61 |
+ var pms PortMappers |
|
| 62 |
+ |
|
| 63 |
+ _, err := pms.Get("nonexistent")
|
|
| 64 |
+ assert.ErrorContains(t, err, "portmapper nonexistent not found") |
|
| 65 |
+ }) |
|
| 66 |
+} |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package portmapperapi |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"net" |
| 5 | 6 |
"net/netip" |
| 6 | 7 |
"os" |
| ... | ... |
@@ -8,6 +9,31 @@ import ( |
| 8 | 8 |
"github.com/docker/docker/daemon/libnetwork/types" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
+// Registerer provides a callback interface for registering port-mappers. |
|
| 12 |
+type Registerer interface {
|
|
| 13 |
+ // Register provides a way for port-mappers to dynamically register with libnetwork. |
|
| 14 |
+ Register(name string, driver PortMapper) error |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+// PortMapper maps / unmaps container ports to host ports. |
|
| 18 |
+type PortMapper interface {
|
|
| 19 |
+ // MapPorts takes a list of port binding requests, and returns a list of |
|
| 20 |
+ // PortBinding. Both lists MUST have the same size. |
|
| 21 |
+ // |
|
| 22 |
+ // Multiple port bindings are passed when they're all requesting the |
|
| 23 |
+ // same port range, or an ephemeral port, over multiple IP addresses and |
|
| 24 |
+ // all pointing to the same container port. In that case, the PortMapper |
|
| 25 |
+ // MUST assign the same HostPort for all IP addresses. |
|
| 26 |
+ // |
|
| 27 |
+ // When an ephemeral port, or a single port from a range is requested |
|
| 28 |
+ // MapPorts should attempt a few times to find a free port available |
|
| 29 |
+ // across all IP addresses. |
|
| 30 |
+ MapPorts(ctx context.Context, reqs []PortBindingReq, fwn Firewaller) ([]PortBinding, error) |
|
| 31 |
+ |
|
| 32 |
+ // UnmapPorts takes a list of port bindings to unmap. |
|
| 33 |
+ UnmapPorts(ctx context.Context, pbs []PortBinding, fwn Firewaller) error |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 11 | 36 |
type PortBindingReq struct {
|
| 12 | 37 |
types.PortBinding |
| 13 | 38 |
// ChildHostIP is a temporary field used to pass the host IP address as |
| 14 | 39 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,14 @@ |
| 0 |
+package portmapperapi |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/daemon/libnetwork/types" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type Firewaller interface {
|
|
| 9 |
+ // AddPorts adds the configuration needed for NATing ports. |
|
| 10 |
+ AddPorts(ctx context.Context, pbs []types.PortBinding) error |
|
| 11 |
+ // DelPorts deletes the configuration needed for NATing ports. |
|
| 12 |
+ DelPorts(ctx context.Context, pbs []types.PortBinding) error |
|
| 13 |
+} |