Index: appengine/logdog/coordinator/project.go |
diff --git a/appengine/logdog/coordinator/project.go b/appengine/logdog/coordinator/project.go |
index 0553620a5bfe27caa9a059bcde0c247b9fd94e46..47defc6c9bc77573382e4000a7bc159ccf967146 100644 |
--- a/appengine/logdog/coordinator/project.go |
+++ b/appengine/logdog/coordinator/project.go |
@@ -7,20 +7,28 @@ package coordinator |
import ( |
"strings" |
- "github.com/luci/gae/service/datastore/meta" |
"github.com/luci/gae/service/info" |
- "github.com/luci/luci-go/common/config" |
+ "github.com/luci/luci-go/appengine/logdog/coordinator/config" |
+ luciConfig "github.com/luci/luci-go/common/config" |
+ log "github.com/luci/luci-go/common/logging" |
+ "github.com/luci/luci-go/common/parallel" |
"github.com/luci/luci-go/common/proto/logdog/svcconfig" |
"golang.org/x/net/context" |
nodir
2016/05/19 00:38:58
should be in a separate import block
|
) |
-// projectNamespacePrefix is the datastore namespace prefix for project |
-// namespaces. |
-const projectNamespacePrefix = "luci." |
+const ( |
+ // projectNamespacePrefix is the datastore namespace prefix for project |
+ // namespaces. |
+ projectNamespacePrefix = "luci." |
nodir
2016/05/19 00:38:58
I'd definitely use "project." so namespaces are "p
|
+ |
+ // projectConfigWorkers is the number of workers that will pull project |
+ // configs from the config service. |
+ projectConfigWorkers = 16 |
+) |
// ProjectNamespace returns the AppEngine namespace for a given luci-config |
// project name. |
-func ProjectNamespace(project config.ProjectName) string { |
+func ProjectNamespace(project luciConfig.ProjectName) string { |
return projectNamespacePrefix + string(project) |
} |
@@ -29,11 +37,11 @@ func ProjectNamespace(project config.ProjectName) string { |
// |
// If the namespace does not have a project namespace prefix, this function |
// will return an empty string. |
-func ProjectFromNamespace(ns string) config.ProjectName { |
+func ProjectFromNamespace(ns string) luciConfig.ProjectName { |
if !strings.HasPrefix(ns, projectNamespacePrefix) { |
return "" |
} |
- return config.ProjectName(ns[len(projectNamespacePrefix):]) |
+ return luciConfig.ProjectName(ns[len(projectNamespacePrefix):]) |
} |
// CurrentProject returns the current project based on the currently-loaded |
@@ -41,7 +49,7 @@ func ProjectFromNamespace(ns string) config.ProjectName { |
// |
// If there is no current namespace, or if the current namespace is not a valid |
// project namespace, an empty string will be returned. |
-func CurrentProject(c context.Context) config.ProjectName { |
+func CurrentProject(c context.Context) luciConfig.ProjectName { |
if ns, ok := info.Get(c).GetNamespace(); ok { |
return ProjectFromNamespace(ns) |
} |
@@ -57,18 +65,67 @@ func CurrentProjectConfig(c context.Context) (*svcconfig.ProjectConfig, error) { |
return GetServices(c).ProjectConfig(c, CurrentProject(c)) |
} |
-// AllProjectsWithNamespaces scans current namespaces and returns those that |
-// belong to LUCI projects. |
-func AllProjectsWithNamespaces(c context.Context) ([]config.ProjectName, error) { |
- var projects []config.ProjectName |
- err := meta.NamespacesWithPrefix(c, projectNamespacePrefix, func(ns string) error { |
- if proj := ProjectFromNamespace(ns); proj != "" { |
- projects = append(projects, proj) |
+// ActiveProjects returns a full list of all config service projects with LogDog |
+// project configurations. |
+// |
+// If userOnly is true, any project not accessible by the current user will not |
+// be returned. |
+func ActiveProjects(c context.Context, userOnly bool) ([]luciConfig.ProjectName, error) { |
+ projects, err := config.Projects(c) |
+ if err != nil { |
+ return nil, err |
+ } |
+ log.Infof(c, "All projects: %v", projects) |
+ |
+ svcs := GetServices(c) |
+ err = parallel.WorkPool(projectConfigWorkers, func(taskC chan<- func() error) { |
+ for i := range projects { |
+ i := i |
+ taskC <- func() error { |
+ var err error |
+ if userOnly { |
+ // Verify user access. This includes loading the project's config. |
+ err = IsProjectReader(c, projects[i]) |
+ } else { |
+ _, err = svcs.ProjectConfig(c, projects[i]) |
+ } |
+ |
+ switch { |
+ case err == nil: |
+ // Project config loaded and (if requested) user had access. |
+ return nil |
+ |
+ case err == luciConfig.ErrNoConfig, err == config.ErrInvalidConfig, IsMembershipError(err): |
+ // No configuration for this project, the configuration is invalid, or |
+ // the user didn't have access. Remove it from the list. |
+ projects[i] = "" |
+ return nil |
+ |
+ default: |
+ log.Fields{ |
+ log.ErrorKey: err, |
+ "project": projects[i], |
+ "userOnly": userOnly, |
+ }.Errorf(c, "Failed to check project.") |
+ return err |
+ } |
+ } |
} |
- return nil |
}) |
if err != nil { |
+ log.WithError(err).Errorf(c, "Failed to get project list.") |
return nil, err |
} |
+ |
+ // Remove any projects that have been blanked out. |
+ cidx := 0 |
+ for _, proj := range projects { |
+ if proj != "" { |
+ projects[cidx] = proj |
+ cidx++ |
+ } |
+ } |
+ projects = projects[:cidx] |
+ log.Infof(c, "Pruned projects: %v", projects) |
return projects, nil |
} |