Browse code

Support dockerd and system restarts for ipvlan and macvlan networks

This commit carries forward the work done in
https://github.com/docker/libnetwork/pull/2295
and fixes two things
1. Allows macvlan and ipvlan to be restored properly
after dockerd or the system is restarted
2. Makes sure the refcount for the configOnly network
is not incremented for the above case so this network
can be deleted after all the associated ConfigFrom networks
are deleted

Addresses: https://github.com/docker/libnetwork/issues/1743

Signed-off-by: Arko Dasgupta <arko.dasgupta@docker.com>

Arko Dasgupta authored on 2019/07/09 05:52:31
Showing 7 changed files
... ...
@@ -706,9 +706,10 @@ const overlayDSROptionString = "dsr"
706 706
 // are network specific and modeled in a generic way.
707 707
 func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
708 708
 	var (
709
-		cap *driverapi.Capability
710
-		err error
711
-		t   *network
709
+		cap            *driverapi.Capability
710
+		err            error
711
+		t              *network
712
+		skipCfgEpCount bool
712 713
 	)
713 714
 
714 715
 	if id != "" {
... ...
@@ -802,7 +803,7 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
802 802
 			return nil, types.InternalErrorf("Failed to apply configuration: %v", err)
803 803
 		}
804 804
 		defer func() {
805
-			if err == nil {
805
+			if err == nil && !skipCfgEpCount {
806 806
 				if err := t.getEpCnt().IncEndpointCnt(); err != nil {
807 807
 					logrus.Warnf("Failed to update reference count for configuration network %q on creation of network %q: %v",
808 808
 						t.Name(), network.Name(), err)
... ...
@@ -823,7 +824,13 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
823 823
 
824 824
 	err = c.addNetwork(network)
825 825
 	if err != nil {
826
-		return nil, err
826
+		if strings.Contains(err.Error(), "restoring existing network") {
827
+			// This error can be ignored and set this boolean
828
+			// value to skip a refcount increment for configOnly networks
829
+			skipCfgEpCount = true
830
+		} else {
831
+			return nil, err
832
+		}
827 833
 	}
828 834
 	defer func() {
829 835
 		if err != nil {
... ...
@@ -60,10 +60,14 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
60 60
 		// empty parent and --internal are handled the same. Set here to update k/v
61 61
 		config.Internal = true
62 62
 	}
63
-	err = d.createNetwork(config)
63
+	foundExisting, err := d.createNetwork(config)
64 64
 	if err != nil {
65 65
 		return err
66 66
 	}
67
+
68
+	if foundExisting {
69
+		return types.InternalMaskableErrorf("restoring existing network %s", config.ID)
70
+	}
67 71
 	// update persistent db, rollback on fail
68 72
 	err = d.storeUpdate(config)
69 73
 	if err != nil {
... ...
@@ -76,22 +80,34 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
76 76
 }
77 77
 
78 78
 // createNetwork is used by new network callbacks and persistent network cache
79
-func (d *driver) createNetwork(config *configuration) error {
79
+func (d *driver) createNetwork(config *configuration) (bool, error) {
80
+	foundExisting := false
80 81
 	networkList := d.getNetworks()
81 82
 	for _, nw := range networkList {
82 83
 		if config.Parent == nw.config.Parent {
83
-			return fmt.Errorf("network %s is already using parent interface %s",
84
-				getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
84
+			if config.ID != nw.config.ID {
85
+				return false, fmt.Errorf("network %s is already using parent interface %s",
86
+					getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
87
+			}
88
+			logrus.Debugf("Create Network for the same ID %s\n", config.ID)
89
+			foundExisting = true
90
+			break
85 91
 		}
86 92
 	}
87 93
 	if !parentExists(config.Parent) {
88 94
 		// if the --internal flag is set, create a dummy link
89 95
 		if config.Internal {
90
-			err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
91
-			if err != nil {
92
-				return err
96
+			if !dummyLinkExists(getDummyName(stringid.TruncateID(config.ID))) {
97
+				err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
98
+				if err != nil {
99
+					return false, err
100
+				}
101
+				config.CreatedSlaveLink = true
102
+
103
+			} else {
104
+				logrus.Debugf("Dummy Link %s for ipvlan already exists", getDummyName(stringid.TruncateID(config.ID)))
93 105
 			}
94
-			config.CreatedSlaveLink = true
106
+
95 107
 			// notify the user in logs they have limited communications
96 108
 			if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
97 109
 				logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
... ...
@@ -100,24 +116,31 @@ func (d *driver) createNetwork(config *configuration) error {
100 100
 		} else {
101 101
 			// if the subinterface parent_iface.vlan_id checks do not pass, return err.
102 102
 			//  a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
103
-			err := createVlanLink(config.Parent)
104
-			if err != nil {
105
-				return err
103
+			if !vlanLinkExists(config.Parent) {
104
+				err := createVlanLink(config.Parent)
105
+				if err != nil {
106
+					return false, err
107
+				}
108
+				// if driver created the networks slave link, record it for future deletion
109
+				config.CreatedSlaveLink = true
110
+			} else {
111
+				logrus.Debugf("Parent Sub Interface %s already Exists NetID %s", config.Parent, config.ID)
106 112
 			}
107
-			// if driver created the networks slave link, record it for future deletion
108
-			config.CreatedSlaveLink = true
113
+
109 114
 		}
110 115
 	}
111
-	n := &network{
112
-		id:        config.ID,
113
-		driver:    d,
114
-		endpoints: endpointTable{},
115
-		config:    config,
116
+	if !foundExisting {
117
+		n := &network{
118
+			id:        config.ID,
119
+			driver:    d,
120
+			endpoints: endpointTable{},
121
+			config:    config,
122
+		}
123
+		// add the network
124
+		d.addNetwork(n)
116 125
 	}
117
-	// add the *network
118
-	d.addNetwork(n)
119 126
 
120
-	return nil
127
+	return foundExisting, nil
121 128
 }
122 129
 
123 130
 // DeleteNetwork the network for the specified driver type
... ...
@@ -70,6 +70,15 @@ func parentExists(ifaceStr string) bool {
70 70
 	return true
71 71
 }
72 72
 
73
+// vlanLinkExists checks if specified vlan link exists in the default namespace
74
+func vlanLinkExists(linkStr string) bool {
75
+	_, err := ns.NlHandle().LinkByName(linkStr)
76
+	if err != nil {
77
+		return false
78
+	}
79
+	return true
80
+}
81
+
73 82
 // createVlanLink parses sub-interfaces and vlan id for creation
74 83
 func createVlanLink(parentName string) error {
75 84
 	if strings.Contains(parentName, ".") {
... ...
@@ -156,6 +165,15 @@ func parseVlan(linkName string) (string, int, error) {
156 156
 	return parent, vidInt, nil
157 157
 }
158 158
 
159
+// dummyLinkExists checks if dummylink exists in the default namespace
160
+func dummyLinkExists(dummyName string) bool {
161
+	_, err := ns.NlHandle().LinkByName(dummyName)
162
+	if err != nil {
163
+		return false
164
+	}
165
+	return true
166
+}
167
+
159 168
 // createDummyLink creates a dummy0 parent link
160 169
 func createDummyLink(dummyName, truncNetID string) error {
161 170
 	// create a parent interface since one was not specified
... ...
@@ -55,7 +55,14 @@ func (d *driver) initStore(option map[string]interface{}) error {
55 55
 			return types.InternalErrorf("ipvlan driver failed to initialize data store: %v", err)
56 56
 		}
57 57
 
58
-		return d.populateNetworks()
58
+		err = d.populateNetworks()
59
+		if err != nil {
60
+			return err
61
+		}
62
+		err = d.populateEndpoints()
63
+		if err != nil {
64
+			return err
65
+		}
59 66
 	}
60 67
 
61 68
 	return nil
... ...
@@ -73,7 +80,7 @@ func (d *driver) populateNetworks() error {
73 73
 	}
74 74
 	for _, kvo := range kvol {
75 75
 		config := kvo.(*configuration)
76
-		if err = d.createNetwork(config); err != nil {
76
+		if _, err = d.createNetwork(config); err != nil {
77 77
 			logrus.Warnf("could not create ipvlan network for id %s from persistent state", config.ID)
78 78
 		}
79 79
 	}
... ...
@@ -64,10 +64,15 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
64 64
 		// empty parent and --internal are handled the same. Set here to update k/v
65 65
 		config.Internal = true
66 66
 	}
67
-	err = d.createNetwork(config)
67
+	foundExisting, err := d.createNetwork(config)
68 68
 	if err != nil {
69 69
 		return err
70 70
 	}
71
+
72
+	if foundExisting {
73
+		return types.InternalMaskableErrorf("restoring existing network %s", config.ID)
74
+	}
75
+
71 76
 	// update persistent db, rollback on fail
72 77
 	err = d.storeUpdate(config)
73 78
 	if err != nil {
... ...
@@ -80,22 +85,32 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
80 80
 }
81 81
 
82 82
 // createNetwork is used by new network callbacks and persistent network cache
83
-func (d *driver) createNetwork(config *configuration) error {
83
+func (d *driver) createNetwork(config *configuration) (bool, error) {
84
+	foundExisting := false
84 85
 	networkList := d.getNetworks()
85 86
 	for _, nw := range networkList {
86 87
 		if config.Parent == nw.config.Parent {
87
-			return fmt.Errorf("network %s is already using parent interface %s",
88
-				getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
88
+			if config.ID != nw.config.ID {
89
+				return false, fmt.Errorf("network %s is already using parent interface %s",
90
+					getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
91
+			}
92
+			logrus.Debugf("Create Network for the same ID %s\n", config.ID)
93
+			foundExisting = true
94
+			break
89 95
 		}
90 96
 	}
91 97
 	if !parentExists(config.Parent) {
92 98
 		// if the --internal flag is set, create a dummy link
93 99
 		if config.Internal {
94
-			err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
95
-			if err != nil {
96
-				return err
100
+			if !dummyLinkExists(getDummyName(stringid.TruncateID(config.ID))) {
101
+				err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
102
+				if err != nil {
103
+					return false, err
104
+				}
105
+				config.CreatedSlaveLink = true
106
+			} else {
107
+				logrus.Debugf("Dummy Link %s for Mac Vlan already exists", getDummyName(stringid.TruncateID(config.ID)))
97 108
 			}
98
-			config.CreatedSlaveLink = true
99 109
 			// notify the user in logs they have limited communications
100 110
 			if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
101 111
 				logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
... ...
@@ -104,24 +119,33 @@ func (d *driver) createNetwork(config *configuration) error {
104 104
 		} else {
105 105
 			// if the subinterface parent_iface.vlan_id checks do not pass, return err.
106 106
 			//  a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
107
-			err := createVlanLink(config.Parent)
108
-			if err != nil {
109
-				return err
107
+
108
+			if !vlanLinkExists(config.Parent) {
109
+				// if the subinterface parent_iface.vlan_id checks do not pass, return err.
110
+				//  a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
111
+				err := createVlanLink(config.Parent)
112
+				if err != nil {
113
+					return false, err
114
+				}
115
+				// if driver created the networks slave link, record it for future deletion
116
+				config.CreatedSlaveLink = true
117
+			} else {
118
+				logrus.Debugf("Parent Sub Interface %s already Exists NetID %s", config.Parent, config.ID)
110 119
 			}
111
-			// if driver created the networks slave link, record it for future deletion
112
-			config.CreatedSlaveLink = true
113 120
 		}
114 121
 	}
115
-	n := &network{
116
-		id:        config.ID,
117
-		driver:    d,
118
-		endpoints: endpointTable{},
119
-		config:    config,
122
+	if !foundExisting {
123
+		n := &network{
124
+			id:        config.ID,
125
+			driver:    d,
126
+			endpoints: endpointTable{},
127
+			config:    config,
128
+		}
129
+		// add the network
130
+		d.addNetwork(n)
120 131
 	}
121
-	// add the *network
122
-	d.addNetwork(n)
123 132
 
124
-	return nil
133
+	return foundExisting, nil
125 134
 }
126 135
 
127 136
 // DeleteNetwork deletes the network for the specified driver type
... ...
@@ -74,6 +74,15 @@ func parentExists(ifaceStr string) bool {
74 74
 	return true
75 75
 }
76 76
 
77
+// vlanLinkExists checks if specified vlan link exists in the default namespace
78
+func vlanLinkExists(linkStr string) bool {
79
+	_, err := ns.NlHandle().LinkByName(linkStr)
80
+	if err != nil {
81
+		return false
82
+	}
83
+	return true
84
+}
85
+
77 86
 // createVlanLink parses sub-interfaces and vlan id for creation
78 87
 func createVlanLink(parentName string) error {
79 88
 	if strings.Contains(parentName, ".") {
... ...
@@ -160,6 +169,15 @@ func parseVlan(linkName string) (string, int, error) {
160 160
 	return parent, vidInt, nil
161 161
 }
162 162
 
163
+// dummyLinkExists checks if dummylink exists in the default namespace
164
+func dummyLinkExists(dummyName string) bool {
165
+	_, err := ns.NlHandle().LinkByName(dummyName)
166
+	if err != nil {
167
+		return false
168
+	}
169
+	return true
170
+}
171
+
163 172
 // createDummyLink creates a dummy0 parent link
164 173
 func createDummyLink(dummyName, truncNetID string) error {
165 174
 	// create a parent interface since one was not specified
... ...
@@ -55,7 +55,15 @@ func (d *driver) initStore(option map[string]interface{}) error {
55 55
 			return types.InternalErrorf("macvlan driver failed to initialize data store: %v", err)
56 56
 		}
57 57
 
58
-		return d.populateNetworks()
58
+		err = d.populateNetworks()
59
+		if err != nil {
60
+			return err
61
+		}
62
+		err = d.populateEndpoints()
63
+		if err != nil {
64
+			return err
65
+		}
66
+
59 67
 	}
60 68
 
61 69
 	return nil
... ...
@@ -73,7 +81,7 @@ func (d *driver) populateNetworks() error {
73 73
 	}
74 74
 	for _, kvo := range kvol {
75 75
 		config := kvo.(*configuration)
76
-		if err = d.createNetwork(config); err != nil {
76
+		if _, err = d.createNetwork(config); err != nil {
77 77
 			logrus.Warnf("Could not create macvlan network for id %s from persistent state", config.ID)
78 78
 		}
79 79
 	}