services/allocator/allocatord: Add to creatorInfo

Add the mountName and the blessingNames to the creatorInfo annotation.

Go back to using self-blessed blessings when testing.

Change-Id: I42adb1a9a0f119b39be25deb6629986c651b3006
diff --git a/services/allocator/allocatord/cache.go b/services/allocator/allocatord/cache.go
index be7bc4a..bd5807f 100644
--- a/services/allocator/allocatord/cache.go
+++ b/services/allocator/allocatord/cache.go
@@ -9,6 +9,7 @@
 
 	"github.com/golang/groupcache/lru"
 
+	"v.io/v23/context"
 	"v.io/v23/verror"
 )
 
@@ -17,7 +18,7 @@
 	ownerCacheMutex sync.Mutex
 )
 
-func checkOwner(email, kName string) error {
+func checkOwner(ctx *context.T, email, kName string) error {
 	ownerCacheMutex.Lock()
 	if ownerCache == nil {
 		ownerCache = lru.New(maxInstancesFlag)
@@ -30,7 +31,7 @@
 	}
 	ownerCacheMutex.Unlock()
 
-	if err := isOwnerOfInstance(email, kName); err != nil {
+	if err := isOwnerOfInstance(ctx, email, kName); err != nil {
 		return err
 	}
 	ownerCacheMutex.Lock()
diff --git a/services/allocator/allocatord/dashboard.go b/services/allocator/allocatord/dashboard.go
index 19a6ed8..fff3715 100644
--- a/services/allocator/allocatord/dashboard.go
+++ b/services/allocator/allocatord/dashboard.go
@@ -54,11 +54,12 @@
 }
 
 func handleDashboard(ss *serverState, rs *requestState) error {
+	ctx := ss.ctx
 	instance := rs.r.FormValue(paramDashboardName)
 	if instance == "" {
 		return fmt.Errorf("parameter %q required for instance name", paramDashboardName)
 	}
-	if err := checkOwner(rs.email, kubeNameFromMountName(instance)); err != nil {
+	if err := checkOwner(ctx, rs.email, kubeNameFromMountName(instance)); err != nil {
 		return err
 	}
 
@@ -86,7 +87,7 @@
 	if mountedName == "" {
 		return fmt.Errorf("parameter %q required for instance name", paramDashboardName)
 	}
-	if err := checkOwner(rs.email, kubeNameFromMountName(mountedName)); err != nil {
+	if err := checkOwner(ctx, rs.email, kubeNameFromMountName(mountedName)); err != nil {
 		return err
 	}
 
diff --git a/services/allocator/allocatord/handlers.go b/services/allocator/allocatord/handlers.go
index bdfb3fe..8635456 100644
--- a/services/allocator/allocatord/handlers.go
+++ b/services/allocator/allocatord/handlers.go
@@ -10,7 +10,6 @@
 	"strings"
 	"time"
 
-	"v.io/v23"
 	"v.io/v23/security"
 )
 
@@ -43,17 +42,21 @@
 		Message:      rs.r.FormValue(paramMessage),
 	}
 	for _, instance := range instances {
-		var patterns []string
-		for _, b := range security.BlessingNames(v23.GetPrincipal(ctx), ss.args.baseBlessings) {
-			bName := strings.Join([]string{b, instance.name}, security.ChainSeparator)
-			patterns = append(patterns, bName)
+		if len(instance.blessingNames) == 0 {
+			// TODO(rthellend): Remove when all instances have the new
+			// creatorInfo annotations.
+			ctx.Info("blessingNames missing from creatorInfo")
+			for _, b := range ss.args.baseBlessingNames {
+				bName := strings.Join([]string{b, instance.name}, security.ChainSeparator)
+				instance.blessingNames = append(instance.blessingNames, bName)
+			}
 		}
 
 		tmplArgs.Instances = append(tmplArgs.Instances, instanceArg{
 			Name:             instance.mountName,
 			NameRoot:         nameRoot(ctx),
 			CreationTime:     instance.creationTime,
-			BlessingPatterns: patterns,
+			BlessingPatterns: instance.blessingNames,
 			DestroyURL:       makeURL(ctx, routeDestroy, params{paramName: instance.mountName, paramCSRF: rs.csrfToken}),
 			DashboardURL:     makeURL(ctx, routeDashboard, params{paramDashboardName: relativeMountName(instance.mountName), paramCSRF: rs.csrfToken}),
 		})
@@ -66,7 +69,7 @@
 
 func handleCreate(ss *serverState, rs *requestState) error {
 	ctx := ss.ctx
-	name, err := create(ctx, rs.email, ss.args.baseBlessings)
+	name, err := create(ctx, rs.email, ss.args.baseBlessings, ss.args.baseBlessingNames)
 	if err != nil {
 		return fmt.Errorf("create failed: %v", err)
 	}
diff --git a/services/allocator/allocatord/http.go b/services/allocator/allocatord/http.go
index 8b6fdb0..932cdbe 100644
--- a/services/allocator/allocatord/http.go
+++ b/services/allocator/allocatord/http.go
@@ -76,9 +76,10 @@
 	dashboardGCMMetric,
 	dashboardGCMProject,
 	monitoringKeyFile string
-	secureCookies bool
-	oauthCreds    *oauthCredentials
-	baseBlessings security.Blessings
+	secureCookies     bool
+	oauthCreds        *oauthCredentials
+	baseBlessings     security.Blessings
+	baseBlessingNames []string
 	// URI prefix for static assets served from (another) content server.
 	staticAssetsPrefix string
 	// Manages locally served resources.
diff --git a/services/allocator/allocatord/main.go b/services/allocator/allocatord/main.go
index 661c5c6..967f1ce 100644
--- a/services/allocator/allocatord/main.go
+++ b/services/allocator/allocatord/main.go
@@ -101,19 +101,18 @@
 		return env.UsageErrorf("--%s not specified, and no default namespace root found", serverNameRootFlagName)
 	}
 
-	var baseBlessings security.Blessings
+	var (
+		baseBlessings     security.Blessings
+		baseBlessingNames []string
+	)
 	if clusterAgentFlag == "" || blessingSecretFlag == "" {
-		fmt.Fprintln(env.Stderr, "WARNING: Using disabled blessings for allocated servers")
-		p := v23.GetPrincipal(ctx)
-		defaultB, _ := p.BlessingStore().Default()
-		// This caveat ensures that the blessing we create cannot be used.
-		disabled, err := security.NewCaveat(security.ConstCaveat, false)
-		if err != nil {
+		fmt.Fprintln(env.Stderr, "WARNING: Using self-blessed blessings for allocated servers")
+		const selfName = "allocator"
+		var err error
+		if baseBlessings, err = v23.GetPrincipal(ctx).BlessSelf(selfName); err != nil {
 			return err
 		}
-		if baseBlessings, err = p.Bless(p.PublicKey(), defaultB, "allocator", disabled); err != nil {
-			return err
-		}
+		baseBlessingNames = []string{selfName}
 	} else {
 		secret, err := ioutil.ReadFile(blessingSecretFlag)
 		if err != nil {
@@ -123,12 +122,13 @@
 		if err != nil {
 			return err
 		}
+		baseBlessingNames = security.BlessingNames(v23.GetPrincipal(ctx), baseBlessings)
 	}
 
 	ctx, server, err := v23.WithNewServer(
 		ctx,
 		nameFlag,
-		allocator.AllocatorServer(&allocatorImpl{baseBlessings}),
+		allocator.AllocatorServer(&allocatorImpl{baseBlessings, baseBlessingNames}),
 		security.AllowEveryone(),
 	)
 	if err != nil {
@@ -157,6 +157,7 @@
 			serverName:          serverNameFlag,
 			secureCookies:       secureCookiesFlag,
 			baseBlessings:       baseBlessings,
+			baseBlessingNames:   baseBlessingNames,
 			staticAssetsPrefix:  staticAssetsPrefixFlag,
 			assets:              ah,
 		})
diff --git a/services/allocator/allocatord/service.go b/services/allocator/allocatord/service.go
index 2036300..b57d007 100644
--- a/services/allocator/allocatord/service.go
+++ b/services/allocator/allocatord/service.go
@@ -33,7 +33,8 @@
 )
 
 type allocatorImpl struct {
-	baseBlessings security.Blessings
+	baseBlessings     security.Blessings
+	baseBlessingNames []string
 }
 
 // Create creates a new instance of the service.
@@ -46,7 +47,7 @@
 	if email == "" {
 		return "", verror.New(verror.ErrNoAccess, ctx, "unable to determine caller's email address")
 	}
-	return create(ctx, email, i.baseBlessings)
+	return create(ctx, email, i.baseBlessings, i.baseBlessingNames)
 }
 
 // Destroy destroys the instance with the given name.
@@ -81,17 +82,17 @@
 	return mNames, nil
 }
 
-func create(ctx *context.T, email string, baseBlessings security.Blessings) (string, error) {
+func create(ctx *context.T, email string, baseBlessings security.Blessings, baseBlessingNames []string) (string, error) {
 	// Enforce a limit on the number of instances. These tests are a little
 	// bit racy. It's possible that multiple calls to create() will run
 	// concurrently and that we'll end up with too many instances.
-	if n, err := serverInstances(email); err != nil {
+	if n, err := serverInstances(ctx, email); err != nil {
 		return "", err
 	} else if len(n) >= maxInstancesPerUserFlag {
 		return "", verror.New(errLimitExceeded, ctx, maxInstancesPerUserFlag)
 	}
 
-	if n, err := serverInstances(""); err != nil {
+	if n, err := serverInstances(ctx, ""); err != nil {
 		return "", err
 	} else if len(n) >= maxInstancesFlag {
 		return "", verror.New(errGlobalLimitExceeded, ctx, maxInstancesFlag)
@@ -103,7 +104,7 @@
 	}
 	mName := mountNameFromKubeName(ctx, kName)
 
-	cfg, cleanup, err := createDeploymentConfig(ctx, email, kName, mName)
+	cfg, cleanup, err := createDeploymentConfig(ctx, email, kName, mName, baseBlessingNames)
 	defer cleanup()
 	if err != nil {
 		return "", err
@@ -135,11 +136,11 @@
 func destroy(ctx *context.T, email, mName string) error {
 	kName := kubeNameFromMountName(mName)
 
-	if err := isOwnerOfInstance(email, kName); err != nil {
+	if err := isOwnerOfInstance(ctx, email, kName); err != nil {
 		return err
 	}
 
-	cfg, cleanup, err := createDeploymentConfig(ctx, email, kName, mName)
+	cfg, cleanup, err := createDeploymentConfig(ctx, email, kName, mName, nil)
 	defer cleanup()
 	if err != nil {
 		return err
@@ -157,23 +158,31 @@
 }
 
 func list(ctx *context.T, email string) ([]serverInstance, error) {
-	instances, err := serverInstances(email)
+	instances, err := serverInstances(ctx, email)
 	if err != nil {
 		return nil, err
 	}
 	for i, instance := range instances {
-		instances[i].mountName = mountNameFromKubeName(ctx, instance.name)
+		// TODO(rthellend): Remove when all instances have the new
+		// creatorInfo annotations.
+		if instances[i].mountName == "" {
+			instances[i].mountName = mountNameFromKubeName(ctx, instance.name)
+		}
 	}
 	return instances, err
 }
 
-func createDeploymentConfig(ctx *context.T, email, deploymentName, mountName string) (string, func(), error) {
+func createDeploymentConfig(ctx *context.T, email, deploymentName, mountName string, baseBlessingNames []string) (string, func(), error) {
 	cleanup := func() {}
 	acl, err := accessList(ctx, email)
 	if err != nil {
 		return "", cleanup, err
 	}
-	creatorInfo, err := creatorInfo(ctx, email)
+	blessingNames := make([]string, len(baseBlessingNames))
+	for i, b := range baseBlessingNames {
+		blessingNames[i] = b + security.ChainSeparator + deploymentName
+	}
+	creatorInfo, err := creatorInfo(ctx, email, mountName, blessingNames)
 	if err != nil {
 		return "", cleanup, err
 	}
@@ -239,18 +248,21 @@
 	return string(j[1 : len(j)-1]), nil
 }
 
+type creatorInfoData struct {
+	Email         string   `json:"email"`
+	BlessingNames []string `json:"blessingNames"`
+	MountName     string   `json:"mountName"`
+}
+
 // creatorInfo returns a double encoded JSON access list that can be used as
 // annotation in a Deployment template, e.g.
 //   "annotations": {
-//     "v.io/allocatord/creator-info": {{.CreatorInfo}}
+//     "v.io/allocator-creator-info": {{.CreatorInfo}}
 //   }
-func creatorInfo(ctx *context.T, email string) (string, error) {
-	info := struct {
-		Email string `json:"email"`
-	}{email}
-	j, err := json.Marshal(info)
+func creatorInfo(ctx *context.T, email, mountName string, blessingNames []string) (string, error) {
+	j, err := json.Marshal(creatorInfoData{email, blessingNames, mountName})
 	if err != nil {
-		ctx.Errorf("json.Marshal(%#v) failed: %v", info, err)
+		ctx.Errorf("json.Marshal() failed: %v", err)
 		return "", err
 	}
 	// JSON encode again, because the annotation is in a JSON template.
@@ -263,22 +275,24 @@
 	return string(j), nil
 }
 
+func decodeCreatorInfo(s string) (data creatorInfoData, err error) {
+	err = json.Unmarshal([]byte(s), &data)
+	return
+}
+
 func emailHash(email string) string {
 	h := sha1.Sum([]byte(email))
 	return hex.EncodeToString(h[:])
 }
 
 type serverInstance struct {
-	name         string
-	mountName    string
-	creationTime time.Time
+	name          string
+	mountName     string
+	blessingNames []string
+	creationTime  time.Time
 }
 
-func serverInstances(email string) ([]serverInstance, error) {
-	// TODO(caprita): Store the mount name and server blessing name(s) as
-	// annotations in the deployment, and retrieve them here.  This should
-	// ensure that the values are accurate, and do not depend on the version
-	// of allocatord that is presenting them.
+func serverInstances(ctx *context.T, email string) ([]serverInstance, error) {
 	args := []string{"kubectl", "get", "deployments", "-o", "json"}
 	if email != "" {
 		args = append(args, "-l", "ownerHash="+emailHash(email))
@@ -299,8 +313,9 @@
 	var list struct {
 		Items []struct {
 			Metadata struct {
-				Name         string    `json:"name"`
-				CreationTime time.Time `json:"creationTimestamp"`
+				Name         string            `json:"name"`
+				CreationTime time.Time         `json:"creationTimestamp"`
+				Annotations  map[string]string `json:"annotations"`
 			} `json:"metadata"`
 		} `json:"items"`
 	}
@@ -309,18 +324,26 @@
 	}
 	instances := []serverInstance{}
 	for _, l := range list.Items {
-		if strings.HasPrefix(l.Metadata.Name, serverNameFlag+"-") {
-			instances = append(instances, serverInstance{
-				name:         l.Metadata.Name,
-				creationTime: l.Metadata.CreationTime,
-			})
+		if !strings.HasPrefix(l.Metadata.Name, serverNameFlag+"-") {
+			continue
 		}
+		cInfo, err := decodeCreatorInfo(l.Metadata.Annotations["v.io/allocator-creator-info"])
+		if err != nil {
+			ctx.Errorf("decodeCreatorInfo failed: %v", err)
+			continue
+		}
+		instances = append(instances, serverInstance{
+			name:          l.Metadata.Name,
+			mountName:     cInfo.MountName,
+			blessingNames: cInfo.BlessingNames,
+			creationTime:  l.Metadata.CreationTime,
+		})
 	}
 	return instances, nil
 }
 
-func isOwnerOfInstance(email, kName string) error {
-	instances, err := serverInstances(email)
+func isOwnerOfInstance(ctx *context.T, email, kName string) error {
+	instances, err := serverInstances(ctx, email)
 	if err != nil {
 		return err
 	}