func()

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
}