Signed-off-by: Santhosh Manohar <santhosh@docker.com>
| ... | ... |
@@ -143,7 +143,7 @@ type controller struct {
|
| 143 | 143 |
extKeyListener net.Listener |
| 144 | 144 |
watchCh chan *endpoint |
| 145 | 145 |
unWatchCh chan *endpoint |
| 146 |
- svcDb map[string]svcMap |
|
| 146 |
+ svcDb map[string]svcInfo |
|
| 147 | 147 |
nmap map[string]*netWatch |
| 148 | 148 |
defOsSbox osl.Sandbox |
| 149 | 149 |
sboxOnce sync.Once |
| ... | ... |
@@ -171,7 +171,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
| 171 | 171 |
sandboxes: sandboxTable{},
|
| 172 | 172 |
drivers: driverTable{},
|
| 173 | 173 |
ipamDrivers: ipamTable{},
|
| 174 |
- svcDb: make(map[string]svcMap), |
|
| 174 |
+ svcDb: make(map[string]svcInfo), |
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
if err := c.initStores(); err != nil {
|
| ... | ... |
@@ -50,21 +50,22 @@ type Endpoint interface {
|
| 50 | 50 |
type EndpointOption func(ep *endpoint) |
| 51 | 51 |
|
| 52 | 52 |
type endpoint struct {
|
| 53 |
- name string |
|
| 54 |
- id string |
|
| 55 |
- network *network |
|
| 56 |
- iface *endpointInterface |
|
| 57 |
- joinInfo *endpointJoinInfo |
|
| 58 |
- sandboxID string |
|
| 59 |
- exposedPorts []types.TransportPort |
|
| 60 |
- anonymous bool |
|
| 61 |
- generic map[string]interface{}
|
|
| 62 |
- joinLeaveDone chan struct{}
|
|
| 63 |
- prefAddress net.IP |
|
| 64 |
- prefAddressV6 net.IP |
|
| 65 |
- ipamOptions map[string]string |
|
| 66 |
- dbIndex uint64 |
|
| 67 |
- dbExists bool |
|
| 53 |
+ name string |
|
| 54 |
+ id string |
|
| 55 |
+ network *network |
|
| 56 |
+ iface *endpointInterface |
|
| 57 |
+ joinInfo *endpointJoinInfo |
|
| 58 |
+ sandboxID string |
|
| 59 |
+ exposedPorts []types.TransportPort |
|
| 60 |
+ anonymous bool |
|
| 61 |
+ disableResolution bool |
|
| 62 |
+ generic map[string]interface{}
|
|
| 63 |
+ joinLeaveDone chan struct{}
|
|
| 64 |
+ prefAddress net.IP |
|
| 65 |
+ prefAddressV6 net.IP |
|
| 66 |
+ ipamOptions map[string]string |
|
| 67 |
+ dbIndex uint64 |
|
| 68 |
+ dbExists bool |
|
| 68 | 69 |
sync.Mutex |
| 69 | 70 |
} |
| 70 | 71 |
|
| ... | ... |
@@ -82,6 +83,7 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
|
| 82 | 82 |
} |
| 83 | 83 |
epMap["sandbox"] = ep.sandboxID |
| 84 | 84 |
epMap["anonymous"] = ep.anonymous |
| 85 |
+ epMap["disableResolution"] = ep.disableResolution |
|
| 85 | 86 |
return json.Marshal(epMap) |
| 86 | 87 |
} |
| 87 | 88 |
|
| ... | ... |
@@ -159,6 +161,9 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
|
| 159 | 159 |
if v, ok := epMap["anonymous"]; ok {
|
| 160 | 160 |
ep.anonymous = v.(bool) |
| 161 | 161 |
} |
| 162 |
+ if v, ok := epMap["disableResolution"]; ok {
|
|
| 163 |
+ ep.disableResolution = v.(bool) |
|
| 164 |
+ } |
|
| 162 | 165 |
return nil |
| 163 | 166 |
} |
| 164 | 167 |
|
| ... | ... |
@@ -177,6 +182,7 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error {
|
| 177 | 177 |
dstEp.dbIndex = ep.dbIndex |
| 178 | 178 |
dstEp.dbExists = ep.dbExists |
| 179 | 179 |
dstEp.anonymous = ep.anonymous |
| 180 |
+ dstEp.disableResolution = ep.disableResolution |
|
| 180 | 181 |
|
| 181 | 182 |
if ep.iface != nil {
|
| 182 | 183 |
dstEp.iface = &endpointInterface{}
|
| ... | ... |
@@ -222,6 +228,12 @@ func (ep *endpoint) isAnonymous() bool {
|
| 222 | 222 |
return ep.anonymous |
| 223 | 223 |
} |
| 224 | 224 |
|
| 225 |
+func (ep *endpoint) needResolver() bool {
|
|
| 226 |
+ ep.Lock() |
|
| 227 |
+ defer ep.Unlock() |
|
| 228 |
+ return !ep.disableResolution |
|
| 229 |
+} |
|
| 230 |
+ |
|
| 225 | 231 |
// endpoint Key structure : endpoint/network-id/endpoint-id |
| 226 | 232 |
func (ep *endpoint) Key() []string {
|
| 227 | 233 |
if ep.network == nil {
|
| ... | ... |
@@ -396,10 +408,9 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
|
| 396 | 396 |
if ip := ep.getFirstInterfaceAddress(); ip != nil {
|
| 397 | 397 |
address = ip.String() |
| 398 | 398 |
} |
| 399 |
- if err = sb.updateHostsFile(address, network.getSvcRecords(ep)); err != nil {
|
|
| 399 |
+ if err = sb.updateHostsFile(address); err != nil {
|
|
| 400 | 400 |
return err |
| 401 | 401 |
} |
| 402 |
- |
|
| 403 | 402 |
if err = sb.updateDNS(network.enableIPv6); err != nil {
|
| 404 | 403 |
return err |
| 405 | 404 |
} |
| ... | ... |
@@ -729,6 +740,14 @@ func CreateOptionAnonymous() EndpointOption {
|
| 729 | 729 |
} |
| 730 | 730 |
} |
| 731 | 731 |
|
| 732 |
+// CreateOptionDisableResolution function returns an option setter to indicate |
|
| 733 |
+// this endpoint doesn't want embedded DNS server functionality |
|
| 734 |
+func CreateOptionDisableResolution() EndpointOption {
|
|
| 735 |
+ return func(ep *endpoint) {
|
|
| 736 |
+ ep.disableResolution = true |
|
| 737 |
+ } |
|
| 738 |
+} |
|
| 739 |
+ |
|
| 732 | 740 |
// JoinOptionPriority function returns an option setter for priority option to |
| 733 | 741 |
// be passed to the endpoint.Join() method. |
| 734 | 742 |
func JoinOptionPriority(ep Endpoint, prio int) EndpointOption {
|
| ... | ... |
@@ -1213,6 +1213,14 @@ func (f *fakeSandbox) SetKey(key string) error {
|
| 1213 | 1213 |
return nil |
| 1214 | 1214 |
} |
| 1215 | 1215 |
|
| 1216 |
+func (f *fakeSandbox) ResolveName(name string) net.IP {
|
|
| 1217 |
+ return nil |
|
| 1218 |
+} |
|
| 1219 |
+ |
|
| 1220 |
+func (f *fakeSandbox) ResolveIP(ip string) string {
|
|
| 1221 |
+ return "" |
|
| 1222 |
+} |
|
| 1223 |
+ |
|
| 1216 | 1224 |
func TestExternalKey(t *testing.T) {
|
| 1217 | 1225 |
externalKeyTest(t, false) |
| 1218 | 1226 |
} |
| ... | ... |
@@ -1698,6 +1706,7 @@ func TestEnableIPv6(t *testing.T) {
|
| 1698 | 1698 |
} |
| 1699 | 1699 |
|
| 1700 | 1700 |
tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n")
|
| 1701 |
+ expectedResolvConf := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\noptions ndots:0\n")
|
|
| 1701 | 1702 |
//take a copy of resolv.conf for restoring after test completes |
| 1702 | 1703 |
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
|
| 1703 | 1704 |
if err != nil {
|
| ... | ... |
@@ -1760,8 +1769,8 @@ func TestEnableIPv6(t *testing.T) {
|
| 1760 | 1760 |
t.Fatal(err) |
| 1761 | 1761 |
} |
| 1762 | 1762 |
|
| 1763 |
- if !bytes.Equal(content, tmpResolvConf) {
|
|
| 1764 |
- t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf), string(content))
|
|
| 1763 |
+ if !bytes.Equal(content, expectedResolvConf) {
|
|
| 1764 |
+ t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf), string(content))
|
|
| 1765 | 1765 |
} |
| 1766 | 1766 |
|
| 1767 | 1767 |
if err != nil {
|
| ... | ... |
@@ -1793,7 +1802,7 @@ func TestResolvConfHost(t *testing.T) {
|
| 1793 | 1793 |
t.Fatal(err) |
| 1794 | 1794 |
} |
| 1795 | 1795 |
|
| 1796 |
- ep1, err := n.CreateEndpoint("ep1", nil)
|
|
| 1796 |
+ ep1, err := n.CreateEndpoint("ep1", libnetwork.CreateOptionDisableResolution())
|
|
| 1797 | 1797 |
if err != nil {
|
| 1798 | 1798 |
t.Fatal(err) |
| 1799 | 1799 |
} |
| ... | ... |
@@ -1854,9 +1863,8 @@ func TestResolvConf(t *testing.T) {
|
| 1854 | 1854 |
} |
| 1855 | 1855 |
|
| 1856 | 1856 |
tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n")
|
| 1857 |
- expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n")
|
|
| 1858 | 1857 |
tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888\n")
|
| 1859 |
- expectedResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\n")
|
|
| 1858 |
+ expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\noptions ndots:0\n")
|
|
| 1860 | 1859 |
tmpResolvConf3 := []byte("search pommesfrites.fr\nnameserver 113.34.56.78\n")
|
| 1861 | 1860 |
|
| 1862 | 1861 |
//take a copy of resolv.conf for restoring after test completes |
| ... | ... |
@@ -1965,8 +1973,8 @@ func TestResolvConf(t *testing.T) {
|
| 1965 | 1965 |
t.Fatal(err) |
| 1966 | 1966 |
} |
| 1967 | 1967 |
|
| 1968 |
- if !bytes.Equal(content, expectedResolvConf2) {
|
|
| 1969 |
- t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf2), string(content))
|
|
| 1968 |
+ if !bytes.Equal(content, expectedResolvConf1) {
|
|
| 1969 |
+ t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf1), string(content))
|
|
| 1970 | 1970 |
} |
| 1971 | 1971 |
|
| 1972 | 1972 |
if err := ioutil.WriteFile(resolvConfPath, tmpResolvConf3, 0644); err != nil {
|
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"fmt" |
| 10 | 10 |
"io" |
| 11 | 11 |
"net" |
| 12 |
+ "strings" |
|
| 12 | 13 |
|
| 13 | 14 |
"github.com/docker/libnetwork/types" |
| 14 | 15 |
) |
| ... | ... |
@@ -132,3 +133,39 @@ func GenerateRandomName(prefix string, size int) (string, error) {
|
| 132 | 132 |
} |
| 133 | 133 |
return prefix + hex.EncodeToString(id)[:size], nil |
| 134 | 134 |
} |
| 135 |
+ |
|
| 136 |
+// ReverseIP accepts a V4 or V6 IP string in the canonical form and returns a reversed IP in |
|
| 137 |
+// the dotted decimal form . This is used to setup the IP to service name mapping in the optimal |
|
| 138 |
+// way for the DNS PTR queries. |
|
| 139 |
+func ReverseIP(IP string) string {
|
|
| 140 |
+ var reverseIP []string |
|
| 141 |
+ |
|
| 142 |
+ if net.ParseIP(IP).To4() != nil {
|
|
| 143 |
+ reverseIP = strings.Split(IP, ".") |
|
| 144 |
+ l := len(reverseIP) |
|
| 145 |
+ for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
|
|
| 146 |
+ reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i] |
|
| 147 |
+ } |
|
| 148 |
+ } else {
|
|
| 149 |
+ reverseIP = strings.Split(IP, ":") |
|
| 150 |
+ |
|
| 151 |
+ // Reversed IPv6 is represented in dotted decimal instead of the typical |
|
| 152 |
+ // colon hex notation |
|
| 153 |
+ for key := range reverseIP {
|
|
| 154 |
+ if len(reverseIP[key]) == 0 { // expand the compressed 0s
|
|
| 155 |
+ reverseIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":"))
|
|
| 156 |
+ } else if len(reverseIP[key]) < 4 { // 0-padding needed
|
|
| 157 |
+ reverseIP[key] = strings.Repeat("0", 4-len(reverseIP[key])) + reverseIP[key]
|
|
| 158 |
+ } |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ reverseIP = strings.Split(strings.Join(reverseIP, ""), "") |
|
| 162 |
+ |
|
| 163 |
+ l := len(reverseIP) |
|
| 164 |
+ for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
|
|
| 165 |
+ reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i] |
|
| 166 |
+ } |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ return strings.Join(reverseIP, ".") |
|
| 170 |
+} |
| ... | ... |
@@ -69,7 +69,10 @@ type NetworkInfo interface {
|
| 69 | 69 |
// When the function returns true, the walk will stop. |
| 70 | 70 |
type EndpointWalker func(ep Endpoint) bool |
| 71 | 71 |
|
| 72 |
-type svcMap map[string]net.IP |
|
| 72 |
+type svcInfo struct {
|
|
| 73 |
+ svcMap map[string]net.IP |
|
| 74 |
+ ipMap map[string]string |
|
| 75 |
+} |
|
| 73 | 76 |
|
| 74 | 77 |
// IpamConf contains all the ipam related configurations for a network |
| 75 | 78 |
type IpamConf struct {
|
| ... | ... |
@@ -159,7 +162,6 @@ type network struct {
|
| 159 | 159 |
epCnt *endpointCnt |
| 160 | 160 |
generic options.Generic |
| 161 | 161 |
dbIndex uint64 |
| 162 |
- svcRecords svcMap |
|
| 163 | 162 |
dbExists bool |
| 164 | 163 |
persist bool |
| 165 | 164 |
stopWatchCh chan struct{}
|
| ... | ... |
@@ -832,62 +834,33 @@ func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool |
| 832 | 832 |
c := n.getController() |
| 833 | 833 |
sr, ok := c.svcDb[n.ID()] |
| 834 | 834 |
if !ok {
|
| 835 |
- c.svcDb[n.ID()] = svcMap{}
|
|
| 835 |
+ c.svcDb[n.ID()] = svcInfo{
|
|
| 836 |
+ svcMap: make(map[string]net.IP), |
|
| 837 |
+ ipMap: make(map[string]string), |
|
| 838 |
+ } |
|
| 836 | 839 |
sr = c.svcDb[n.ID()] |
| 837 | 840 |
} |
| 838 | 841 |
|
| 842 |
+ epName := ep.Name() |
|
| 839 | 843 |
n.Lock() |
| 840 |
- var recs []etchosts.Record |
|
| 841 | 844 |
if iface := ep.Iface(); iface.Address() != nil {
|
| 845 |
+ |
|
| 846 |
+ reverseIP := netutils.ReverseIP(iface.Address().IP.String()) |
|
| 842 | 847 |
if isAdd {
|
| 843 | 848 |
// If we already have this endpoint in service db just return |
| 844 |
- if _, ok := sr[ep.Name()]; ok {
|
|
| 849 |
+ if _, ok := sr.svcMap[epName]; ok {
|
|
| 845 | 850 |
n.Unlock() |
| 846 | 851 |
return |
| 847 | 852 |
} |
| 848 | 853 |
|
| 849 |
- sr[ep.Name()] = iface.Address().IP |
|
| 850 |
- sr[ep.Name()+"."+n.name] = iface.Address().IP |
|
| 854 |
+ sr.svcMap[epName] = iface.Address().IP |
|
| 855 |
+ sr.ipMap[reverseIP] = epName |
|
| 851 | 856 |
} else {
|
| 852 |
- delete(sr, ep.Name()) |
|
| 853 |
- delete(sr, ep.Name()+"."+n.name) |
|
| 857 |
+ delete(sr.svcMap, epName) |
|
| 858 |
+ delete(sr.ipMap, reverseIP) |
|
| 854 | 859 |
} |
| 855 |
- |
|
| 856 |
- recs = append(recs, etchosts.Record{
|
|
| 857 |
- Hosts: ep.Name(), |
|
| 858 |
- IP: iface.Address().IP.String(), |
|
| 859 |
- }) |
|
| 860 |
- |
|
| 861 |
- recs = append(recs, etchosts.Record{
|
|
| 862 |
- Hosts: ep.Name() + "." + n.name, |
|
| 863 |
- IP: iface.Address().IP.String(), |
|
| 864 |
- }) |
|
| 865 | 860 |
} |
| 866 | 861 |
n.Unlock() |
| 867 |
- |
|
| 868 |
- // If there are no records to add or delete then simply return here |
|
| 869 |
- if len(recs) == 0 {
|
|
| 870 |
- return |
|
| 871 |
- } |
|
| 872 |
- |
|
| 873 |
- var sbList []*sandbox |
|
| 874 |
- for _, lEp := range localEps {
|
|
| 875 |
- if ep.ID() == lEp.ID() {
|
|
| 876 |
- continue |
|
| 877 |
- } |
|
| 878 |
- |
|
| 879 |
- if sb, hasSandbox := lEp.getSandbox(); hasSandbox {
|
|
| 880 |
- sbList = append(sbList, sb) |
|
| 881 |
- } |
|
| 882 |
- } |
|
| 883 |
- |
|
| 884 |
- for _, sb := range sbList {
|
|
| 885 |
- if isAdd {
|
|
| 886 |
- sb.addHostsEntries(recs) |
|
| 887 |
- } else {
|
|
| 888 |
- sb.deleteHostsEntries(recs) |
|
| 889 |
- } |
|
| 890 |
- } |
|
| 891 | 862 |
} |
| 892 | 863 |
|
| 893 | 864 |
func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
|
| ... | ... |
@@ -897,7 +870,7 @@ func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
|
| 897 | 897 |
var recs []etchosts.Record |
| 898 | 898 |
sr, _ := n.ctrlr.svcDb[n.id] |
| 899 | 899 |
|
| 900 |
- for h, ip := range sr {
|
|
| 900 |
+ for h, ip := range sr.svcMap {
|
|
| 901 | 901 |
if ep != nil && strings.Split(h, ".")[0] == ep.Name() {
|
| 902 | 902 |
continue |
| 903 | 903 |
} |
| 904 | 904 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,205 @@ |
| 0 |
+package libnetwork |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ log "github.com/Sirupsen/logrus" |
|
| 8 |
+ "github.com/docker/libnetwork/iptables" |
|
| 9 |
+ "github.com/miekg/dns" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// Resolver represents the embedded DNS server in Docker. It operates |
|
| 13 |
+// by listening on container's loopback interface for DNS queries. |
|
| 14 |
+type Resolver interface {
|
|
| 15 |
+ // Start starts the name server for the container |
|
| 16 |
+ Start() error |
|
| 17 |
+ // Stop stops the name server for the container |
|
| 18 |
+ Stop() |
|
| 19 |
+ // SetupFunc() provides the setup function that should be run |
|
| 20 |
+ // in the container's network namespace. |
|
| 21 |
+ SetupFunc() func() |
|
| 22 |
+ // NameServer() returns the IP of the DNS resolver for the |
|
| 23 |
+ // containers. |
|
| 24 |
+ NameServer() string |
|
| 25 |
+ // To configure external name servers the resolver should use |
|
| 26 |
+ SetExtServers([]string) |
|
| 27 |
+ // ResolverOptions returns resolv.conf options that should be set |
|
| 28 |
+ ResolverOptions() []string |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+const ( |
|
| 32 |
+ resolverIP = "127.0.0.11" |
|
| 33 |
+ dnsPort = "53" |
|
| 34 |
+ ptrIPv4domain = ".in-addr.arpa." |
|
| 35 |
+ ptrIPv6domain = ".ip6.arpa." |
|
| 36 |
+ respTTL = 1800 |
|
| 37 |
+) |
|
| 38 |
+ |
|
| 39 |
+// resolver implements the Resolver interface |
|
| 40 |
+type resolver struct {
|
|
| 41 |
+ sb *sandbox |
|
| 42 |
+ extDNS []string |
|
| 43 |
+ server *dns.Server |
|
| 44 |
+ conn *net.UDPConn |
|
| 45 |
+ err error |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// NewResolver creates a new instance of the Resolver |
|
| 49 |
+func NewResolver(sb *sandbox) Resolver {
|
|
| 50 |
+ return &resolver{
|
|
| 51 |
+ sb: sb, |
|
| 52 |
+ err: fmt.Errorf("setup not done yet"),
|
|
| 53 |
+ } |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+func (r *resolver) SetupFunc() func() {
|
|
| 57 |
+ return (func() {
|
|
| 58 |
+ var err error |
|
| 59 |
+ |
|
| 60 |
+ addr := &net.UDPAddr{
|
|
| 61 |
+ IP: net.ParseIP(resolverIP), |
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ r.conn, err = net.ListenUDP("udp", addr)
|
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ r.err = fmt.Errorf("error in opening name server socket %v", err)
|
|
| 67 |
+ return |
|
| 68 |
+ } |
|
| 69 |
+ laddr := r.conn.LocalAddr() |
|
| 70 |
+ _, ipPort, _ := net.SplitHostPort(laddr.String()) |
|
| 71 |
+ |
|
| 72 |
+ rules := [][]string{
|
|
| 73 |
+ {"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
|
|
| 74 |
+ {"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ for _, rule := range rules {
|
|
| 78 |
+ r.err = iptables.RawCombinedOutput(rule...) |
|
| 79 |
+ if r.err != nil {
|
|
| 80 |
+ return |
|
| 81 |
+ } |
|
| 82 |
+ } |
|
| 83 |
+ r.err = nil |
|
| 84 |
+ }) |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func (r *resolver) Start() error {
|
|
| 88 |
+ // make sure the resolver has been setup before starting |
|
| 89 |
+ if r.err != nil {
|
|
| 90 |
+ return r.err |
|
| 91 |
+ } |
|
| 92 |
+ s := &dns.Server{Handler: r, PacketConn: r.conn}
|
|
| 93 |
+ r.server = s |
|
| 94 |
+ go func() {
|
|
| 95 |
+ s.ActivateAndServe() |
|
| 96 |
+ }() |
|
| 97 |
+ return nil |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (r *resolver) Stop() {
|
|
| 101 |
+ if r.server != nil {
|
|
| 102 |
+ r.server.Shutdown() |
|
| 103 |
+ } |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+func (r *resolver) SetExtServers(dns []string) {
|
|
| 107 |
+ r.extDNS = dns |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func (r *resolver) NameServer() string {
|
|
| 111 |
+ return resolverIP |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func (r *resolver) ResolverOptions() []string {
|
|
| 115 |
+ return []string{"ndots:0"}
|
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
|
|
| 119 |
+ addr := r.sb.ResolveName(name) |
|
| 120 |
+ if addr == nil {
|
|
| 121 |
+ return nil, nil |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ log.Debugf("Lookup for %s: IP %s", name, addr.String())
|
|
| 125 |
+ |
|
| 126 |
+ resp := new(dns.Msg) |
|
| 127 |
+ resp.SetReply(query) |
|
| 128 |
+ |
|
| 129 |
+ rr := new(dns.A) |
|
| 130 |
+ rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
|
|
| 131 |
+ rr.A = addr |
|
| 132 |
+ resp.Answer = append(resp.Answer, rr) |
|
| 133 |
+ return resp, nil |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
|
|
| 137 |
+ parts := []string{}
|
|
| 138 |
+ |
|
| 139 |
+ if strings.HasSuffix(ptr, ptrIPv4domain) {
|
|
| 140 |
+ parts = strings.Split(ptr, ptrIPv4domain) |
|
| 141 |
+ } else if strings.HasSuffix(ptr, ptrIPv6domain) {
|
|
| 142 |
+ parts = strings.Split(ptr, ptrIPv6domain) |
|
| 143 |
+ } else {
|
|
| 144 |
+ return nil, fmt.Errorf("invalid PTR query, %v", ptr)
|
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 147 |
+ host := r.sb.ResolveIP(parts[0]) |
|
| 148 |
+ if len(host) == 0 {
|
|
| 149 |
+ return nil, nil |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ log.Debugf("Lookup for IP %s: name %s", parts[0], host)
|
|
| 153 |
+ fqdn := dns.Fqdn(host) |
|
| 154 |
+ |
|
| 155 |
+ resp := new(dns.Msg) |
|
| 156 |
+ resp.SetReply(query) |
|
| 157 |
+ |
|
| 158 |
+ rr := new(dns.PTR) |
|
| 159 |
+ rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
|
|
| 160 |
+ rr.Ptr = fqdn |
|
| 161 |
+ resp.Answer = append(resp.Answer, rr) |
|
| 162 |
+ return resp, nil |
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|
| 166 |
+ var ( |
|
| 167 |
+ resp *dns.Msg |
|
| 168 |
+ err error |
|
| 169 |
+ ) |
|
| 170 |
+ |
|
| 171 |
+ name := query.Question[0].Name |
|
| 172 |
+ if query.Question[0].Qtype == dns.TypeA {
|
|
| 173 |
+ resp, err = r.handleIPv4Query(name, query) |
|
| 174 |
+ } else if query.Question[0].Qtype == dns.TypePTR {
|
|
| 175 |
+ resp, err = r.handlePTRQuery(name, query) |
|
| 176 |
+ } |
|
| 177 |
+ |
|
| 178 |
+ if err != nil {
|
|
| 179 |
+ log.Error(err) |
|
| 180 |
+ return |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 183 |
+ if resp == nil {
|
|
| 184 |
+ if len(r.extDNS) == 0 {
|
|
| 185 |
+ return |
|
| 186 |
+ } |
|
| 187 |
+ log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[0], name, query.Question[0].Qtype)
|
|
| 188 |
+ |
|
| 189 |
+ c := &dns.Client{Net: "udp"}
|
|
| 190 |
+ addr := fmt.Sprintf("%s:%d", r.extDNS[0], 53)
|
|
| 191 |
+ |
|
| 192 |
+ // TODO: iterate over avilable servers in case of error |
|
| 193 |
+ resp, _, err = c.Exchange(query, addr) |
|
| 194 |
+ if err != nil {
|
|
| 195 |
+ log.Errorf("external resolution failed, %s", err)
|
|
| 196 |
+ return |
|
| 197 |
+ } |
|
| 198 |
+ } |
|
| 199 |
+ |
|
| 200 |
+ err = w.WriteMsg(resp) |
|
| 201 |
+ if err != nil {
|
|
| 202 |
+ log.Errorf("error writing resolver resp, %s", err)
|
|
| 203 |
+ } |
|
| 204 |
+} |
| ... | ... |
@@ -5,9 +5,11 @@ import ( |
| 5 | 5 |
"encoding/json" |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"io/ioutil" |
| 8 |
+ "net" |
|
| 8 | 9 |
"os" |
| 9 | 10 |
"path" |
| 10 | 11 |
"path/filepath" |
| 12 |
+ "strings" |
|
| 11 | 13 |
"sync" |
| 12 | 14 |
|
| 13 | 15 |
log "github.com/Sirupsen/logrus" |
| ... | ... |
@@ -38,6 +40,12 @@ type Sandbox interface {
|
| 38 | 38 |
Rename(name string) error |
| 39 | 39 |
// Delete destroys this container after detaching it from all connected endpoints. |
| 40 | 40 |
Delete() error |
| 41 |
+ // ResolveName searches for the service name in the networks to which the sandbox |
|
| 42 |
+ // is connected to. |
|
| 43 |
+ ResolveName(name string) net.IP |
|
| 44 |
+ // ResolveIP returns the service name for the passed in IP. IP is in reverse dotted |
|
| 45 |
+ // notation; the format used for DNS PTR records |
|
| 46 |
+ ResolveIP(name string) string |
|
| 41 | 47 |
} |
| 42 | 48 |
|
| 43 | 49 |
// SandboxOption is a option setter function type used to pass varios options to |
| ... | ... |
@@ -59,8 +67,11 @@ type sandbox struct {
|
| 59 | 59 |
id string |
| 60 | 60 |
containerID string |
| 61 | 61 |
config containerConfig |
| 62 |
+ extDNS []string |
|
| 62 | 63 |
osSbox osl.Sandbox |
| 63 | 64 |
controller *controller |
| 65 |
+ resolver Resolver |
|
| 66 |
+ resolverOnce sync.Once |
|
| 64 | 67 |
refCnt int |
| 65 | 68 |
endpoints epHeap |
| 66 | 69 |
epPriority map[string]int |
| ... | ... |
@@ -202,6 +213,10 @@ func (sb *sandbox) Delete() error {
|
| 202 | 202 |
// likely not required any more. Drop it. |
| 203 | 203 |
etchosts.Drop(sb.config.hostsPath) |
| 204 | 204 |
|
| 205 |
+ if sb.resolver != nil {
|
|
| 206 |
+ sb.resolver.Stop() |
|
| 207 |
+ } |
|
| 208 |
+ |
|
| 205 | 209 |
if sb.osSbox != nil && !sb.config.useDefaultSandBox {
|
| 206 | 210 |
sb.osSbox.Destroy() |
| 207 | 211 |
} |
| ... | ... |
@@ -291,6 +306,26 @@ func (sb *sandbox) UnmarshalJSON(b []byte) (err error) {
|
| 291 | 291 |
return nil |
| 292 | 292 |
} |
| 293 | 293 |
|
| 294 |
+func (sb *sandbox) startResolver() {
|
|
| 295 |
+ sb.resolverOnce.Do(func() {
|
|
| 296 |
+ var err error |
|
| 297 |
+ sb.resolver = NewResolver(sb) |
|
| 298 |
+ defer func() {
|
|
| 299 |
+ if err != nil {
|
|
| 300 |
+ sb.resolver = nil |
|
| 301 |
+ } |
|
| 302 |
+ }() |
|
| 303 |
+ |
|
| 304 |
+ sb.rebuildDNS() |
|
| 305 |
+ sb.resolver.SetExtServers(sb.extDNS) |
|
| 306 |
+ |
|
| 307 |
+ sb.osSbox.InvokeFunc(sb.resolver.SetupFunc()) |
|
| 308 |
+ if err := sb.resolver.Start(); err != nil {
|
|
| 309 |
+ log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
|
|
| 310 |
+ } |
|
| 311 |
+ }) |
|
| 312 |
+} |
|
| 313 |
+ |
|
| 294 | 314 |
func (sb *sandbox) setupResolutionFiles() error {
|
| 295 | 315 |
if err := sb.buildHostsFile(); err != nil {
|
| 296 | 316 |
return err |
| ... | ... |
@@ -361,6 +396,56 @@ func (sb *sandbox) updateGateway(ep *endpoint) error {
|
| 361 | 361 |
return nil |
| 362 | 362 |
} |
| 363 | 363 |
|
| 364 |
+func (sb *sandbox) ResolveIP(ip string) string {
|
|
| 365 |
+ var svc string |
|
| 366 |
+ log.Debugf("IP To resolve %v", ip)
|
|
| 367 |
+ |
|
| 368 |
+ for _, ep := range sb.getConnectedEndpoints() {
|
|
| 369 |
+ n := ep.getNetwork() |
|
| 370 |
+ |
|
| 371 |
+ sr, ok := n.getController().svcDb[n.ID()] |
|
| 372 |
+ if !ok {
|
|
| 373 |
+ continue |
|
| 374 |
+ } |
|
| 375 |
+ |
|
| 376 |
+ nwName := n.Name() |
|
| 377 |
+ n.Lock() |
|
| 378 |
+ svc, ok = sr.ipMap[ip] |
|
| 379 |
+ n.Unlock() |
|
| 380 |
+ if ok {
|
|
| 381 |
+ return svc + "." + nwName |
|
| 382 |
+ } |
|
| 383 |
+ } |
|
| 384 |
+ return svc |
|
| 385 |
+} |
|
| 386 |
+ |
|
| 387 |
+func (sb *sandbox) ResolveName(name string) net.IP {
|
|
| 388 |
+ var ip net.IP |
|
| 389 |
+ parts := strings.Split(name, ".") |
|
| 390 |
+ log.Debugf("To resolve %v", parts)
|
|
| 391 |
+ |
|
| 392 |
+ for _, ep := range sb.getConnectedEndpoints() {
|
|
| 393 |
+ n := ep.getNetwork() |
|
| 394 |
+ |
|
| 395 |
+ if len(parts) > 1 && parts[1] != "" && parts[1] != n.Name() {
|
|
| 396 |
+ continue |
|
| 397 |
+ } |
|
| 398 |
+ |
|
| 399 |
+ sr, ok := n.getController().svcDb[n.ID()] |
|
| 400 |
+ if !ok {
|
|
| 401 |
+ continue |
|
| 402 |
+ } |
|
| 403 |
+ |
|
| 404 |
+ n.Lock() |
|
| 405 |
+ ip, ok = sr.svcMap[parts[0]] |
|
| 406 |
+ n.Unlock() |
|
| 407 |
+ if ok {
|
|
| 408 |
+ return ip |
|
| 409 |
+ } |
|
| 410 |
+ } |
|
| 411 |
+ return ip |
|
| 412 |
+} |
|
| 413 |
+ |
|
| 364 | 414 |
func (sb *sandbox) SetKey(basePath string) error {
|
| 365 | 415 |
var err error |
| 366 | 416 |
if basePath == "" {
|
| ... | ... |
@@ -459,6 +544,10 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
| 459 | 459 |
i := ep.iface |
| 460 | 460 |
ep.Unlock() |
| 461 | 461 |
|
| 462 |
+ if ep.needResolver() {
|
|
| 463 |
+ sb.startResolver() |
|
| 464 |
+ } |
|
| 465 |
+ |
|
| 462 | 466 |
if i != nil && i.srcName != "" {
|
| 463 | 467 |
var ifaceOptions []osl.IfaceOption |
| 464 | 468 |
|
| ... | ... |
@@ -599,7 +688,7 @@ func (sb *sandbox) buildHostsFile() error {
|
| 599 | 599 |
return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent) |
| 600 | 600 |
} |
| 601 | 601 |
|
| 602 |
-func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record) error {
|
|
| 602 |
+func (sb *sandbox) updateHostsFile(ifaceIP string) error {
|
|
| 603 | 603 |
var mhost string |
| 604 | 604 |
|
| 605 | 605 |
if sb.config.originHostsPath != "" {
|
| ... | ... |
@@ -613,11 +702,7 @@ func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record) |
| 613 | 613 |
mhost = sb.config.hostName |
| 614 | 614 |
} |
| 615 | 615 |
|
| 616 |
- extraContent := make([]etchosts.Record, 0, len(svcRecords)+1) |
|
| 617 |
- extraContent = append(extraContent, etchosts.Record{Hosts: mhost, IP: ifaceIP})
|
|
| 618 |
- for _, svc := range svcRecords {
|
|
| 619 |
- extraContent = append(extraContent, svc) |
|
| 620 |
- } |
|
| 616 |
+ extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
|
|
| 621 | 617 |
|
| 622 | 618 |
sb.addHostsEntries(extraContent) |
| 623 | 619 |
return nil |
| ... | ... |
@@ -787,6 +872,48 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
|
| 787 | 787 |
return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath) |
| 788 | 788 |
} |
| 789 | 789 |
|
| 790 |
+// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's |
|
| 791 |
+// resolv.conf by doing the follwing |
|
| 792 |
+// - Save the external name servers in resolv.conf in the sandbox |
|
| 793 |
+// - Add only the embedded server's IP to container's resolv.conf |
|
| 794 |
+// - If the embedded server needs any resolv.conf options add it to the current list |
|
| 795 |
+func (sb *sandbox) rebuildDNS() error {
|
|
| 796 |
+ currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath) |
|
| 797 |
+ if err != nil {
|
|
| 798 |
+ return err |
|
| 799 |
+ } |
|
| 800 |
+ |
|
| 801 |
+ // localhost entries have already been filtered out from the list |
|
| 802 |
+ sb.extDNS = resolvconf.GetNameservers(currRC.Content) |
|
| 803 |
+ |
|
| 804 |
+ var ( |
|
| 805 |
+ dnsList = []string{sb.resolver.NameServer()}
|
|
| 806 |
+ dnsOptionsList = resolvconf.GetOptions(currRC.Content) |
|
| 807 |
+ dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) |
|
| 808 |
+ ) |
|
| 809 |
+ |
|
| 810 |
+ // Resolver returns the options in the format resolv.conf expects |
|
| 811 |
+ dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...) |
|
| 812 |
+ |
|
| 813 |
+ dir := path.Dir(sb.config.resolvConfPath) |
|
| 814 |
+ tmpResolvFile, err := ioutil.TempFile(dir, "resolv") |
|
| 815 |
+ if err != nil {
|
|
| 816 |
+ return err |
|
| 817 |
+ } |
|
| 818 |
+ |
|
| 819 |
+ // Change the perms to filePerm (0644) since ioutil.TempFile creates it by default as 0600 |
|
| 820 |
+ if err := os.Chmod(tmpResolvFile.Name(), filePerm); err != nil {
|
|
| 821 |
+ return err |
|
| 822 |
+ } |
|
| 823 |
+ |
|
| 824 |
+ _, err = resolvconf.Build(tmpResolvFile.Name(), dnsList, dnsSearchList, dnsOptionsList) |
|
| 825 |
+ if err != nil {
|
|
| 826 |
+ return err |
|
| 827 |
+ } |
|
| 828 |
+ |
|
| 829 |
+ return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath) |
|
| 830 |
+} |
|
| 831 |
+ |
|
| 790 | 832 |
// joinLeaveStart waits to ensure there are no joins or leaves in progress and |
| 791 | 833 |
// marks this join/leave in progress without race |
| 792 | 834 |
func (sb *sandbox) joinLeaveStart() {
|
| ... | ... |
@@ -179,6 +179,23 @@ function test_single_network_connectivity() {
|
| 179 | 179 |
done |
| 180 | 180 |
done |
| 181 | 181 |
|
| 182 |
+ svcs=( |
|
| 183 |
+ 0,0 |
|
| 184 |
+ 2,3 |
|
| 185 |
+ 1,3 |
|
| 186 |
+ 1,2 |
|
| 187 |
+ ) |
|
| 188 |
+ |
|
| 189 |
+ echo "Test connectivity failure" |
|
| 190 |
+ for i in `seq ${start} ${end}`;
|
|
| 191 |
+ do |
|
| 192 |
+ IFS=, read a b <<<"${svcs[$i]}"
|
|
| 193 |
+ osvc="svc${a}${b}"
|
|
| 194 |
+ echo "pinging ${osvc}"
|
|
| 195 |
+ runc_nofail $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) "ping -c 1 ${osvc}"
|
|
| 196 |
+ [ "${status}" -ne 0 ]
|
|
| 197 |
+ done |
|
| 198 |
+ |
|
| 182 | 199 |
for i in `seq ${start} ${end}`;
|
| 183 | 200 |
do |
| 184 | 201 |
for j in `seq ${start} ${end}`;
|
| ... | ... |
@@ -351,84 +351,6 @@ function check_etchosts() {
|
| 351 | 351 |
echo ${retval}
|
| 352 | 352 |
} |
| 353 | 353 |
|
| 354 |
-function test_overlay_etchosts() {
|
|
| 355 |
- local clist dnet_suffix |
|
| 356 |
- |
|
| 357 |
- dnet_suffix=$1 |
|
| 358 |
- shift |
|
| 359 |
- |
|
| 360 |
- echo $(docker ps) |
|
| 361 |
- |
|
| 362 |
- start=1 |
|
| 363 |
- end=3 |
|
| 364 |
- # Setup overlay network and connect containers ot it |
|
| 365 |
- dnet_cmd $(inst_id2port 1) network create -d overlay multihost |
|
| 366 |
- |
|
| 367 |
- for iter in `seq 1 2`; |
|
| 368 |
- do |
|
| 369 |
- for i in `seq ${start} ${end}`;
|
|
| 370 |
- do |
|
| 371 |
- dnet_cmd $(inst_id2port $i) container create container_${iter}_${i}
|
|
| 372 |
- net_connect ${i} container_${iter}_${i} multihost
|
|
| 373 |
- done |
|
| 374 |
- |
|
| 375 |
- # Now test the /etc/hosts content of all the containers |
|
| 376 |
- for i in `seq ${start} ${end}`;
|
|
| 377 |
- do |
|
| 378 |
- clist="" |
|
| 379 |
- oldclist="" |
|
| 380 |
- for j in `seq ${start} ${end}`;
|
|
| 381 |
- do |
|
| 382 |
- if [ "$i" -eq "$j" ]; then |
|
| 383 |
- continue |
|
| 384 |
- fi |
|
| 385 |
- clist="$clist container_${iter}_$j"
|
|
| 386 |
- oldclist="$oldclist container_1_$j" |
|
| 387 |
- done |
|
| 388 |
- rv=$(check_etchosts $(dnet_container_name $i $dnet_suffix) \ |
|
| 389 |
- $(get_sbox_id ${i} container_${iter}_${i}) \
|
|
| 390 |
- ${clist})
|
|
| 391 |
- [ "$rv" = "true" ] |
|
| 392 |
- |
|
| 393 |
- # check to see the containers don't have stale entries from previous iteration |
|
| 394 |
- if [ "$iter" -eq 2 ]; then |
|
| 395 |
- rv=$(check_etchosts $(dnet_container_name $i $dnet_suffix) \ |
|
| 396 |
- $(get_sbox_id ${i} container_${iter}_${i}) \
|
|
| 397 |
- ${oldclist})
|
|
| 398 |
- [ "$rv" = "false" ] |
|
| 399 |
- fi |
|
| 400 |
- done |
|
| 401 |
- |
|
| 402 |
- # Teardown the container connections and the network |
|
| 403 |
- clist="" |
|
| 404 |
- for i in `seq ${start} ${end}`;
|
|
| 405 |
- do |
|
| 406 |
- net_disconnect ${i} container_${iter}_${i} multihost
|
|
| 407 |
- dnet_cmd $(inst_id2port $i) container rm container_${iter}_${i}
|
|
| 408 |
- |
|
| 409 |
- #check if the /etc/hosts of other containers does not contain this container |
|
| 410 |
- for j in `seq ${start} ${end}`;
|
|
| 411 |
- do |
|
| 412 |
- if [ "$i" -eq "$j" ]; then |
|
| 413 |
- continue |
|
| 414 |
- fi |
|
| 415 |
- |
|
| 416 |
- if [[ "${clist}" =~ .*container_${iter}_${j}.* ]]; then
|
|
| 417 |
- continue |
|
| 418 |
- fi |
|
| 419 |
- |
|
| 420 |
- rv=$(check_etchosts $(dnet_container_name $j $dnet_suffix) \ |
|
| 421 |
- $(get_sbox_id ${j} container_${iter}_${j}) \
|
|
| 422 |
- container_${iter}_${i})
|
|
| 423 |
- [ "$rv" = "false" ] |
|
| 424 |
- done |
|
| 425 |
- clist="${clist} container_${iter}_${i}"
|
|
| 426 |
- done |
|
| 427 |
- done |
|
| 428 |
- |
|
| 429 |
- dnet_cmd $(inst_id2port 2) network rm multihost |
|
| 430 |
-} |
|
| 431 |
- |
|
| 432 | 354 |
function test_overlay_singlehost() {
|
| 433 | 355 |
dnet_suffix=$1 |
| 434 | 356 |
shift |
| ... | ... |
@@ -13,11 +13,6 @@ load helpers |
| 13 | 13 |
test_overlay_singlehost consul |
| 14 | 14 |
} |
| 15 | 15 |
|
| 16 |
-@test "test overlay network etc hosts with consul" {
|
|
| 17 |
- skip_for_circleci |
|
| 18 |
- test_overlay_etchosts consul |
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 | 16 |
@test "Test overlay network with dnet restart" {
|
| 22 | 17 |
skip_for_circleci |
| 23 | 18 |
test_overlay consul skip_rm |
| ... | ... |
@@ -33,4 +28,4 @@ load helpers |
| 33 | 33 |
@test "Test overlay network internal network with consul" {
|
| 34 | 34 |
skip_for_circleci |
| 35 | 35 |
test_overlay consul internal |
| 36 |
-} |
|
| 37 | 36 |
\ No newline at end of file |
| 37 |
+} |