github.com/openshift/installer@v1.4.17/cmd/openshift-install/graph.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"reflect"
     8  	"regexp"
     9  
    10  	"github.com/awalterschulze/gographviz"
    11  	"github.com/sirupsen/logrus"
    12  	"github.com/spf13/cobra"
    13  
    14  	"github.com/openshift/installer/pkg/asset"
    15  )
    16  
    17  var (
    18  	graphOpts struct {
    19  		outputFile string
    20  	}
    21  )
    22  
    23  func newGraphCmd() *cobra.Command {
    24  	cmd := &cobra.Command{
    25  		Use:   "graph",
    26  		Short: "Outputs the internal dependency graph for installer",
    27  		Long:  "",
    28  		Args:  cobra.ExactArgs(0),
    29  		RunE: func(cmd *cobra.Command, args []string) error {
    30  			return runGraphCmd(cmd, args, targets)
    31  		},
    32  	}
    33  	cmd.PersistentFlags().StringVar(&graphOpts.outputFile, "output-file", "", "file where the graph is written, if empty prints the graph to Stdout.")
    34  	return cmd
    35  }
    36  
    37  func runGraphCmd(cmd *cobra.Command, args []string, cmdTargets []target) error {
    38  	g := gographviz.NewGraph()
    39  	g.SetName("G")
    40  	g.SetDir(true)
    41  	g.SetStrict(true)
    42  
    43  	tNodeAttr := map[string]string{
    44  		string(gographviz.Shape): "box",
    45  		string(gographviz.Style): "filled",
    46  	}
    47  	for _, t := range cmdTargets {
    48  		name := fmt.Sprintf("%q", fmt.Sprintf("Target %s", t.name))
    49  		g.AddNode("G", name, tNodeAttr)
    50  		for _, dep := range t.assets {
    51  			addEdge(g, name, dep)
    52  		}
    53  	}
    54  
    55  	g.AddAttr("G", "rankdir", "LR")
    56  	r := regexp.MustCompile(`[. ]`)
    57  	for _, node := range g.Nodes.Nodes {
    58  		cluster := r.Split(node.Name, -1)[0][1:]
    59  		subgraphName := "cluster_" + cluster
    60  		_, ok := g.SubGraphs.SubGraphs[subgraphName]
    61  		if !ok {
    62  			g.AddSubGraph("G", subgraphName, map[string]string{"label": cluster})
    63  		}
    64  		g.AddNode(subgraphName, node.Name, nil)
    65  	}
    66  
    67  	out := os.Stdout
    68  	if graphOpts.outputFile != "" {
    69  		f, err := os.Create(graphOpts.outputFile)
    70  		if err != nil {
    71  			return err
    72  		}
    73  		defer f.Close()
    74  		out = f
    75  	}
    76  
    77  	if _, err := io.WriteString(out, g.String()); err != nil {
    78  		return err
    79  	}
    80  	return nil
    81  }
    82  
    83  func addEdge(g *gographviz.Graph, parent string, asset asset.Asset) {
    84  	name := fmt.Sprintf("%q", reflect.TypeOf(asset).Elem())
    85  
    86  	if !g.IsNode(name) {
    87  		logrus.Debugf("adding node %s", name)
    88  		g.AddNode("G", name, nil)
    89  	}
    90  	if !isEdge(g, name, parent) {
    91  		logrus.Debugf("adding edge %s -> %s", name, parent)
    92  		g.AddEdge(name, parent, true, nil)
    93  	}
    94  
    95  	deps := asset.Dependencies()
    96  	for _, dep := range deps {
    97  		addEdge(g, name, dep)
    98  	}
    99  }
   100  
   101  func isEdge(g *gographviz.Graph, src, dst string) bool {
   102  	for _, edge := range g.Edges.Edges {
   103  		if edge.Src == src && edge.Dst == dst {
   104  			return true
   105  		}
   106  	}
   107  	return false
   108  }