Move load balancer sandbox creation/deletion into libnetwork
| ... | ... |
@@ -3,6 +3,7 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"net" |
| 6 |
+ "runtime" |
|
| 6 | 7 |
"sort" |
| 7 | 8 |
"strings" |
| 8 | 9 |
"sync" |
| ... | ... |
@@ -183,21 +184,14 @@ func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip |
| 183 | 183 |
// Otherwise continue down the call to create or recreate sandbox. |
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 |
- n, err := daemon.GetNetworkByID(create.ID) |
|
| 186 |
+ _, err := daemon.GetNetworkByID(create.ID) |
|
| 187 | 187 |
if err != nil {
|
| 188 | 188 |
logrus.Errorf("Failed getting ingress network by id after creating: %v", err)
|
| 189 | 189 |
} |
| 190 |
- |
|
| 191 |
- if err = daemon.createLoadBalancerSandbox("ingress", create.ID, ip, n, libnetwork.OptionIngress()); err != nil {
|
|
| 192 |
- logrus.Errorf("Failed creating load balancer sandbox for ingress network: %v", err)
|
|
| 193 |
- } |
|
| 194 | 190 |
} |
| 195 | 191 |
|
| 196 | 192 |
func (daemon *Daemon) releaseIngress(id string) {
|
| 197 | 193 |
controller := daemon.netController |
| 198 |
- if err := controller.SandboxDestroy("ingress-sbox"); err != nil {
|
|
| 199 |
- logrus.Errorf("Failed to delete ingress sandbox: %v", err)
|
|
| 200 |
- } |
|
| 201 | 194 |
|
| 202 | 195 |
if id == "" {
|
| 203 | 196 |
return |
| ... | ... |
@@ -209,13 +203,6 @@ func (daemon *Daemon) releaseIngress(id string) {
|
| 209 | 209 |
return |
| 210 | 210 |
} |
| 211 | 211 |
|
| 212 |
- for _, ep := range n.Endpoints() {
|
|
| 213 |
- if err := ep.Delete(true); err != nil {
|
|
| 214 |
- logrus.Errorf("Failed to delete endpoint %s (%s): %v", ep.Name(), ep.ID(), err)
|
|
| 215 |
- return |
|
| 216 |
- } |
|
| 217 |
- } |
|
| 218 |
- |
|
| 219 | 212 |
if err := n.Delete(); err != nil {
|
| 220 | 213 |
logrus.Errorf("Failed to delete ingress network %s: %v", n.ID(), err)
|
| 221 | 214 |
return |
| ... | ... |
@@ -270,34 +257,6 @@ func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.N |
| 270 | 270 |
return resp, err |
| 271 | 271 |
} |
| 272 | 272 |
|
| 273 |
-func (daemon *Daemon) createLoadBalancerSandbox(prefix, id string, ip net.IP, n libnetwork.Network, options ...libnetwork.SandboxOption) error {
|
|
| 274 |
- c := daemon.netController |
|
| 275 |
- sandboxName := prefix + "-sbox" |
|
| 276 |
- sb, err := c.NewSandbox(sandboxName, options...) |
|
| 277 |
- if err != nil {
|
|
| 278 |
- if _, ok := err.(networktypes.ForbiddenError); !ok {
|
|
| 279 |
- return errors.Wrapf(err, "Failed creating %s sandbox", sandboxName) |
|
| 280 |
- } |
|
| 281 |
- return nil |
|
| 282 |
- } |
|
| 283 |
- |
|
| 284 |
- endpointName := prefix + "-endpoint" |
|
| 285 |
- ep, err := n.CreateEndpoint(endpointName, libnetwork.CreateOptionIpam(ip, nil, nil, nil), libnetwork.CreateOptionLoadBalancer()) |
|
| 286 |
- if err != nil {
|
|
| 287 |
- return errors.Wrapf(err, "Failed creating %s in sandbox %s", endpointName, sandboxName) |
|
| 288 |
- } |
|
| 289 |
- |
|
| 290 |
- if err := ep.Join(sb, nil); err != nil {
|
|
| 291 |
- return errors.Wrapf(err, "Failed joining %s to sandbox %s", endpointName, sandboxName) |
|
| 292 |
- } |
|
| 293 |
- |
|
| 294 |
- if err := sb.EnableService(); err != nil {
|
|
| 295 |
- return errors.Wrapf(err, "Failed enabling service in %s sandbox", sandboxName) |
|
| 296 |
- } |
|
| 297 |
- |
|
| 298 |
- return nil |
|
| 299 |
-} |
|
| 300 |
- |
|
| 301 | 273 |
func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
|
| 302 | 274 |
if runconfig.IsPreDefinedNetwork(create.Name) && !agent {
|
| 303 | 275 |
err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
|
| ... | ... |
@@ -360,6 +319,15 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string |
| 360 | 360 |
nwOptions = append(nwOptions, libnetwork.NetworkOptionConfigFrom(create.ConfigFrom.Network)) |
| 361 | 361 |
} |
| 362 | 362 |
|
| 363 |
+ if agent && driver == "overlay" && (create.Ingress || runtime.GOOS == "windows") {
|
|
| 364 |
+ nodeIP, exists := daemon.GetAttachmentStore().GetIPForNetwork(id) |
|
| 365 |
+ if !exists {
|
|
| 366 |
+ return nil, fmt.Errorf("Failed to find a load balancer IP to use for network: %v", id)
|
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ nwOptions = append(nwOptions, libnetwork.NetworkOptionLBEndpoint(nodeIP)) |
|
| 370 |
+ } |
|
| 371 |
+ |
|
| 363 | 372 |
n, err := c.NewNetwork(driver, create.Name, id, nwOptions...) |
| 364 | 373 |
if err != nil {
|
| 365 | 374 |
if _, ok := err.(libnetwork.ErrDataStoreNotInitialized); ok {
|
| ... | ... |
@@ -375,18 +343,6 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string |
| 375 | 375 |
} |
| 376 | 376 |
daemon.LogNetworkEvent(n, "create") |
| 377 | 377 |
|
| 378 |
- if agent && !n.Info().Ingress() && n.Type() == "overlay" {
|
|
| 379 |
- nodeIP, exists := daemon.GetAttachmentStore().GetIPForNetwork(id) |
|
| 380 |
- if !exists {
|
|
| 381 |
- return nil, fmt.Errorf("Failed to find a load balancer IP to use for network: %v", id)
|
|
| 382 |
- } |
|
| 383 |
- |
|
| 384 |
- if err := daemon.createLoadBalancerSandbox(create.Name, id, nodeIP, n); err != nil {
|
|
| 385 |
- return nil, err |
|
| 386 |
- } |
|
| 387 |
- |
|
| 388 |
- } |
|
| 389 |
- |
|
| 390 | 378 |
return &types.NetworkCreateResponse{
|
| 391 | 379 |
ID: n.ID(), |
| 392 | 380 |
Warning: warning, |
| ... | ... |
@@ -517,43 +473,16 @@ func (daemon *Daemon) DeleteNetwork(networkID string) error {
|
| 517 | 517 |
return daemon.deleteNetwork(networkID, false) |
| 518 | 518 |
} |
| 519 | 519 |
|
| 520 |
-func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) {
|
|
| 521 |
- controller := daemon.netController |
|
| 522 |
- |
|
| 523 |
- //The only endpoint left should be the LB endpoint (nw.Name() + "-endpoint") |
|
| 524 |
- endpoints := n.Endpoints() |
|
| 525 |
- if len(endpoints) == 1 {
|
|
| 526 |
- sandboxName := n.Name() + "-sbox" |
|
| 527 |
- |
|
| 528 |
- info := endpoints[0].Info() |
|
| 529 |
- if info != nil {
|
|
| 530 |
- sb := info.Sandbox() |
|
| 531 |
- if sb != nil {
|
|
| 532 |
- if err := sb.DisableService(); err != nil {
|
|
| 533 |
- logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
|
|
| 534 |
- //Ignore error and attempt to delete the load balancer endpoint |
|
| 535 |
- } |
|
| 536 |
- } |
|
| 537 |
- } |
|
| 538 |
- |
|
| 539 |
- if err := endpoints[0].Delete(true); err != nil {
|
|
| 540 |
- logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoints[0].Name(), endpoints[0].ID(), sandboxName, err)
|
|
| 541 |
- //Ignore error and attempt to delete the sandbox. |
|
| 542 |
- } |
|
| 543 |
- |
|
| 544 |
- if err := controller.SandboxDestroy(sandboxName); err != nil {
|
|
| 545 |
- logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err)
|
|
| 546 |
- //Ignore error and attempt to delete the network. |
|
| 547 |
- } |
|
| 548 |
- } |
|
| 549 |
-} |
|
| 550 |
- |
|
| 551 | 520 |
func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
|
| 552 | 521 |
nw, err := daemon.FindNetwork(networkID) |
| 553 | 522 |
if err != nil {
|
| 554 | 523 |
return err |
| 555 | 524 |
} |
| 556 | 525 |
|
| 526 |
+ if nw.Info().Ingress() {
|
|
| 527 |
+ return nil |
|
| 528 |
+ } |
|
| 529 |
+ |
|
| 557 | 530 |
if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
|
| 558 | 531 |
err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
|
| 559 | 532 |
return notAllowedError{err}
|
| ... | ... |
@@ -569,10 +498,6 @@ func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
|
| 569 | 569 |
return notAllowedError{err}
|
| 570 | 570 |
} |
| 571 | 571 |
|
| 572 |
- if !nw.Info().Ingress() && nw.Type() == "overlay" {
|
|
| 573 |
- daemon.deleteLoadBalancerSandbox(nw) |
|
| 574 |
- } |
|
| 575 |
- |
|
| 576 | 572 |
if err := nw.Delete(); err != nil {
|
| 577 | 573 |
return err |
| 578 | 574 |
} |
| ... | ... |
@@ -11,12 +11,11 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/client" |
| 12 | 12 |
"github.com/docker/docker/integration-cli/request" |
| 13 | 13 |
"github.com/gotestyourself/gotestyourself/poll" |
| 14 |
- "github.com/stretchr/testify/assert" |
|
| 15 | 14 |
"github.com/stretchr/testify/require" |
| 16 | 15 |
"golang.org/x/net/context" |
| 17 | 16 |
) |
| 18 | 17 |
|
| 19 |
-func TestCreateWithLBSandbox(t *testing.T) {
|
|
| 18 |
+func TestCreateServiceMultipleTimes(t *testing.T) {
|
|
| 20 | 19 |
defer setupTest(t)() |
| 21 | 20 |
d := newSwarm(t) |
| 22 | 21 |
defer d.Stop(t) |
| ... | ... |
@@ -33,9 +32,8 @@ func TestCreateWithLBSandbox(t *testing.T) {
|
| 33 | 33 |
require.NoError(t, err) |
| 34 | 34 |
overlayID := netResp.ID |
| 35 | 35 |
|
| 36 |
- var instances uint64 = 1 |
|
| 36 |
+ var instances uint64 = 4 |
|
| 37 | 37 |
serviceSpec := swarmServiceSpec("TestService", instances)
|
| 38 |
- |
|
| 39 | 38 |
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: overlayName})
|
| 40 | 39 |
|
| 41 | 40 |
serviceResp, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
|
| ... | ... |
@@ -56,14 +54,26 @@ func TestCreateWithLBSandbox(t *testing.T) {
|
| 56 | 56 |
_, _, err = client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
|
| 57 | 57 |
require.NoError(t, err) |
| 58 | 58 |
|
| 59 |
- network, err := client.NetworkInspect(context.Background(), overlayID, types.NetworkInspectOptions{})
|
|
| 60 |
- require.NoError(t, err) |
|
| 61 |
- assert.Contains(t, network.Containers, overlayName+"-sbox") |
|
| 62 |
- |
|
| 63 | 59 |
err = client.ServiceRemove(context.Background(), serviceID) |
| 64 | 60 |
require.NoError(t, err) |
| 65 | 61 |
|
| 66 | 62 |
poll.WaitOn(t, serviceIsRemoved(client, serviceID), pollSettings) |
| 63 |
+ poll.WaitOn(t, noTasks(client), pollSettings) |
|
| 64 |
+ |
|
| 65 |
+ serviceResp, err = client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
|
|
| 66 |
+ QueryRegistry: false, |
|
| 67 |
+ }) |
|
| 68 |
+ require.NoError(t, err) |
|
| 69 |
+ |
|
| 70 |
+ serviceID2 := serviceResp.ID |
|
| 71 |
+ poll.WaitOn(t, serviceRunningTasksCount(client, serviceID2, instances), pollSettings) |
|
| 72 |
+ |
|
| 73 |
+ err = client.ServiceRemove(context.Background(), serviceID2) |
|
| 74 |
+ require.NoError(t, err) |
|
| 75 |
+ |
|
| 76 |
+ poll.WaitOn(t, serviceIsRemoved(client, serviceID2), pollSettings) |
|
| 77 |
+ poll.WaitOn(t, noTasks(client), pollSettings) |
|
| 78 |
+ |
|
| 67 | 79 |
err = client.NetworkRemove(context.Background(), overlayID) |
| 68 | 80 |
require.NoError(t, err) |
| 69 | 81 |
|
| ... | ... |
@@ -112,6 +122,23 @@ func serviceRunningTasksCount(client client.ServiceAPIClient, serviceID string, |
| 112 | 112 |
} |
| 113 | 113 |
} |
| 114 | 114 |
|
| 115 |
+func noTasks(client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
|
|
| 116 |
+ return func(log poll.LogT) poll.Result {
|
|
| 117 |
+ filter := filters.NewArgs() |
|
| 118 |
+ tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
|
|
| 119 |
+ Filters: filter, |
|
| 120 |
+ }) |
|
| 121 |
+ switch {
|
|
| 122 |
+ case err != nil: |
|
| 123 |
+ return poll.Error(err) |
|
| 124 |
+ case len(tasks) == 0: |
|
| 125 |
+ return poll.Success() |
|
| 126 |
+ default: |
|
| 127 |
+ return poll.Continue("task count at %d waiting for 0", len(tasks))
|
|
| 128 |
+ } |
|
| 129 |
+ } |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 115 | 132 |
func serviceIsRemoved(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
|
| 116 | 133 |
return func(log poll.LogT) poll.Result {
|
| 117 | 134 |
filter := filters.NewArgs() |
| ... | ... |
@@ -30,7 +30,7 @@ github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8 |
| 30 | 30 |
github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 |
| 31 | 31 |
|
| 32 | 32 |
#get libnetwork packages |
| 33 |
-github.com/docker/libnetwork f7d21337cf1eb628ad54eecac0881fa23ec266df |
|
| 33 |
+github.com/docker/libnetwork 64ae58878fc8f95e4a167499d654e13fa36abdc7 |
|
| 34 | 34 |
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 |
| 35 | 35 |
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 |
| 36 | 36 |
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec |
| ... | ... |
@@ -837,11 +837,34 @@ addToStore: |
| 837 | 837 |
if err = c.updateToStore(network); err != nil {
|
| 838 | 838 |
return nil, err |
| 839 | 839 |
} |
| 840 |
+ defer func() {
|
|
| 841 |
+ if err != nil {
|
|
| 842 |
+ if e := c.deleteFromStore(network); e != nil {
|
|
| 843 |
+ logrus.Warnf("could not rollback from store, network %v on failure (%v): %v", network, err, e)
|
|
| 844 |
+ } |
|
| 845 |
+ } |
|
| 846 |
+ }() |
|
| 847 |
+ |
|
| 840 | 848 |
if network.configOnly {
|
| 841 | 849 |
return network, nil |
| 842 | 850 |
} |
| 843 | 851 |
|
| 844 | 852 |
joinCluster(network) |
| 853 |
+ defer func() {
|
|
| 854 |
+ if err != nil {
|
|
| 855 |
+ network.cancelDriverWatches() |
|
| 856 |
+ if e := network.leaveCluster(); e != nil {
|
|
| 857 |
+ logrus.Warnf("Failed to leave agent cluster on network %s on failure (%v): %v", network.name, err, e)
|
|
| 858 |
+ } |
|
| 859 |
+ } |
|
| 860 |
+ }() |
|
| 861 |
+ |
|
| 862 |
+ if len(network.loadBalancerIP) != 0 {
|
|
| 863 |
+ if err = network.createLoadBalancerSandbox(); err != nil {
|
|
| 864 |
+ return nil, err |
|
| 865 |
+ } |
|
| 866 |
+ } |
|
| 867 |
+ |
|
| 845 | 868 |
if !c.isDistributedControl() {
|
| 846 | 869 |
c.Lock() |
| 847 | 870 |
arrangeIngressFilterRule() |
| ... | ... |
@@ -199,39 +199,40 @@ func (i *IpamInfo) UnmarshalJSON(data []byte) error {
|
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 | 201 |
type network struct {
|
| 202 |
- ctrlr *controller |
|
| 203 |
- name string |
|
| 204 |
- networkType string |
|
| 205 |
- id string |
|
| 206 |
- created time.Time |
|
| 207 |
- scope string // network data scope |
|
| 208 |
- labels map[string]string |
|
| 209 |
- ipamType string |
|
| 210 |
- ipamOptions map[string]string |
|
| 211 |
- addrSpace string |
|
| 212 |
- ipamV4Config []*IpamConf |
|
| 213 |
- ipamV6Config []*IpamConf |
|
| 214 |
- ipamV4Info []*IpamInfo |
|
| 215 |
- ipamV6Info []*IpamInfo |
|
| 216 |
- enableIPv6 bool |
|
| 217 |
- postIPv6 bool |
|
| 218 |
- epCnt *endpointCnt |
|
| 219 |
- generic options.Generic |
|
| 220 |
- dbIndex uint64 |
|
| 221 |
- dbExists bool |
|
| 222 |
- persist bool |
|
| 223 |
- stopWatchCh chan struct{}
|
|
| 224 |
- drvOnce *sync.Once |
|
| 225 |
- resolverOnce sync.Once |
|
| 226 |
- resolver []Resolver |
|
| 227 |
- internal bool |
|
| 228 |
- attachable bool |
|
| 229 |
- inDelete bool |
|
| 230 |
- ingress bool |
|
| 231 |
- driverTables []networkDBTable |
|
| 232 |
- dynamic bool |
|
| 233 |
- configOnly bool |
|
| 234 |
- configFrom string |
|
| 202 |
+ ctrlr *controller |
|
| 203 |
+ name string |
|
| 204 |
+ networkType string |
|
| 205 |
+ id string |
|
| 206 |
+ created time.Time |
|
| 207 |
+ scope string // network data scope |
|
| 208 |
+ labels map[string]string |
|
| 209 |
+ ipamType string |
|
| 210 |
+ ipamOptions map[string]string |
|
| 211 |
+ addrSpace string |
|
| 212 |
+ ipamV4Config []*IpamConf |
|
| 213 |
+ ipamV6Config []*IpamConf |
|
| 214 |
+ ipamV4Info []*IpamInfo |
|
| 215 |
+ ipamV6Info []*IpamInfo |
|
| 216 |
+ enableIPv6 bool |
|
| 217 |
+ postIPv6 bool |
|
| 218 |
+ epCnt *endpointCnt |
|
| 219 |
+ generic options.Generic |
|
| 220 |
+ dbIndex uint64 |
|
| 221 |
+ dbExists bool |
|
| 222 |
+ persist bool |
|
| 223 |
+ stopWatchCh chan struct{}
|
|
| 224 |
+ drvOnce *sync.Once |
|
| 225 |
+ resolverOnce sync.Once |
|
| 226 |
+ resolver []Resolver |
|
| 227 |
+ internal bool |
|
| 228 |
+ attachable bool |
|
| 229 |
+ inDelete bool |
|
| 230 |
+ ingress bool |
|
| 231 |
+ driverTables []networkDBTable |
|
| 232 |
+ dynamic bool |
|
| 233 |
+ configOnly bool |
|
| 234 |
+ configFrom string |
|
| 235 |
+ loadBalancerIP net.IP |
|
| 235 | 236 |
sync.Mutex |
| 236 | 237 |
} |
| 237 | 238 |
|
| ... | ... |
@@ -473,6 +474,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
|
| 473 | 473 |
dstN.ingress = n.ingress |
| 474 | 474 |
dstN.configOnly = n.configOnly |
| 475 | 475 |
dstN.configFrom = n.configFrom |
| 476 |
+ dstN.loadBalancerIP = n.loadBalancerIP |
|
| 476 | 477 |
|
| 477 | 478 |
// copy labels |
| 478 | 479 |
if dstN.labels == nil {
|
| ... | ... |
@@ -589,6 +591,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
|
| 589 | 589 |
netMap["ingress"] = n.ingress |
| 590 | 590 |
netMap["configOnly"] = n.configOnly |
| 591 | 591 |
netMap["configFrom"] = n.configFrom |
| 592 |
+ netMap["loadBalancerIP"] = n.loadBalancerIP |
|
| 592 | 593 |
return json.Marshal(netMap) |
| 593 | 594 |
} |
| 594 | 595 |
|
| ... | ... |
@@ -699,6 +702,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
|
| 699 | 699 |
if v, ok := netMap["configFrom"]; ok {
|
| 700 | 700 |
n.configFrom = v.(string) |
| 701 | 701 |
} |
| 702 |
+ if v, ok := netMap["loadBalancerIP"]; ok {
|
|
| 703 |
+ n.loadBalancerIP = net.ParseIP(v.(string)) |
|
| 704 |
+ } |
|
| 702 | 705 |
// Reconcile old networks with the recently added `--ipv6` flag |
| 703 | 706 |
if !n.enableIPv6 {
|
| 704 | 707 |
n.enableIPv6 = len(n.ipamV6Info) > 0 |
| ... | ... |
@@ -799,6 +805,13 @@ func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ip |
| 799 | 799 |
} |
| 800 | 800 |
} |
| 801 | 801 |
|
| 802 |
+// NetworkOptionLBEndpoint function returns an option setter for the configuration of the load balancer endpoint for this network |
|
| 803 |
+func NetworkOptionLBEndpoint(ip net.IP) NetworkOption {
|
|
| 804 |
+ return func(n *network) {
|
|
| 805 |
+ n.loadBalancerIP = ip |
|
| 806 |
+ } |
|
| 807 |
+} |
|
| 808 |
+ |
|
| 802 | 809 |
// NetworkOptionDriverOpts function returns an option setter for any driver parameter described by a map |
| 803 | 810 |
func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
|
| 804 | 811 |
return func(n *network) {
|
| ... | ... |
@@ -944,6 +957,18 @@ func (n *network) delete(force bool) error {
|
| 944 | 944 |
return &UnknownNetworkError{name: name, id: id}
|
| 945 | 945 |
} |
| 946 | 946 |
|
| 947 |
+ if len(n.loadBalancerIP) != 0 {
|
|
| 948 |
+ endpoints := n.Endpoints() |
|
| 949 |
+ if force || len(endpoints) == 1 {
|
|
| 950 |
+ n.deleteLoadBalancerSandbox() |
|
| 951 |
+ } |
|
| 952 |
+ //Reload the network from the store to update the epcnt. |
|
| 953 |
+ n, err = c.getNetworkFromStore(id) |
|
| 954 |
+ if err != nil {
|
|
| 955 |
+ return &UnknownNetworkError{name: name, id: id}
|
|
| 956 |
+ } |
|
| 957 |
+ } |
|
| 958 |
+ |
|
| 947 | 959 |
if !force && n.getEpCnt().EndpointCnt() != 0 {
|
| 948 | 960 |
if n.configOnly {
|
| 949 | 961 |
return types.ForbiddenErrorf("configuration network %q is in use", n.Name())
|
| ... | ... |
@@ -1071,12 +1096,19 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi |
| 1071 | 1071 |
return nil, types.ForbiddenErrorf("endpoint with name %s already exists in network %s", name, n.Name())
|
| 1072 | 1072 |
} |
| 1073 | 1073 |
|
| 1074 |
- ep := &endpoint{name: name, generic: make(map[string]interface{}), iface: &endpointInterface{}}
|
|
| 1075 |
- ep.id = stringid.GenerateRandomID() |
|
| 1076 |
- |
|
| 1077 | 1074 |
n.ctrlr.networkLocker.Lock(n.id) |
| 1078 | 1075 |
defer n.ctrlr.networkLocker.Unlock(n.id) |
| 1079 | 1076 |
|
| 1077 |
+ return n.createEndpoint(name, options...) |
|
| 1078 |
+ |
|
| 1079 |
+} |
|
| 1080 |
+ |
|
| 1081 |
+func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoint, error) {
|
|
| 1082 |
+ var err error |
|
| 1083 |
+ |
|
| 1084 |
+ ep := &endpoint{name: name, generic: make(map[string]interface{}), iface: &endpointInterface{}}
|
|
| 1085 |
+ ep.id = stringid.GenerateRandomID() |
|
| 1086 |
+ |
|
| 1080 | 1087 |
// Initialize ep.network with a possibly stale copy of n. We need this to get network from |
| 1081 | 1088 |
// store. But once we get it from store we will have the most uptodate copy possibly. |
| 1082 | 1089 |
ep.network = n |
| ... | ... |
@@ -2021,3 +2053,80 @@ func (c *controller) getConfigNetwork(name string) (*network, error) {
|
| 2021 | 2021 |
|
| 2022 | 2022 |
return n.(*network), nil |
| 2023 | 2023 |
} |
| 2024 |
+ |
|
| 2025 |
+func (n *network) createLoadBalancerSandbox() error {
|
|
| 2026 |
+ sandboxName := n.name + "-sbox" |
|
| 2027 |
+ sbOptions := []SandboxOption{}
|
|
| 2028 |
+ if n.ingress {
|
|
| 2029 |
+ sbOptions = append(sbOptions, OptionIngress()) |
|
| 2030 |
+ } |
|
| 2031 |
+ sb, err := n.ctrlr.NewSandbox(sandboxName, sbOptions...) |
|
| 2032 |
+ if err != nil {
|
|
| 2033 |
+ return err |
|
| 2034 |
+ } |
|
| 2035 |
+ defer func() {
|
|
| 2036 |
+ if err != nil {
|
|
| 2037 |
+ if e := n.ctrlr.SandboxDestroy(sandboxName); e != nil {
|
|
| 2038 |
+ logrus.Warnf("could not delete sandbox %s on failure on failure (%v): %v", sandboxName, err, e)
|
|
| 2039 |
+ } |
|
| 2040 |
+ } |
|
| 2041 |
+ }() |
|
| 2042 |
+ |
|
| 2043 |
+ endpointName := n.name + "-endpoint" |
|
| 2044 |
+ epOptions := []EndpointOption{
|
|
| 2045 |
+ CreateOptionIpam(n.loadBalancerIP, nil, nil, nil), |
|
| 2046 |
+ CreateOptionLoadBalancer(), |
|
| 2047 |
+ } |
|
| 2048 |
+ ep, err := n.createEndpoint(endpointName, epOptions...) |
|
| 2049 |
+ if err != nil {
|
|
| 2050 |
+ return err |
|
| 2051 |
+ } |
|
| 2052 |
+ defer func() {
|
|
| 2053 |
+ if err != nil {
|
|
| 2054 |
+ if e := ep.Delete(true); e != nil {
|
|
| 2055 |
+ logrus.Warnf("could not delete endpoint %s on failure on failure (%v): %v", endpointName, err, e)
|
|
| 2056 |
+ } |
|
| 2057 |
+ } |
|
| 2058 |
+ }() |
|
| 2059 |
+ |
|
| 2060 |
+ if err := ep.Join(sb, nil); err != nil {
|
|
| 2061 |
+ return err |
|
| 2062 |
+ } |
|
| 2063 |
+ return sb.EnableService() |
|
| 2064 |
+} |
|
| 2065 |
+ |
|
| 2066 |
+func (n *network) deleteLoadBalancerSandbox() {
|
|
| 2067 |
+ n.Lock() |
|
| 2068 |
+ c := n.ctrlr |
|
| 2069 |
+ name := n.name |
|
| 2070 |
+ n.Unlock() |
|
| 2071 |
+ |
|
| 2072 |
+ endpointName := name + "-endpoint" |
|
| 2073 |
+ sandboxName := name + "-sbox" |
|
| 2074 |
+ |
|
| 2075 |
+ endpoint, err := n.EndpointByName(endpointName) |
|
| 2076 |
+ if err != nil {
|
|
| 2077 |
+ logrus.Warnf("Failed to find load balancer endpoint %s on network %s: %v", endpointName, name, err)
|
|
| 2078 |
+ } else {
|
|
| 2079 |
+ |
|
| 2080 |
+ info := endpoint.Info() |
|
| 2081 |
+ if info != nil {
|
|
| 2082 |
+ sb := info.Sandbox() |
|
| 2083 |
+ if sb != nil {
|
|
| 2084 |
+ if err := sb.DisableService(); err != nil {
|
|
| 2085 |
+ logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
|
|
| 2086 |
+ //Ignore error and attempt to delete the load balancer endpoint |
|
| 2087 |
+ } |
|
| 2088 |
+ } |
|
| 2089 |
+ } |
|
| 2090 |
+ |
|
| 2091 |
+ if err := endpoint.Delete(true); err != nil {
|
|
| 2092 |
+ logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoint.Name(), endpoint.ID(), sandboxName, err)
|
|
| 2093 |
+ //Ignore error and attempt to delete the sandbox. |
|
| 2094 |
+ } |
|
| 2095 |
+ } |
|
| 2096 |
+ |
|
| 2097 |
+ if err := c.SandboxDestroy(sandboxName); err != nil {
|
|
| 2098 |
+ logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err)
|
|
| 2099 |
+ } |
|
| 2100 |
+} |
| ... | ... |
@@ -256,7 +256,7 @@ retry: |
| 256 | 256 |
if err := cs.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil {
|
| 257 | 257 |
return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err)
|
| 258 | 258 |
} |
| 259 |
- logrus.Errorf("Error (%v) deleting object %v, retrying....", err, kvObject.Key())
|
|
| 259 |
+ logrus.Warnf("Error (%v) deleting object %v, retrying....", err, kvObject.Key())
|
|
| 260 | 260 |
goto retry |
| 261 | 261 |
} |
| 262 | 262 |
return err |