diff --git a/stackdriver_exporter.go b/stackdriver_exporter.go index 760cb71b..cebb87d0 100644 --- a/stackdriver_exporter.go +++ b/stackdriver_exporter.go @@ -57,6 +57,10 @@ var ( "google.project-id", "Comma seperated list of Google Project IDs.", ).String() + projectsQuery = kingpin.Flag( + "google.projects.filter", "Google projects search query.", + ).String() + stackdriverMaxRetries = kingpin.Flag( "stackdriver.max-retries", "Max number of retries that should be attempted on 503 errors from stackdriver.", ).Default("0").Int() @@ -261,8 +265,8 @@ func main() { logger := promlog.New(promlogConfig) ctx := context.Background() - if *projectID == "" { - level.Info(logger).Log("msg", "no projectID was provided. Trying to discover it") + if *projectID == "" && *projectsQuery == "" { + level.Info(logger).Log("msg", "Neither projectID nor projectsQuery was provided. Trying to discover it") var err error projectID, err = getDefaultGCPProject(ctx) if err != nil { @@ -273,7 +277,6 @@ func main() { level.Info(logger).Log("msg", "Starting stackdriver_exporter", "version", version.Info()) level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) - level.Info(logger).Log("msg", "Using Google Cloud Project ID", "projectID", *projectID) monitoringService, err := createMonitoringService(ctx) if err != nil { @@ -281,7 +284,23 @@ func main() { os.Exit(1) } - projectIDs := strings.Split(*projectID, ",") + var projectIDs []string + + if *projectsQuery != "" { + level.Info(logger).Log("msg", "Using Google Cloud Projects Query", "projectsQuery", *projectsQuery) + projectIDs, err = utils.GetProjectIDsFromQuery(ctx, *projectsQuery) + if err != nil { + level.Error(logger).Log("msg", "failed to get project IDs from query", "err", err) + os.Exit(1) + } + } + + if *projectID != "" { + projectIDs = append(projectIDs, strings.Split(*projectID, ",")...) + } + + level.Info(logger).Log("msg", "Using Google Cloud Project IDs", "projectIDs", fmt.Sprintf("%v", projectIDs)) + metricsTypePrefixes := strings.Split(*monitoringMetricsTypePrefixes, ",") metricExtraFilters := parseMetricExtraFilters() diff --git a/utils/utils.go b/utils/utils.go index 9809bdf6..e56c18e4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -14,10 +14,12 @@ package utils import ( + "context" "regexp" "strings" "github.com/fatih/camelcase" + "google.golang.org/api/cloudresourcemanager/v1" ) var ( @@ -50,3 +52,25 @@ func GetExtraFilterModifiers(extraFilter string, separator string) (string, stri func ProjectResource(projectID string) string { return "projects/" + projectID } + +// GetProjectIDsFromQuery returns a list of project IDs from a Google Cloud organization using a query filter. +func GetProjectIDsFromQuery(ctx context.Context, query string) ([]string, error) { + var projectIDs []string + + service, err := cloudresourcemanager.NewService(ctx) + if err != nil { + return nil, err + } + + projects := service.Projects.List().Filter(query) + if err := projects.Pages(context.Background(), func(page *cloudresourcemanager.ListProjectsResponse) error { + for _, project := range page.Projects { + projectIDs = append(projectIDs, project.ProjectId) + } + return nil + }); err != nil { + return nil, err + } + + return projectIDs, nil +}