diff --git a/libnetwork/etchosts/etchosts.go b/libnetwork/etchosts/etchosts.go index 92597b71b4..256a89dfa8 100644 --- a/libnetwork/etchosts/etchosts.go +++ b/libnetwork/etchosts/etchosts.go @@ -1,6 +1,7 @@ package etchosts import ( + "bufio" "bytes" "fmt" "io" @@ -138,19 +139,36 @@ func Delete(path string, recs []Record) error { if len(recs) == 0 { return nil } - - old, err := ioutil.ReadFile(path) + old, err := os.Open(path) if err != nil { return err } - regexpStr := fmt.Sprintf("\\S*\\t%s\\n", regexp.QuoteMeta(recs[0].Hosts)) - for _, r := range recs[1:] { - regexpStr = regexpStr + "|" + fmt.Sprintf("\\S*\\t%s\\n", regexp.QuoteMeta(r.Hosts)) - } + var buf bytes.Buffer - var re = regexp.MustCompile(regexpStr) - return ioutil.WriteFile(path, re.ReplaceAll(old, []byte("")), 0644) + s := bufio.NewScanner(old) + eol := []byte{'\n'} +loop: + for s.Scan() { + b := s.Bytes() + if b[0] == '#' { + buf.Write(b) + buf.Write(eol) + continue + } + for _, r := range recs { + if bytes.HasSuffix(b, []byte("\t"+r.Hosts)) { + continue loop + } + } + buf.Write(b) + buf.Write(eol) + } + old.Close() + if err := s.Err(); err != nil { + return err + } + return ioutil.WriteFile(path, buf.Bytes(), 0644) } // Update all IP addresses where hostname matches. diff --git a/libnetwork/etchosts/etchosts_test.go b/libnetwork/etchosts/etchosts_test.go index edea8f746d..a79b43b6b7 100644 --- a/libnetwork/etchosts/etchosts_test.go +++ b/libnetwork/etchosts/etchosts_test.go @@ -223,6 +223,10 @@ func TestDelete(t *testing.T) { Hosts: "testhostname2", IP: "2.2.2.2", }, + Record{ + Hosts: "testhostname3", + IP: "3.3.3.3", + }, }); err != nil { t.Fatal(err) } @@ -232,6 +236,10 @@ func TestDelete(t *testing.T) { Hosts: "testhostname1", IP: "1.1.1.1", }, + Record{ + Hosts: "testhostname3", + IP: "3.3.3.3", + }, }); err != nil { t.Fatal(err) } @@ -307,3 +315,50 @@ func TestConcurrentWrites(t *testing.T) { t.Fatalf("Expected to find '%s' got '%s'", expected, content) } } + +func benchDelete(b *testing.B) { + b.StopTimer() + file, err := ioutil.TempFile("", "") + if err != nil { + b.Fatal(err) + } + defer func() { + b.StopTimer() + file.Close() + os.Remove(file.Name()) + b.StartTimer() + }() + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + b.Fatal(err) + } + + var records []Record + var toDelete []Record + for i := 0; i < 255; i++ { + record := Record{ + Hosts: fmt.Sprintf("testhostname%d", i), + IP: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i), + } + records = append(records, record) + if i%2 == 0 { + toDelete = append(records, record) + } + } + + if err := Add(file.Name(), records); err != nil { + b.Fatal(err) + } + + b.StartTimer() + if err := Delete(file.Name(), toDelete); err != nil { + b.Fatal(err) + } +} + +func BenchmarkDelete(b *testing.B) { + for i := 0; i < b.N; i++ { + benchDelete(b) + } +}