From f680226fa4ae6208d2c2a03df7b131e6b1725092 Mon Sep 17 00:00:00 2001 From: Isaiah Snell-Feikema Date: Fri, 31 Aug 2018 11:16:19 -0500 Subject: [PATCH 1/3] wip --- pilot/pkg/model/push_context.go | 13 +- pilot/pkg/model/radix.go | 142 ++++++++++++++++++ pilot/pkg/model/radix_test.go | 88 +++++++++++ .../pkg/networking/core/v1alpha3/listener.go | 1 + 4 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 pilot/pkg/model/radix.go create mode 100644 pilot/pkg/model/radix_test.go diff --git a/pilot/pkg/model/push_context.go b/pilot/pkg/model/push_context.go index a901f591818..5fdb86c14c6 100644 --- a/pilot/pkg/model/push_context.go +++ b/pilot/pkg/model/push_context.go @@ -69,7 +69,7 @@ type PushContext struct { // ServiceAccounts map[string][]string // Temp: the code in alpha3 should use VirtualService directly VirtualServiceConfigs []Config `json:"-,omitempty"` - + destinationRuleHosts []Hostname destinationRuleByHosts map[Hostname]*combinedDestinationRule @@ -286,6 +286,10 @@ func (ps *PushContext) VirtualServices(gateways map[string]bool) []Config { return out } +func (ps *PushContext) VirtualService(gateways map[string]bool, hostname Hostname) (Config, bool) { + return Config{}, false +} + // InitContext will initialize the data structures used for code generation. // This should be called before starting the push, from the thread creating // the push context. @@ -323,7 +327,8 @@ func (ps *PushContext) initServiceRegistry(env *Environment) error { return err } // Sort the services in order of creation. - ps.Services = sortServicesByCreationTime(services) + sortServicesByCreationTime(services) + ps.Services = services for _, s := range services { ps.ServiceByHostname[s.Hostname] = s } @@ -331,11 +336,10 @@ func (ps *PushContext) initServiceRegistry(env *Environment) error { } // sortServicesByCreationTime sorts the list of services in ascending order by their creation time (if available). -func sortServicesByCreationTime(services []*Service) []*Service { +func sortServicesByCreationTime(services []*Service) { sort.SliceStable(services, func(i, j int) bool { return services[i].CreationTime.Before(services[j].CreationTime) }) - return services } // Caches list of virtual services @@ -344,7 +348,6 @@ func (ps *PushContext) initVirtualServices(env *Environment) error { if err != nil { return err } - sortConfigByCreationTime(vservices) ps.VirtualServiceConfigs = vservices // convert all shortnames in virtual services into FQDNs diff --git a/pilot/pkg/model/radix.go b/pilot/pkg/model/radix.go new file mode 100644 index 00000000000..616a4b3954a --- /dev/null +++ b/pilot/pkg/model/radix.go @@ -0,0 +1,142 @@ +// Copyright 2018 Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "github.com/hashicorp/go-immutable-radix" + "strings" +) + +// TODO: handle lookups with wildcards in them? +// TODO: if there are conflicts, pick the oldest config + +type hostLookup interface { + Lookup(hostname Hostname) map[Hostname]Config +} + +type reverseRadix struct { + radix *iradix.Tree +} + +func newReverseRadix() *reverseRadix { + return &reverseRadix{ + radix: iradix.New(), + } +} + +// This function returns the set of the most specific matching virtual services +// for a hostname. It supports wildcards in both the search hostname as well +// as the virtual service hostnames. +// +// To retrieve the most specific matches, we need to define what we consider more or less specific. +// We define the specificity of a match as the amount of the query host that is matched with the +// config host. A match where more of the query host is matched is considered more specific. +// +// "abc.def" matches both "abc.def" and "*.def" +// "abc.def" has an exact match of all 7 characters of "abc.def", but only 4 characters of "*.def". +// therefore the match of "abc.def" is considered more specific than the match of "*.def" +// +// This definition of specificity becomes important when wildcards are present in both the query +// host and the config host. When the query host contains a wildcard, there can be multiple +// equally specific matches. This is illustrated below with an example: +// +// "*.def" matches "abc.def", "*.def", and "*" +// the match with "abc.def" and "*.def" have equal specificity: both have an exact match of +// 4 characters with the query host. +// the match of "*" is less specific than the other two matches, since the exact match is 0 characters. +// thus, the most specific matches are "abc.def" and "*.def" +// +// Let's explore some examples, starting with the simplest case and iteratively adding complexity. +// +// Virtual services: +// {"abc.def"} -> A +// {"abc.xyz"} -> B +// +// lookup("abc.def") => A +// lookup("abc.xyz") => B +// lookup("abc.mno") => nil +// +// Now, let's add wildcard virtual service hosts. +// +// Virtual services: +// {"abc.def"} -> A +// {"*.def"} -> B +// +// lookup("abc.def") -> A +// lookup("foo.def") -> B +// +// "abc.def" matches both A and B, but A is a more specific match (more of the lookup hostname is found in A than B). +// For "foo.def", however, the most specific match that exists is B. +// +// Things get extra tricky when we allow wildcards in the lookup host. +// +// Virtual services: +// {"abc.def"} -> A +// {"*.def"} -> B +// {"*"} -> C +// +// lookup("abc.def") -> A (also matches B, C, but those are less specific) +// lookup("foo.def") -> B (also matches C, but that is less specific) +// lookup("*.def") -> A, B (A and B are equally specific. also matches C, but C is less specific than A or B) +// lookup("*.qwerty") -> C (only matches C) +// +// +func (r *reverseRadix) Lookup(hostname Hostname) map[Hostname]Config { + configs := make(map[Hostname]Config) + s := string(hostname) + wildcard := strings.Contains(s, "*") + s = strings.Replace(s, "*", "", -1) + data := []byte(s) + reverse(data) + + // + if wildcard { + r.radix.Root().WalkPrefix(data, func(k []byte, v interface{}) bool { + kCopy := make([]byte, len(k)) + copy(kCopy, k) + reverse(kCopy) + config, _ := v.(Config) + configs[Hostname(kCopy)] = config + return false + }) + } + + // + if !wildcard || len(configs) == 0 { + data := []byte(s) + reverse(data) + k, v, _ := r.radix.Root().LongestPrefix(data) + config, _ := v.(Config) + kCopy := make([]byte, len(k)) + copy(kCopy, k) + reverse(kCopy) + configs[Hostname(kCopy)] = config + } + + return configs +} + +func (r *reverseRadix) Insert(hostname Hostname, config Config) { + s := strings.Replace(string(hostname), "*", "", -1) + data := []byte(s) + reverse(data) + r.radix, _, _ = r.radix.Insert(data, config) +} + +func reverse(data []byte) { + for i := 0; i < len(data)/2; i++ { + data[i], data[len(data)-i-1] = data[len(data)-i-1], data[i] + } +} diff --git a/pilot/pkg/model/radix_test.go b/pilot/pkg/model/radix_test.go new file mode 100644 index 00000000000..eba6d72f0db --- /dev/null +++ b/pilot/pkg/model/radix_test.go @@ -0,0 +1,88 @@ +// Copyright 2018 Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package model + +import ( + "testing" +) +func TestReverseRadix(t *testing.T) { + r := newReverseRadix() + + contents := []struct{ + config Config + hostnames Hostnames + } { + {Config{ConfigMeta: ConfigMeta{Name:"cnn"}}, Hostnames{"www.cnn.com", "*.cnn.com", "*.com"}}, + {Config{ConfigMeta: ConfigMeta{Name:"edition_cnn"}}, Hostnames{"edition.cnn.com"}}, + {Config{ConfigMeta: ConfigMeta{Name:"*.co.uk"}}, Hostnames{"*.co.uk"}}, + {Config{ConfigMeta: ConfigMeta{Name:"*"}}, Hostnames{"*"}}, + {Config{ConfigMeta: ConfigMeta{Name:"io"}}, Hostnames{"*.io"}}, + {Config{ConfigMeta: ConfigMeta{Name:"*.preliminary.io"}}, Hostnames{"*.preliminary.io"}}, + } + + for _, content := range contents { + for _, hostname := range content.hostnames { + r.Insert(hostname, content.config) + } + } + + // testCases := []struct{ + // hostname Hostname + // configName string + // } { + // {"www.cnn.com", "cnn"}, + // {"money.cnn.com", "cnn"}, + // {"edition.cnn.com", "edition_cnn"}, + // {"bbc.co.uk", "*.co.uk"}, + // {"www.wikipedia.org", "*"}, + // {"*.cnn.com", "cnn"}, + // {"*.uk", "*.co.uk"}, + // {"*", "oldest config"}, + // } + + testCases := []struct{ + in Hostname + out Hostnames + } { + {"www.cnn.com", Hostnames{"www.cnn.com"}}, + {"money.cnn.com", Hostnames{".cnn.com"}}, + {"edition.cnn.com", Hostnames{"edition.cnn.com"}}, + {"bbc.co.uk", Hostnames{".co.uk"}}, + {"www.wikipedia.org", Hostnames{""}}, // wildcard + {"*.cnn.com", Hostnames{"www.cnn.com", ".cnn.com", "edition.cnn.com"}}, // what about *.com? what about just *? + {"*.com", Hostnames{".com", ".cnn.com", "www.cnn.com", "edition.cnn.com"}}, // what about *? + {"*.uk", Hostnames{".co.uk"}}, + //{"*", Hostnames{"www.cnn.com", ".cnn.com", ".com", "edition.cnn.com", "", ".co.uk"}}, + {"*.istio.io", Hostnames{".io"}}, + {"*.preliminary.io", Hostnames{".preliminary.io"}}, + } + + for _, tt := range testCases { + // config, _ := r.MostSpecificHostMatch(tt.hostname) + // if config.Name != tt.configName { + // t.Errorf("f(%v) -> wanted %v, got %v", tt.hostname, tt.configName, config.Name) + // } + + configs := r.Lookup(tt.in) + if len(tt.out) != len(configs) { + t.Errorf("f(%v) -> wanted len()=%v, got len()=%v", tt.in, len(tt.out), len(configs)) + } + for _, h := range tt.out { + if _, ok := configs[h]; !ok { + t.Errorf("f(%v) -> missing %v", tt.in, h) + } + } + } +} + diff --git a/pilot/pkg/networking/core/v1alpha3/listener.go b/pilot/pkg/networking/core/v1alpha3/listener.go index c030ff04719..d8584442348 100644 --- a/pilot/pkg/networking/core/v1alpha3/listener.go +++ b/pilot/pkg/networking/core/v1alpha3/listener.go @@ -385,6 +385,7 @@ func (c outboundListenerConflict) addMetric(push *model.PushContext) { // buildSidecarOutboundListeners generates http and tcp listeners for outbound connections from the service instance // TODO(github.com/istio/pilot/issues/237) // +// FIXME: the doc below is out of date... // Sharing tcp_proxy and http_connection_manager filters on the same port for // different destination services doesn't work with Envoy (yet). When the // tcp_proxy filter's route matching fails for the http service the connection From 0483172191af3ef6db15fbd79e1e2a9004327c52 Mon Sep 17 00:00:00 2001 From: Isaiah Snell-Feikema Date: Mon, 15 Oct 2018 13:37:30 -0500 Subject: [PATCH 2/3] simplify code --- pilot/pkg/model/radix.go | 37 ++++++++++++++++++----------------- pilot/pkg/model/radix_test.go | 30 +++++++--------------------- 2 files changed, 26 insertions(+), 41 deletions(-) diff --git a/pilot/pkg/model/radix.go b/pilot/pkg/model/radix.go index 616a4b3954a..017cbe652cd 100644 --- a/pilot/pkg/model/radix.go +++ b/pilot/pkg/model/radix.go @@ -95,44 +95,45 @@ func newReverseRadix() *reverseRadix { // func (r *reverseRadix) Lookup(hostname Hostname) map[Hostname]Config { configs := make(map[Hostname]Config) - s := string(hostname) - wildcard := strings.Contains(s, "*") - s = strings.Replace(s, "*", "", -1) - data := []byte(s) - reverse(data) + wildcard := strings.Contains(string(hostname), "*") // if wildcard { - r.radix.Root().WalkPrefix(data, func(k []byte, v interface{}) bool { - kCopy := make([]byte, len(k)) - copy(kCopy, k) - reverse(kCopy) + r.radix.Root().WalkPrefix(r.toKey(hostname), func(k []byte, v interface{}) bool { config, _ := v.(Config) - configs[Hostname(kCopy)] = config + configs[r.fromKey(k)] = config return false }) } // if !wildcard || len(configs) == 0 { - data := []byte(s) - reverse(data) - k, v, _ := r.radix.Root().LongestPrefix(data) + k, v, _ := r.radix.Root().LongestPrefix(r.toKey(hostname)) config, _ := v.(Config) - kCopy := make([]byte, len(k)) - copy(kCopy, k) - reverse(kCopy) - configs[Hostname(kCopy)] = config + configs[r.fromKey(k)] = config } return configs } func (r *reverseRadix) Insert(hostname Hostname, config Config) { + r.radix, _, _ = r.radix.Insert(r.toKey(hostname), config) +} + +// removes wildcard, reverses +func (r *reverseRadix) toKey(hostname Hostname) []byte { s := strings.Replace(string(hostname), "*", "", -1) data := []byte(s) reverse(data) - r.radix, _, _ = r.radix.Insert(data, config) + return data +} + +// unreverses +func (r *reverseRadix) fromKey(key []byte) Hostname { + data := make([]byte, len(key)) + copy(data, key) + reverse(data) + return Hostname(data) } func reverse(data []byte) { diff --git a/pilot/pkg/model/radix_test.go b/pilot/pkg/model/radix_test.go index eba6d72f0db..568284f01df 100644 --- a/pilot/pkg/model/radix_test.go +++ b/pilot/pkg/model/radix_test.go @@ -37,20 +37,6 @@ func TestReverseRadix(t *testing.T) { } } - // testCases := []struct{ - // hostname Hostname - // configName string - // } { - // {"www.cnn.com", "cnn"}, - // {"money.cnn.com", "cnn"}, - // {"edition.cnn.com", "edition_cnn"}, - // {"bbc.co.uk", "*.co.uk"}, - // {"www.wikipedia.org", "*"}, - // {"*.cnn.com", "cnn"}, - // {"*.uk", "*.co.uk"}, - // {"*", "oldest config"}, - // } - testCases := []struct{ in Hostname out Hostnames @@ -59,24 +45,22 @@ func TestReverseRadix(t *testing.T) { {"money.cnn.com", Hostnames{".cnn.com"}}, {"edition.cnn.com", Hostnames{"edition.cnn.com"}}, {"bbc.co.uk", Hostnames{".co.uk"}}, - {"www.wikipedia.org", Hostnames{""}}, // wildcard - {"*.cnn.com", Hostnames{"www.cnn.com", ".cnn.com", "edition.cnn.com"}}, // what about *.com? what about just *? - {"*.com", Hostnames{".com", ".cnn.com", "www.cnn.com", "edition.cnn.com"}}, // what about *? + {"www.wikipedia.org", Hostnames{""}}, + {"*.cnn.com", Hostnames{"www.cnn.com", ".cnn.com", "edition.cnn.com"}}, + {"*.com", Hostnames{".com", ".cnn.com", "www.cnn.com", "edition.cnn.com"}}, {"*.uk", Hostnames{".co.uk"}}, - //{"*", Hostnames{"www.cnn.com", ".cnn.com", ".com", "edition.cnn.com", "", ".co.uk"}}, {"*.istio.io", Hostnames{".io"}}, {"*.preliminary.io", Hostnames{".preliminary.io"}}, + {"*.io", Hostnames{".io", ".preliminary.io"}}, + {"nothing.nowhere.net", Hostnames{""}}, + // {"*", Hostnames{"www.cnn.com", ".cnn.com", ".com", "edition.cnn.com", "", ".co.uk"}}, // this is a maintenance burden } for _, tt := range testCases { - // config, _ := r.MostSpecificHostMatch(tt.hostname) - // if config.Name != tt.configName { - // t.Errorf("f(%v) -> wanted %v, got %v", tt.hostname, tt.configName, config.Name) - // } - configs := r.Lookup(tt.in) if len(tt.out) != len(configs) { t.Errorf("f(%v) -> wanted len()=%v, got len()=%v", tt.in, len(tt.out), len(configs)) + t.Errorf("%#v", configs) } for _, h := range tt.out { if _, ok := configs[h]; !ok { From aa36574d2c7b17ef112cded49d6bb115c2249b82 Mon Sep 17 00:00:00 2001 From: Isaiah Snell-Feikema Date: Tue, 16 Oct 2018 11:56:17 -0500 Subject: [PATCH 3/3] more doc + cleanup --- pilot/pkg/model/radix.go | 87 +++++++++++------------------------ pilot/pkg/model/radix_test.go | 34 +++++++------- 2 files changed, 45 insertions(+), 76 deletions(-) diff --git a/pilot/pkg/model/radix.go b/pilot/pkg/model/radix.go index 017cbe652cd..3e135c994cd 100644 --- a/pilot/pkg/model/radix.go +++ b/pilot/pkg/model/radix.go @@ -19,85 +19,53 @@ import ( "strings" ) -// TODO: handle lookups with wildcards in them? // TODO: if there are conflicts, pick the oldest config type hostLookup interface { Lookup(hostname Hostname) map[Hostname]Config } -type reverseRadix struct { +type radix struct { radix *iradix.Tree } -func newReverseRadix() *reverseRadix { - return &reverseRadix{ +func newRadix() *radix { + return &radix{ radix: iradix.New(), } } -// This function returns the set of the most specific matching virtual services -// for a hostname. It supports wildcards in both the search hostname as well -// as the virtual service hostnames. +// This function returns the set of the most specific matching configs +// for a hostname. It supports wildcards in both the query hostname as well +// as the config hostnames. // // To retrieve the most specific matches, we need to define what we consider more or less specific. -// We define the specificity of a match as the amount of the query host that is matched with the +// We define the specificity of a match as the amount of the query host that is matched with the // config host. A match where more of the query host is matched is considered more specific. // -// "abc.def" matches both "abc.def" and "*.def" -// "abc.def" has an exact match of all 7 characters of "abc.def", but only 4 characters of "*.def". -// therefore the match of "abc.def" is considered more specific than the match of "*.def" +// Consider the query hostname "abc.def" and config hostnames "abc.def" and "*.def": +// - The query hostname "abc.def" matches both "abc.def" and "*.def" +// - The query hostname "abc.def" has an exact match of all 7 characters of "abc.def", but only 4 +// characters of "*.def". Therefore the match of "abc.def" is considered more specific than +// the match of "*.def". // // This definition of specificity becomes important when wildcards are present in both the query // host and the config host. When the query host contains a wildcard, there can be multiple // equally specific matches. This is illustrated below with an example: // -// "*.def" matches "abc.def", "*.def", and "*" -// the match with "abc.def" and "*.def" have equal specificity: both have an exact match of -// 4 characters with the query host. -// the match of "*" is less specific than the other two matches, since the exact match is 0 characters. -// thus, the most specific matches are "abc.def" and "*.def" +// The query host "*.def" matches "abc.def", "*.def", and "*" +// - the match with "abc.def" and "*.def" have equal specificity: both have an exact match of +// 4 characters with the query host. +// - the match of "*" is less specific than the other two matches, since the exact match is 0 characters. +// thus, the most specific matches are "abc.def" and "*.def" // -// Let's explore some examples, starting with the simplest case and iteratively adding complexity. -// -// Virtual services: -// {"abc.def"} -> A -// {"abc.xyz"} -> B -// -// lookup("abc.def") => A -// lookup("abc.xyz") => B -// lookup("abc.mno") => nil -// -// Now, let's add wildcard virtual service hosts. -// -// Virtual services: -// {"abc.def"} -> A -// {"*.def"} -> B -// -// lookup("abc.def") -> A -// lookup("foo.def") -> B -// -// "abc.def" matches both A and B, but A is a more specific match (more of the lookup hostname is found in A than B). -// For "foo.def", however, the most specific match that exists is B. -// -// Things get extra tricky when we allow wildcards in the lookup host. -// -// Virtual services: -// {"abc.def"} -> A -// {"*.def"} -> B -// {"*"} -> C -// -// lookup("abc.def") -> A (also matches B, C, but those are less specific) -// lookup("foo.def") -> B (also matches C, but that is less specific) -// lookup("*.def") -> A, B (A and B are equally specific. also matches C, but C is less specific than A or B) -// lookup("*.qwerty") -> C (only matches C) -// -// -func (r *reverseRadix) Lookup(hostname Hostname) map[Hostname]Config { +// This function uses a radix to implement the behavior described above. +func (r *radix) Lookup(hostname Hostname) map[Hostname]Config { configs := make(map[Hostname]Config) wildcard := strings.Contains(string(hostname), "*") - // + // If a wildcard is present in the query hostname there may be multiple equally specific matches, + // so we attempt to walk every config hostname under this prefix. if wildcard { r.radix.Root().WalkPrefix(r.toKey(hostname), func(k []byte, v interface{}) bool { config, _ := v.(Config) @@ -106,7 +74,8 @@ func (r *reverseRadix) Lookup(hostname Hostname) map[Hostname]Config { }) } - // + // If the query hostname has no wildcard, or there were no configs under the prefix, we get the + // longest matching prefix for this query hostname. if !wildcard || len(configs) == 0 { k, v, _ := r.radix.Root().LongestPrefix(r.toKey(hostname)) config, _ := v.(Config) @@ -116,20 +85,20 @@ func (r *reverseRadix) Lookup(hostname Hostname) map[Hostname]Config { return configs } -func (r *reverseRadix) Insert(hostname Hostname, config Config) { +func (r *radix) Insert(hostname Hostname, config Config) { r.radix, _, _ = r.radix.Insert(r.toKey(hostname), config) } -// removes wildcard, reverses -func (r *reverseRadix) toKey(hostname Hostname) []byte { +// Strips the wildcard character '*' and stores the hostname in the radix in reversed character order. +func (r *radix) toKey(hostname Hostname) []byte { s := strings.Replace(string(hostname), "*", "", -1) data := []byte(s) reverse(data) return data } -// unreverses -func (r *reverseRadix) fromKey(key []byte) Hostname { +// Unreverses the hostname. +func (r *radix) fromKey(key []byte) Hostname { data := make([]byte, len(key)) copy(data, key) reverse(data) diff --git a/pilot/pkg/model/radix_test.go b/pilot/pkg/model/radix_test.go index 568284f01df..547c8f6804f 100644 --- a/pilot/pkg/model/radix_test.go +++ b/pilot/pkg/model/radix_test.go @@ -16,31 +16,32 @@ package model import ( "testing" ) -func TestReverseRadix(t *testing.T) { - r := newReverseRadix() - contents := []struct{ - config Config +func TestRadix(t *testing.T) { + r := newRadix() + + contents := []struct { + config Config hostnames Hostnames - } { - {Config{ConfigMeta: ConfigMeta{Name:"cnn"}}, Hostnames{"www.cnn.com", "*.cnn.com", "*.com"}}, - {Config{ConfigMeta: ConfigMeta{Name:"edition_cnn"}}, Hostnames{"edition.cnn.com"}}, - {Config{ConfigMeta: ConfigMeta{Name:"*.co.uk"}}, Hostnames{"*.co.uk"}}, - {Config{ConfigMeta: ConfigMeta{Name:"*"}}, Hostnames{"*"}}, - {Config{ConfigMeta: ConfigMeta{Name:"io"}}, Hostnames{"*.io"}}, - {Config{ConfigMeta: ConfigMeta{Name:"*.preliminary.io"}}, Hostnames{"*.preliminary.io"}}, + }{ + {Config{ConfigMeta: ConfigMeta{Name: "cnn"}}, Hostnames{"www.cnn.com", "*.cnn.com", "*.com"}}, + {Config{ConfigMeta: ConfigMeta{Name: "edition_cnn"}}, Hostnames{"edition.cnn.com"}}, + {Config{ConfigMeta: ConfigMeta{Name: "*.co.uk"}}, Hostnames{"*.co.uk"}}, + {Config{ConfigMeta: ConfigMeta{Name: "*"}}, Hostnames{"*"}}, + {Config{ConfigMeta: ConfigMeta{Name: "io"}}, Hostnames{"*.io"}}, + {Config{ConfigMeta: ConfigMeta{Name: "*.preliminary.io"}}, Hostnames{"*.preliminary.io"}}, } - + for _, content := range contents { for _, hostname := range content.hostnames { r.Insert(hostname, content.config) } } - testCases := []struct{ - in Hostname + testCases := []struct { + in Hostname out Hostnames - } { + }{ {"www.cnn.com", Hostnames{"www.cnn.com"}}, {"money.cnn.com", Hostnames{".cnn.com"}}, {"edition.cnn.com", Hostnames{"edition.cnn.com"}}, @@ -53,7 +54,7 @@ func TestReverseRadix(t *testing.T) { {"*.preliminary.io", Hostnames{".preliminary.io"}}, {"*.io", Hostnames{".io", ".preliminary.io"}}, {"nothing.nowhere.net", Hostnames{""}}, - // {"*", Hostnames{"www.cnn.com", ".cnn.com", ".com", "edition.cnn.com", "", ".co.uk"}}, // this is a maintenance burden + // {"*", Hostnames{"www.cnn.com", ".cnn.com", ".com", "edition.cnn.com", "", ".co.uk"}}, // maintenance burden } for _, tt := range testCases { @@ -69,4 +70,3 @@ func TestReverseRadix(t *testing.T) { } } } -