-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
121 lines (104 loc) · 3.37 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"strings"
"sync"
"cloud.google.com/go/resourcemanager/apiv3"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
resourcemanagerpb "google.golang.org/genproto/googleapis/cloud/resourcemanager/v3"
)
type Folder struct {
Name string `json:"name"`
ID string `json:"id"`
Children []*Folder `json:"children,omitempty"`
Projects []*resourcemanagerpb.Project `json:"projects,omitempty"`
}
func collectProjects(ctx context.Context, client *resourcemanager.ProjectsClient, folderID string, folder *Folder) {
req := &resourcemanagerpb.ListProjectsRequest{Parent: "folders/" + folderID}
it := client.ListProjects(ctx, req)
for {
proj, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to list projects: %v", err)
}
folder.Projects = append(folder.Projects, proj)
}
}
func buildTreeConcurrently(ctx context.Context, client *resourcemanager.FoldersClient, projectClient *resourcemanager.ProjectsClient, folderID string, folderChan chan<- *Folder, wg *sync.WaitGroup) {
defer wg.Done()
folder := &Folder{}
folder.ID = folderID
resp, err := client.GetFolder(ctx, &resourcemanagerpb.GetFolderRequest{Name: "folders/" + folderID})
if err != nil {
log.Fatalf("Failed to get folder: %v", err)
}
folder.Name = resp.Name[len("folders/"):]
req := &resourcemanagerpb.ListFoldersRequest{Parent: "folders/" + folderID}
it := client.ListFolders(ctx, req)
var childWg sync.WaitGroup
for {
subFolder, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to list sub-folders: %v", err)
}
childChan := make(chan *Folder, 1)
childWg.Add(1)
go buildTreeConcurrently(ctx, client, projectClient, subFolder.Name[len("folders/"):], childChan, &childWg)
child := <-childChan
folder.Children = append(folder.Children, child)
}
childWg.Wait()
collectProjects(ctx, projectClient, folderID, folder)
folderChan <- folder
}
func main() {
keyFile := flag.String("key-file", "", "path to GCP ServiceAccount JSON file")
folderIDs := flag.String("folders", "", "comma-separated list of folder IDs")
flag.Parse()
if *keyFile == "" {
log.Fatal("Missing required argument --key-file")
}
if *folderIDs == "" {
log.Fatal("Missing required argument --folders")
}
folders := strings.Split(*folderIDs, ",")
ctx := context.Background()
folderClient, err := resourcemanager.NewFoldersClient(ctx, option.WithCredentialsFile(*keyFile))
if err != nil {
log.Fatalf("Failed to create folder client: %v", err)
}
defer folderClient.Close()
projectClient, err := resourcemanager.NewProjectsClient(ctx, option.WithCredentialsFile(*keyFile))
if err != nil {
log.Fatalf("Failed to create project client: %v", err)
}
defer projectClient.Close()
folderChan := make(chan *Folder, len(folders))
var wg sync.WaitGroup
for _, folderID := range folders {
wg.Add(1)
go buildTreeConcurrently(ctx, folderClient, projectClient, folderID, folderChan, &wg)
}
wg.Wait()
close(folderChan)
var rootFolders []*Folder
for folder := range folderChan {
rootFolders = append(rootFolders, folder)
}
jsonResult, err := json.MarshalIndent(rootFolders, "", " ")
if err != nil {
log.Fatalf("Failed to marshal JSON result: %v", err)
}
fmt.Println(string(jsonResult))
}