in tools/gcpviz/main.go [524:700]
func (v *GcpViz) GenerateNodes(wg *sync.WaitGroup, ctx context.Context, gizmoQuery string, parameters map[string]interface{}, out io.Writer) error {
defer wg.Done()
stats, err := v.QS.Stats(ctx, true)
if err != nil {
return err
}
v.bfilter = bloom.New(uint(20*stats.Quads.Size), 5)
fmt.Fprintf(out, "digraph GCP {\n")
for k, v := range Style.Global {
var style bytes.Buffer
styleTemplate, err := template.New("style").Parse(v)
if err != nil {
return fmt.Errorf("error parsing style template: %v", err)
}
styleTemplate.Execute(&style, parameters)
fmt.Fprintf(out, " %s [%s];\n", k, style.String())
}
for k, v := range Style.Options {
fmt.Fprintf(out, " %s=%s;\n", k, v)
}
queryTemplate, err := template.New("query").Parse(gizmoQuery)
if err != nil {
return fmt.Errorf("error parsing query template: %v", err)
}
var gizmoQ bytes.Buffer
parameters["Organizations"] = v.OrgRoots
queryTemplate.Execute(&gizmoQ, parameters)
session := gizmo.NewSession(v.QS)
it, err := session.Execute(ctx, gizmoQ.String(), query.Options{
Collation: query.Raw,
Limit: -1,
})
if err != nil {
return err
}
defer it.Close()
for it.Next(ctx) {
result := it.Result()
switch result.(type) {
case (*gizmo.Result):
case string:
log.Fatalf("Invalid result from query, expected nodes, got string: %v", result.(string))
default:
log.Fatalf("Invalid result from query, expected nodes, got: %v", result)
}
data := result.(*gizmo.Result)
var (
node string
id int64
val map[string]interface{}
)
if _, found := data.Tags[gizmo.TopResultTag]; !found {
val = data.Val.(map[string]interface{})
if _, found = val[gizmo.TopResultTag]; !found {
continue
} else {
node = val[gizmo.TopResultTag].(string)
qval, err := cquad.AsValue(node)
if !err {
continue
}
id = reflect.ValueOf(v.QS.ValueOf(qval).Key()).Int()
}
} else {
node = v.QS.NameOf(data.Tags[gizmo.TopResultTag]).Native().(string)
id = reflect.ValueOf(data.Tags[gizmo.TopResultTag]).Int()
}
var parent string = ""
var parentId int64 = -1
if _, found := data.Tags["parent"]; !found {
if _, found := val["parent"]; found {
parent = val["parent"].(string)
qval, err := cquad.AsValue(parent)
if !err {
continue
}
parentId = reflect.ValueOf(v.QS.ValueOf(qval).Key()).Int()
} else {
if !strings.HasPrefix(node, "//cloudresourcemanager.googleapis.com/") {
keys := make([]string, 0, len(data.Tags))
for key := range data.Tags {
keys = append(keys, key)
}
return errors.New(fmt.Sprintf("Node %s missing parent tag (tags: %s)", node, strings.Join(keys, ",")))
}
}
} else {
parent = v.QS.NameOf(data.Tags["parent"]).Native().(string)
parentId = reflect.ValueOf(data.Tags["parent"]).Int()
}
err := v.renderBothNodes(parent, node, parentId, id, out)
if err != nil {
fmt.Fprintf(os.Stderr, "Error rendering node (%s -> %s): %v\n", parent, node, err)
}
}
if err := it.Err(); err != nil {
return err
}
// Second pass, render edges
session = gizmo.NewSession(v.QS)
it, err = session.Execute(ctx, gizmoQ.String(), query.Options{
Collation: query.Raw,
Limit: -1,
})
if err != nil {
return err
}
defer it.Close()
for it.Next(ctx) {
data := it.Result().(*gizmo.Result)
var (
node string
id int64
val map[string]interface{}
)
if _, found := data.Tags[gizmo.TopResultTag]; !found {
val = data.Val.(map[string]interface{})
if _, found = val[gizmo.TopResultTag]; !found {
continue
} else {
node = val[gizmo.TopResultTag].(string)
qval, err := cquad.AsValue(node)
if !err {
continue
}
id = reflect.ValueOf(v.QS.ValueOf(qval).Key()).Int()
}
} else {
node = v.QS.NameOf(data.Tags[gizmo.TopResultTag]).Native().(string)
id = reflect.ValueOf(data.Tags[gizmo.TopResultTag]).Int()
}
qval, err := cquad.AsValue(node)
if !err {
continue
}
p := cayley.StartPath(v.QS, qval).In("uses")
var hadEdge bool = false
p.Iterate(nil).EachValue(v.QS, func(val cquad.Value) {
targetId := reflect.ValueOf(v.QS.ValueOf(val).Key()).Int()
tRaw := make([]byte, 8)
binary.BigEndian.PutUint64(tRaw, uint64(targetId))
sRaw := make([]byte, 8)
binary.BigEndian.PutUint64(sRaw, uint64(id))
if v.bfilter.Test(sRaw) && v.bfilter.Test(tRaw) {
target := cquad.NativeOf(val).(string)
err := v.renderEdge(target, node, targetId, id, out) // Ignore errors, because some resources might be missing
if err == nil {
hadEdge = true
}
}
})
if !hadEdge { // Disconnected item
// We may want to do something here in the future, likely forcible connect to parent to avoid floating
// resources.
}
}
if err := it.Err(); err != nil {
return err
}
fmt.Fprintf(out, "}\n")
return nil
}