discovery: InterfaceName support for global discovery.

MultiPart: 1/3
Change-Id: I9adb2056832035e69a308393cdf61d4b1a001a2e
diff --git a/lib/discovery/global/encoding.go b/lib/discovery/global/encoding.go
index 5d3e324..48e516f 100644
--- a/lib/discovery/global/encoding.go
+++ b/lib/discovery/global/encoding.go
@@ -15,7 +15,7 @@
 
 // encodeAdToSuffix encodes the ad.Id and the ad.Attributes into the suffix at
 // which we mount the advertisement.
-// The format of the generated suffix is id/timestamp/attributes.
+// The format of the generated suffix is id/interfaceName/timestamp/attributes.
 //
 // TODO(suharshs): Currently only the id and the attributes are encoded; we may
 // want to encode the rest of the advertisement someday?
@@ -26,28 +26,34 @@
 	}
 	// Escape suffixDelim to use it as our delimeter between the id and the attrs.
 	id := ad.Id.String()
+	// InterfaceName can never be empty as per validate.go.
+	interfaceName := naming.EncodeAsNameElement(ad.InterfaceName)
 	timestamp := strconv.FormatInt(timestampNs, 10)
 	attr := naming.EncodeAsNameElement(string(b))
-	return naming.Join(id, timestamp, attr), nil
+	return naming.Join(id, interfaceName, timestamp, attr), nil
 }
 
 // decodeAdFromSuffix decodes in into an advertisement.
-// The format of the input suffix is id/timestamp/attributes.
+// The format of the input suffix is id/interfaceName/timestamp/attributes.
 func decodeAdFromSuffix(in string) (*discovery.Advertisement, int64, error) {
-	parts := strings.SplitN(in, "/", 3)
-	if len(parts) != 3 {
+	parts := strings.SplitN(in, "/", 4)
+	if len(parts) != 4 {
 		return nil, 0, NewErrAdInvalidEncoding(nil, in)
 	}
 	var err error
+	var ok bool
 	ad := &discovery.Advertisement{}
 	if ad.Id, err = discovery.ParseAdId(parts[0]); err != nil {
 		return nil, 0, err
 	}
-	timestampNs, err := strconv.ParseInt(parts[1], 10, 64)
+	if ad.InterfaceName, ok = naming.DecodeFromNameElement(parts[1]); !ok {
+		return nil, 0, NewErrAdInvalidEncoding(nil, in)
+	}
+	timestampNs, err := strconv.ParseInt(parts[2], 10, 64)
 	if err != nil {
 		return nil, 0, err
 	}
-	attrs, ok := naming.DecodeFromNameElement(parts[2])
+	attrs, ok := naming.DecodeFromNameElement(parts[3])
 	if !ok {
 		return nil, 0, NewErrAdInvalidEncoding(nil, in)
 	}
diff --git a/lib/discovery/global/encoding_test.go b/lib/discovery/global/encoding_test.go
index 63f369c..2be8f34 100644
--- a/lib/discovery/global/encoding_test.go
+++ b/lib/discovery/global/encoding_test.go
@@ -14,10 +14,28 @@
 
 func TestAdSuffix(t *testing.T) {
 	testCases := []testCase{
-		{ad: &discovery.Advertisement{}, timestampNs: time.Now().UnixNano()},
-		{ad: &discovery.Advertisement{Id: discovery.AdId{1, 2, 3}}, timestampNs: time.Now().UnixNano()},
-		{ad: &discovery.Advertisement{Attributes: discovery.Attributes{"k": "v"}}, timestampNs: time.Now().UnixNano()},
-		{ad: &discovery.Advertisement{Id: discovery.AdId{1, 2, 3}, Attributes: discovery.Attributes{"k": "v"}}, timestampNs: time.Now().UnixNano()},
+		{
+			ad: &discovery.Advertisement{
+				Id:            discovery.AdId{1, 2, 3},
+				InterfaceName: "foo",
+			},
+			timestampNs: time.Now().UnixNano(),
+		},
+		{
+			ad: &discovery.Advertisement{
+				Attributes:    discovery.Attributes{"k": "v"},
+				InterfaceName: "foo",
+			},
+			timestampNs: time.Now().UnixNano(),
+		},
+		{
+			ad: &discovery.Advertisement{
+				Id:            discovery.AdId{1, 2, 3},
+				Attributes:    discovery.Attributes{"k": "v"},
+				InterfaceName: "foo",
+			},
+			timestampNs: time.Now().UnixNano(),
+		},
 	}
 	for i, want := range testCases {
 		encAd, err := encodeAdToSuffix(want.ad, want.timestampNs)
diff --git a/lib/discovery/global/global_test.go b/lib/discovery/global/global_test.go
index b889e7f..5021c85 100644
--- a/lib/discovery/global/global_test.go
+++ b/lib/discovery/global/global_test.go
@@ -29,12 +29,14 @@
 
 	ads := []discovery.Advertisement{
 		{
-			Id:         discovery.AdId{1, 2, 3},
-			Addresses:  []string{"/h1:123/x", "/h2:123/y"},
-			Attributes: discovery.Attributes{"k": "v"},
+			Id:            discovery.AdId{1, 2, 3},
+			InterfaceName: "foo/bar/baz",
+			Addresses:     []string{"/h1:123/x", "/h2:123/y"},
+			Attributes:    discovery.Attributes{"k": "v"},
 		},
 		{
-			Addresses: []string{"/h1:123/x", "/h2:123/z"},
+			InterfaceName: "foo/bar/baz",
+			Addresses:     []string{"/h1:123/x", "/h2:123/z"},
 		},
 	}
 
@@ -113,7 +115,8 @@
 	defer shutdown()
 
 	ad := discovery.Advertisement{
-		Addresses: []string{"/h1:123/x"},
+		InterfaceName: "foo/bar/baz",
+		Addresses:     []string{"/h1:123/x"},
 	}
 	visibility := []security.BlessingPattern{
 		security.BlessingPattern("test-blessing:bob"),
@@ -160,7 +163,8 @@
 	defer shutdown()
 
 	ad := discovery.Advertisement{
-		Addresses: []string{"/h1:123/x"},
+		InterfaceName: "foo/bar/baz",
+		Addresses:     []string{"/h1:123/x"},
 	}
 
 	d, _ := New(ctx, testPath)
@@ -190,7 +194,8 @@
 	ns.SetRoots(mtserver.Status().Endpoints[0].Name())
 
 	ad := discovery.Advertisement{
-		Addresses: []string{"/h1:123/x"},
+		InterfaceName: "foo/bar/baz",
+		Addresses:     []string{"/h1:123/x"},
 	}
 
 	const mountTTL = 10 * time.Second
diff --git a/lib/discovery/global/scan.go b/lib/discovery/global/scan.go
index 489ada1..ddbaf23 100644
--- a/lib/discovery/global/scan.go
+++ b/lib/discovery/global/scan.go
@@ -28,7 +28,7 @@
 
 		var prevFound map[discovery.AdId]*idiscovery.AdInfo
 		for {
-			found, err := d.doScan(ctx, matcher.TargetKey(), matcher)
+			found, err := d.doScan(ctx, matcher)
 			if found == nil {
 				if err != nil {
 					ctx.Error(err)
@@ -48,24 +48,8 @@
 	return updateCh, nil
 }
 
-func (d *gdiscovery) doScan(ctx *context.T, target string, matcher idiscovery.Matcher) (map[discovery.AdId]*idiscovery.AdInfo, error) {
-	// If the target is neither empty nor a valid AdId, we return without an error,
-	// since there will be not entries with the requested target length in the namespace.
-	if len(target) > 0 {
-		if _, err := discovery.ParseAdId(target); err != nil {
-			return nil, nil
-		}
-	}
-
-	// In the case of empty, we need to scan for everything.
-	// In the case where target is a AdId we need to scan for entries prefixed with
-	// the AdId with any encoded attributes afterwards.
-	if len(target) == 0 {
-		target = naming.Join("*", "*", "*")
-	} else {
-		target = naming.Join(target, "*", "*")
-	}
-	scanCh, err := d.ns.Glob(ctx, target)
+func (d *gdiscovery) doScan(ctx *context.T, matcher idiscovery.Matcher) (map[discovery.AdId]*idiscovery.AdInfo, error) {
+	scanCh, err := d.ns.Glob(ctx, generateGlobQuery(matcher))
 	if err != nil {
 		return nil, err
 	}
@@ -110,6 +94,20 @@
 	}
 }
 
+func generateGlobQuery(matcher idiscovery.Matcher) string {
+	// The suffixes are of the form "id/interfaceName/timestamp/attrs" so we need
+	// to replace wildcards in our query with values based on the query matcher.
+	id, interfaceName, timestamp, attrs := "*", "*", "*", "*"
+	// Currently we support query by id or interfaceName.
+	if targetKey := matcher.TargetKey(); targetKey != "" {
+		id = targetKey
+	}
+	if targetInterface := matcher.TargetInterfaceName(); targetInterface != "" {
+		interfaceName = targetInterface
+	}
+	return naming.Join(id, interfaceName, timestamp, attrs)
+}
+
 func (d *gdiscovery) hasAd(ad *discovery.Advertisement) bool {
 	d.mu.Lock()
 	_, ok := d.ads[ad.Id]
diff --git a/lib/discovery/global/validate.go b/lib/discovery/global/validate.go
index 83eef8e..52a5689 100644
--- a/lib/discovery/global/validate.go
+++ b/lib/discovery/global/validate.go
@@ -15,8 +15,8 @@
 	if !ad.Id.IsValid() {
 		return errors.New("id not valid")
 	}
-	if len(ad.InterfaceName) > 0 {
-		return errors.New("interface name not supported")
+	if len(ad.InterfaceName) == 0 {
+		return errors.New("interface name not provided")
 	}
 	if len(ad.Addresses) == 0 {
 		return errors.New("address not provided")
diff --git a/lib/discovery/global/validate_test.go b/lib/discovery/global/validate_test.go
index e9a4a6b..132073c 100644
--- a/lib/discovery/global/validate_test.go
+++ b/lib/discovery/global/validate_test.go
@@ -17,37 +17,40 @@
 	}{
 		{
 			discovery.Advertisement{
-				Id:         discovery.AdId{1, 2, 3},
-				Addresses:  []string{"/h:123/x"},
-				Attributes: discovery.Attributes{"k": "v"},
+				Id:            discovery.AdId{1, 2, 3},
+				Addresses:     []string{"/h:123/x"},
+				Attributes:    discovery.Attributes{"k": "v"},
+				InterfaceName: "foo/bar/baz",
 			},
 			true,
 		},
 		{
 			discovery.Advertisement{
-				Id:        discovery.AdId{}, // Invalid id.
-				Addresses: []string{"/h:123/x"},
+				Id:            discovery.AdId{}, // Invalid id.
+				Addresses:     []string{"/h:123/x"},
+				InterfaceName: "foo/bar/baz",
 			},
 			false,
 		},
 		{
 			discovery.Advertisement{ // No addresses.
-				Id: discovery.AdId{1, 2, 3},
+				Id:            discovery.AdId{1, 2, 3},
+				InterfaceName: "foo/bar/baz",
+			},
+			false,
+		},
+		{
+			discovery.Advertisement{ // Has no interface name.
+				Id:        discovery.AdId{1, 2, 3},
+				Addresses: []string{"/h:123/x"},
 			},
 			false,
 		},
 		{
 			discovery.Advertisement{
 				Id:            discovery.AdId{1, 2, 3},
-				InterfaceName: "v.io/v23/a", // Has interface name.
+				InterfaceName: "foo/bar/baz",
 				Addresses:     []string{"/h:123/x"},
-			},
-			false,
-		},
-		{
-			discovery.Advertisement{
-				Id:        discovery.AdId{1, 2, 3},
-				Addresses: []string{"/h:123/x"},
 				Attachments: discovery.Attachments{ // Has attachments.
 					"k": []byte{1},
 				},