recipes/beyla-service-graph/graphgen/internal/graph.go (76 lines of code) (raw):

// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package internal import ( "io" "log/slog" "github.com/goccy/go-graphviz" "github.com/goccy/go-graphviz/cgraph" ) type Node struct { Ip string Name string } type Graph struct { // Adjacency list where nodes are IP address strings and values are the nodes. Each key called the value. adjacencies map[string][]string nodes map[string]*Node } func NewGraph() *Graph { return &Graph{ adjacencies: make(map[string][]string), nodes: make(map[string]*Node), } } func (g Graph) AddEdge(client, server *Node) { g.nodes[client.Ip] = client g.nodes[server.Ip] = server g.adjacencies[client.Ip] = append(g.adjacencies[client.Ip], server.Ip) } func (g Graph) Render(writer io.Writer) error { gv := graphviz.New() graph, err := gv.Graph() if err != nil { return err } defer func() { if err := graph.Close(); err != nil { slog.Error("error closing graph", "err", err) } gv.Close() }() nodes := map[*Node]*cgraph.Node{} // add nodes and edges for clientIp, servers := range g.adjacencies { clientNode, err := getCNode(g.nodes[clientIp], graph, nodes) if err != nil { return err } for _, serverIp := range servers { serverNode, err := getCNode(g.nodes[serverIp], graph, nodes) if err != nil { return err } _, err = graph.CreateEdge("", clientNode, serverNode) // TODO: set weight in label // e.SetLabel("e") if err != nil { return err } } } if err := gv.Render(graph, graphviz.SVG, writer); err != nil { return err } return nil } func getCNode(node *Node, graph *cgraph.Graph, nodes map[*Node]*cgraph.Node) (*cgraph.Node, error) { if node, ok := nodes[node]; ok { return node, nil } cnode, err := graph.CreateNode(node.Ip) if err != nil { return nil, err } cnode.SetTooltip(node.Ip) if node.Name != "" { cnode.SetLabel(node.Name) } cnode.SetOrdering(cgraph.OutOrdering) nodes[node] = cnode return cnode, nil }