From 6bff651e87a6182fd2533e108ad5fd41add12e29 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 11:55:45 +0100 Subject: [PATCH 01/12] Changed to new URL scheme with container identifier Implementation matches design.md better: - http action changes from POST /add to PUT /name - each record now has the container identifier --- server/http.go | 44 +++++++++++++++++++++++++++----------- server/http_test.go | 13 +++++++++-- server/mdns_server_test.go | 5 +++-- server/zone.go | 7 +++--- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/server/http.go b/server/http.go index e0eb40e9..35697c5c 100644 --- a/server/http.go +++ b/server/http.go @@ -1,30 +1,48 @@ package weavedns import ( + "errors" "fmt" "log" "net" "net/http" + "strings" ) +// Parse a URL of the form PUT /name// +func parseUrl(url string) (identifier string, ipaddr string, err error) { + parts := strings.Split(url, "/") + if len(parts) != 4 { + return "", "", errors.New("Unable to parse url: " + url) + } + return parts[2], parts[3], nil +} + func ListenHttp(db Zone, port int) { - http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { - name := r.FormValue("name") - addr := r.FormValue("ip") - weave_cidr := r.FormValue("weave_cidr") - if name != "" && addr != "" && weave_cidr != "" { - ip := net.ParseIP(addr) + http.HandleFunc("/name/", func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "PUT": + identifier, weave_ipstr, err := parseUrl(r.URL.Path) + name := r.FormValue("fqdn") + prefix := r.FormValue("routing_prefix") + local_ip := r.FormValue("local_ip") + if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { + log.Printf("Invalid request: %s, %s", r.URL, r.Form) + return + } + ip := net.ParseIP(local_ip) + if ip == nil { + log.Printf("Invalid IP in request: %s", local_ip) + return + } + weave_cidr := weave_ipstr + "/" + prefix weave_ip, subnet, err := net.ParseCIDR(weave_cidr) - if err == nil && ip != nil { - log.Printf("Adding %s (%s) -> %s", name, addr, weave_cidr) - db.AddRecord(name, ip, weave_ip, subnet) + if err == nil { + log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) + db.AddRecord(identifier, name, ip, weave_ip, subnet) } else if err != nil { log.Printf("Invalid CIDR in request: %s", weave_cidr) - } else { - log.Printf("Invalid IP in request: %s", addr) } - } else { - log.Printf("Invalid request: %s, %s, %s", name, addr, weave_cidr) } }) address := fmt.Sprintf(":%d", port) diff --git a/server/http_test.go b/server/http_test.go index 7a1cfe78..328bafe2 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -6,12 +6,20 @@ import ( "net" "net/http" "net/url" + "strings" "testing" "time" ) +func putForm(url string, data url.Values) (resp *http.Response, err error) { + req, err := http.NewRequest("PUT", url, strings.NewReader(data.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + return http.DefaultClient.Do(req) +} + func TestHttp(t *testing.T) { var ( + container_id = "deadbeef" success_test_name = "test1.weave." test_addr1 = "10.0.2.1/24" docker_ip = "9.8.7.6" @@ -24,8 +32,9 @@ func TestHttp(t *testing.T) { time.Sleep(100 * time.Millisecond) // Allow for http server to get going - resp, err := http.PostForm(fmt.Sprintf("http://localhost:%d/add", port), - url.Values{"name": {success_test_name}, "ip": {docker_ip}, "weave_cidr": {test_addr1}}) + addr_parts := strings.Split(test_addr1, "/") + resp, err := putForm(fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), + url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) } diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index 68e2f387..aafe821e 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -9,7 +9,8 @@ import ( ) var ( - test_addr1 = "10.0.2.1/24" + container_id = "deadbeef" + test_addr1 = "10.0.2.1/24" ) func sendQuery(name string, querytype uint16) error { @@ -33,7 +34,7 @@ func TestServerSimpleQuery(t *testing.T) { var zone = new(ZoneDb) docker_ip := net.ParseIP("9.8.7.6") weave_ip, subnet, _ := net.ParseCIDR(test_addr1) - zone.AddRecord("test.weave.", docker_ip, weave_ip, subnet) + zone.AddRecord(container_id, "test.weave.", docker_ip, weave_ip, subnet) mdnsServer, err := NewMDNSServer(zone) if err != nil { diff --git a/server/zone.go b/server/zone.go index 824dd8d8..6b8a8d1f 100644 --- a/server/zone.go +++ b/server/zone.go @@ -8,11 +8,12 @@ import ( ) type Zone interface { - AddRecord(string, net.IP, net.IP, *net.IPNet) error + AddRecord(string, string, net.IP, net.IP, *net.IPNet) error MatchLocal(string) (net.IP, error) } type Record struct { + Ident string Name string Ip net.IP WeaveIp net.IP @@ -47,9 +48,9 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { return zone.match(name) } -func (zone *ZoneDb) AddRecord(name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { +func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() - zone.recs = append(zone.recs, Record{dns.Fqdn(name), ip, weave_ip, weave_subnet}) + zone.recs = append(zone.recs, Record{identifier, dns.Fqdn(name), ip, weave_ip, weave_subnet}) return nil } From 36fa3cca7343ca4b903ef16a9e5e7c935c4212ff Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 14:12:42 +0100 Subject: [PATCH 02/12] Tidy up --- server/http_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index 328bafe2..f54a2d2d 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -32,6 +32,7 @@ func TestHttp(t *testing.T) { time.Sleep(100 * time.Millisecond) // Allow for http server to get going + // Ask the http server to add our test address into the database addr_parts := strings.Split(test_addr1, "/") resp, err := putForm(fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) @@ -39,19 +40,17 @@ func TestHttp(t *testing.T) { t.Fatal(err) } if resp.StatusCode != 200 { - t.Log("Unexpected http response", resp.Status) - t.Fail() + t.Fatal("Unexpected http response", resp.Status) } + // Check that the address is now there. ip, err := zone.MatchLocal(success_test_name) if err != nil { - t.Log(zone) t.Fatal(err) } weave_ip, _, _ := net.ParseCIDR(test_addr1) if !ip.Equal(weave_ip) { - t.Log("Unexpected result for", success_test_name, ip) - t.Fail() + t.Fatal("Unexpected result for", success_test_name, ip) } // Would like to shut down the http server at the end of this test From 7f6e0a1e428eaea3fe5dbecdf391a22c2715b1ae Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 15:19:20 +0100 Subject: [PATCH 03/12] Make error message more helpful --- server/zone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/zone.go b/server/zone.go index 6b8a8d1f..d80fcfbd 100644 --- a/server/zone.go +++ b/server/zone.go @@ -28,7 +28,7 @@ type ZoneDb struct { type LookupError string func (ops LookupError) Error() string { - return string(ops) + return "Unable to find " + string(ops) } // Stop gap. From 9bd464a00393e718f439cf6ef3faa61d741e44b1 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 15:23:20 +0100 Subject: [PATCH 04/12] Make putForm more generic --- server/http_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index f54a2d2d..ac81f70c 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -11,8 +11,8 @@ import ( "time" ) -func putForm(url string, data url.Values) (resp *http.Response, err error) { - req, err := http.NewRequest("PUT", url, strings.NewReader(data.Encode())) +func genForm(method string, url string, data url.Values) (resp *http.Response, err error) { + req, err := http.NewRequest(method, url, strings.NewReader(data.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") return http.DefaultClient.Do(req) } @@ -34,7 +34,7 @@ func TestHttp(t *testing.T) { // Ask the http server to add our test address into the database addr_parts := strings.Split(test_addr1, "/") - resp, err := putForm(fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), + resp, err := genForm("PUT", fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) From 8ebc06315c4609a4c3e9264c8dda4dd45676ede9 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:06:08 +0100 Subject: [PATCH 05/12] Detect duplicate add --- server/zone.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/server/zone.go b/server/zone.go index d80fcfbd..6957b040 100644 --- a/server/zone.go +++ b/server/zone.go @@ -31,6 +31,15 @@ func (ops LookupError) Error() string { return "Unable to find " + string(ops) } +type DuplicateError struct { + Name string + WeaveIp net.IP +} + +func (err DuplicateError) Error() string { + return "Duplicate " + err.Name + "," + err.WeaveIp.String() +} + // Stop gap. func (zone *ZoneDb) match(name string) (net.IP, error) { for _, r := range zone.recs { @@ -42,6 +51,15 @@ func (zone *ZoneDb) match(name string) (net.IP, error) { return nil, LookupError(name) } +func (zone *ZoneDb) indexOfNameAddr(name string, addr net.IP) int { + for i, r := range zone.recs { + if r.Name == name && r.WeaveIp.Equal(addr) { + return i + } + } + return -1 +} + func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { zone.mx.Lock() defer zone.mx.Unlock() @@ -51,6 +69,10 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() - zone.recs = append(zone.recs, Record{identifier, dns.Fqdn(name), ip, weave_ip, weave_subnet}) + fqdn := dns.Fqdn(name) + if zone.indexOfNameAddr(fqdn, weave_ip) != -1 { + return DuplicateError{fqdn, weave_ip} + } + zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) return nil } From bc87c7c94b91a6d4718f74fb2be62b65670439c9 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:06:25 +0100 Subject: [PATCH 06/12] Initial Zone tests --- server/zone_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 server/zone_test.go diff --git a/server/zone_test.go b/server/zone_test.go new file mode 100644 index 00000000..6f392c9d --- /dev/null +++ b/server/zone_test.go @@ -0,0 +1,39 @@ +package weavedns + +import ( + "net" + "testing" +) + +func TestZone(t *testing.T) { + var ( + container_id = "deadbeef" + success_test_name = "test1.weave." + test_addr1 = "10.0.2.1/24" + docker_ip = "9.8.7.6" + ) + + var zone = new(ZoneDb) + + ip := net.ParseIP(docker_ip) + weave_ip, subnet, _ := net.ParseCIDR(test_addr1) + err := zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) + if err != nil { + t.Fatal(err) + } + + // Check that the address is now there. + found_ip, err := zone.MatchLocal(success_test_name) + if err != nil { + t.Fatal(err) + } + if !found_ip.Equal(weave_ip) { + t.Fatal("Unexpected result for", success_test_name, ip) + } + + // Now try to add the same address again + err = zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) + if _, ok := err.(DuplicateError); !ok { + t.Fatal(err) + } +} From 10772f828902987e4a52cd68c29f1e450db1252c Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:12:50 +0100 Subject: [PATCH 07/12] Better error-checking --- server/http.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/server/http.go b/server/http.go index 35697c5c..910651fb 100644 --- a/server/http.go +++ b/server/http.go @@ -37,12 +37,20 @@ func ListenHttp(db Zone, port int) { } weave_cidr := weave_ipstr + "/" + prefix weave_ip, subnet, err := net.ParseCIDR(weave_cidr) - if err == nil { - log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) - db.AddRecord(identifier, name, ip, weave_ip, subnet) - } else if err != nil { + if err != nil { log.Printf("Invalid CIDR in request: %s", weave_cidr) + http.Error(w, fmt.Sprintf("Invalid CIDR: %s", weave_cidr), http.StatusBadRequest) + return } + log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) + err = db.AddRecord(identifier, name, ip, weave_ip, subnet) + if err != nil { + http.Error(w, err.Error(), http.StatusConflict) + return + } + default: + log.Println("Unexpected http method", r.Method) + http.Error(w, "Unexpected http method: "+r.Method, http.StatusBadRequest) } }) address := fmt.Sprintf(":%d", port) From ff540265a98ce7d59a05d0ebe2880c6d411b9855 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:49:27 +0100 Subject: [PATCH 08/12] Better error-checking --- server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http.go b/server/http.go index 910651fb..fffef8ad 100644 --- a/server/http.go +++ b/server/http.go @@ -28,6 +28,7 @@ func ListenHttp(db Zone, port int) { local_ip := r.FormValue("local_ip") if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { log.Printf("Invalid request: %s, %s", r.URL, r.Form) + http.Error(w, "Invalid Request", http.StatusBadRequest) return } ip := net.ParseIP(local_ip) From 6d32ac80b78ad4ed613f9a41ba59efe672e2668d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:50:20 +0100 Subject: [PATCH 09/12] Better error reporting --- server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http.go b/server/http.go index fffef8ad..29484521 100644 --- a/server/http.go +++ b/server/http.go @@ -34,6 +34,7 @@ func ListenHttp(db Zone, port int) { ip := net.ParseIP(local_ip) if ip == nil { log.Printf("Invalid IP in request: %s", local_ip) + http.Error(w, "Invalid IP in request", http.StatusBadRequest) return } weave_cidr := weave_ipstr + "/" + prefix From 4709f852c3428ac6ad53cbdba86ecf33cb8eaa27 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:59:00 +0100 Subject: [PATCH 10/12] Better error reporting --- server/zone.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/zone.go b/server/zone.go index 6957b040..bc33fd5c 100644 --- a/server/zone.go +++ b/server/zone.go @@ -34,10 +34,11 @@ func (ops LookupError) Error() string { type DuplicateError struct { Name string WeaveIp net.IP + Ident string } func (err DuplicateError) Error() string { - return "Duplicate " + err.Name + "," + err.WeaveIp.String() + return "Duplicate " + err.Name + "," + err.WeaveIp.String() + " in container " + err.Ident } // Stop gap. @@ -70,8 +71,8 @@ func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_i zone.mx.Lock() defer zone.mx.Unlock() fqdn := dns.Fqdn(name) - if zone.indexOfNameAddr(fqdn, weave_ip) != -1 { - return DuplicateError{fqdn, weave_ip} + if index := zone.indexOfNameAddr(fqdn, weave_ip); index != -1 { + return DuplicateError{fqdn, weave_ip, zone.recs[index].Ident} } zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) return nil From 11ec66111a146fd2c894a0d8a11766dc3d06e600 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 17:00:14 +0100 Subject: [PATCH 11/12] Check duplicate add --- server/http_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/http_test.go b/server/http_test.go index ac81f70c..662f86d8 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -34,7 +34,8 @@ func TestHttp(t *testing.T) { // Ask the http server to add our test address into the database addr_parts := strings.Split(test_addr1, "/") - resp, err := genForm("PUT", fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), + addr_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]) + resp, err := genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) @@ -53,6 +54,15 @@ func TestHttp(t *testing.T) { t.Fatal("Unexpected result for", success_test_name, ip) } + // Now try adding the same address again - should fail + resp, err = genForm("PUT", addr_url, + url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusConflict { + t.Fatal("Unexpected http response", resp.Status) + } // Would like to shut down the http server at the end of this test // but it's complicated. // See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE From 6e3e7dcd681ae161de6e64a6b11f69655915c302 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 17:01:13 +0100 Subject: [PATCH 12/12] Support DELETE function --- server/http.go | 19 +++++++++++++++++++ server/http_test.go | 16 ++++++++++++++++ server/zone.go | 22 ++++++++++++++++++++++ server/zone_test.go | 12 ++++++++++++ 4 files changed, 69 insertions(+) diff --git a/server/http.go b/server/http.go index 29484521..34c929be 100644 --- a/server/http.go +++ b/server/http.go @@ -50,6 +50,25 @@ func ListenHttp(db Zone, port int) { http.Error(w, err.Error(), http.StatusConflict) return } + case "DELETE": + identifier, weave_ipstr, err := parseUrl(r.URL.Path) + if identifier == "" || weave_ipstr == "" { + log.Printf("Invalid request: %s, %s", r.URL, r.Form) + http.Error(w, "Invalid Request", http.StatusBadRequest) + return + } + weave_ip := net.ParseIP(weave_ipstr) + if weave_ip == nil { + log.Printf("Invalid IP in request: %s", weave_ipstr) + http.Error(w, "Invalid IP in request", http.StatusBadRequest) + return + } + log.Printf("Deleting %s (%s)", identifier, weave_ipstr) + err = db.DeleteRecord(identifier, weave_ip) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } default: log.Println("Unexpected http method", r.Method) http.Error(w, "Unexpected http method: "+r.Method, http.StatusBadRequest) diff --git a/server/http_test.go b/server/http_test.go index 662f86d8..483dc920 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -63,6 +63,22 @@ func TestHttp(t *testing.T) { if resp.StatusCode != http.StatusConflict { t.Fatal("Unexpected http response", resp.Status) } + + // Delete the address + resp, err = genForm("DELETE", addr_url, nil) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusOK { + t.Fatal("Unexpected http response", resp.Status) + } + + // Check that the address is not there now. + _, err = zone.MatchLocal(success_test_name) + if _, ok := err.(LookupError); !ok { + t.Fatal(err) + } + // Would like to shut down the http server at the end of this test // but it's complicated. // See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE diff --git a/server/zone.go b/server/zone.go index bc33fd5c..d3173949 100644 --- a/server/zone.go +++ b/server/zone.go @@ -9,6 +9,7 @@ import ( type Zone interface { AddRecord(string, string, net.IP, net.IP, *net.IPNet) error + DeleteRecord(ident string, weave_ip net.IP) error MatchLocal(string) (net.IP, error) } @@ -61,6 +62,15 @@ func (zone *ZoneDb) indexOfNameAddr(name string, addr net.IP) int { return -1 } +func (zone *ZoneDb) indexOfIdentAddr(ident string, addr net.IP) int { + for i, r := range zone.recs { + if r.Ident == ident && r.WeaveIp.Equal(addr) { + return i + } + } + return -1 +} + func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { zone.mx.Lock() defer zone.mx.Unlock() @@ -77,3 +87,15 @@ func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_i zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) return nil } + +func (zone *ZoneDb) DeleteRecord(ident string, weave_ip net.IP) error { + zone.mx.Lock() + defer zone.mx.Unlock() + if index := zone.indexOfIdentAddr(ident, weave_ip); index == -1 { + return LookupError(ident) + } else { + zone.recs = append(zone.recs[:index], zone.recs[index+1:]...) + } + + return nil +} diff --git a/server/zone_test.go b/server/zone_test.go index 6f392c9d..a3c7b252 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -36,4 +36,16 @@ func TestZone(t *testing.T) { if _, ok := err.(DuplicateError); !ok { t.Fatal(err) } + + // Now delete the record + err = zone.DeleteRecord(container_id, weave_ip) + if err != nil { + t.Fatal(err) + } + + // Check that the address is not there now. + _, err = zone.MatchLocal(success_test_name) + if _, ok := err.(LookupError); !ok { + t.Fatal("Unexpected result when deleting record", success_test_name, err) + } }