// DependencyGraph builds a dependency graph for the collection func (c Collection) DependencyGraph() (*graph.Graph, error) { g := graph.New() // A map containing the resource ids and their nodes in the graph nodes := make(map[string]*graph.Node) for id := range c { node := graph.NewNode(id) nodes[id] = node g.AddNode(node) } // Connect the nodes in the graph for id, r := range c { before := r.GetBefore() after := r.GetAfter() // Connect current resource with the ones that happen after it for _, dep := range after { if _, ok := c[dep]; !ok { return g, fmt.Errorf("%s wants %s, which does not exist", id, dep) } g.AddEdge(nodes[id], nodes[dep]) } // Connect current resource with the ones that happen before it for _, dep := range before { if _, ok := c[dep]; !ok { return g, fmt.Errorf("%s wants %s, which does not exist", id, dep) } g.AddEdge(nodes[dep], nodes[id]) } } return g, nil }
// DependencyGraph builds a dependency graph for the collection func (c Collection) DependencyGraph() (*graph.Graph, error) { g := graph.New() // A map containing the resource ids and their nodes in the graph nodes := make(map[string]*graph.Node) for id := range c { node := graph.NewNode(id) nodes[id] = node g.AddNode(node) } // Connect the nodes in the graph for id, r := range c { // Create edges between the nodes and the ones // required by it for _, dep := range r.Dependencies() { if _, ok := c[dep]; !ok { return g, fmt.Errorf("%s wants %s, which does not exist", id, dep) } g.AddEdge(nodes[id], nodes[dep]) } // Create edges between the nodes and the resources for // which we subscribe for changes to for dep := range r.SubscribedTo() { if _, ok := c[dep]; !ok { return g, fmt.Errorf("%s subscribes to %s, which does not exist", id, dep) } g.AddEdge(nodes[id], nodes[dep]) } } return g, nil }
// New creates a new empty catalog with the provided configuration func New(config *Config) *Catalog { c := &Catalog{ config: config, collection: make(resource.Collection), sorted: make([]*graph.Node, 0), reversed: graph.New(), status: &Status{ Items: make(map[string]*StatusItem), }, Unsorted: make([]resource.Resource, 0), } // Inject the configuration for resources resource.DefaultConfig = &resource.Config{ Logger: config.Logger, SiteRepo: config.SiteRepo, } // Register the catalog type in Lua and also register // metamethods for the catalog, so that we can use // the catalog in a more Lua-friendly way mt := luar.MT(config.L, c) mt.RawSetString("__len", luar.New(config.L, (*Catalog).luaLen)) config.L.SetGlobal("catalog", luar.New(config.L, c)) return c }
// Executes the "graph" command func execGraphCommand(c *cli.Context) error { if len(c.Args()) < 1 { return cli.NewExitError(errNoModuleName.Error(), 64) } L := lua.NewState() defer L.Close() module := c.Args()[0] config := &catalog.Config{ Module: module, DryRun: true, Logger: log.New(os.Stdout, "", log.LstdFlags), SiteRepo: c.String("siterepo"), L: L, } katalog := catalog.New(config) resource.LuaRegisterBuiltin(L) if err := L.DoFile(module); err != nil { return cli.NewExitError(err.Error(), 1) } collection, err := resource.CreateCollection(katalog.Unsorted) if err != nil { return cli.NewExitError(err.Error(), 1) } g, err := collection.DependencyGraph() if err != nil { return cli.NewExitError(err.Error(), 1) } g.AsDot("resources", os.Stdout) g.Reversed().AsDot("reversed", os.Stdout) sorted, err := g.Sort() if err == graph.ErrCircularDependency { circular := graph.New() circular.AddNode(sorted...) circular.AsDot("circular", os.Stdout) return cli.NewExitError(graph.ErrCircularDependency.Error(), 1) } return nil }