// NewGraphFromConfig returns a new graph from existing input, such as from the // existing graph, and a GraphConfig struct. func (c *GraphConfig) NewGraphFromConfig(g *pgraph.Graph, embdEtcd *etcd.EmbdEtcd, noop bool) (*pgraph.Graph, error) { if c.Hostname == "" { return nil, fmt.Errorf("Config: Error: Hostname can't be empty!") } var graph *pgraph.Graph // new graph to return if g == nil { // FIXME: how can we check for an empty graph? graph = pgraph.NewGraph("Graph") // give graph a default name } else { graph = g.Copy() // same vertices, since they're pointers! } var lookup = make(map[string]map[string]*pgraph.Vertex) //log.Printf("%+v", config) // debug // TODO: if defined (somehow)... graph.SetName(c.Graph) // set graph name var keep []*pgraph.Vertex // list of vertex which are the same in new graph var resourceList []resources.Res // list of resources to export // use reflection to avoid duplicating code... better options welcome! value := reflect.Indirect(reflect.ValueOf(c.Resources)) vtype := value.Type() for i := 0; i < vtype.NumField(); i++ { // number of fields in struct name := vtype.Field(i).Name // string of field name field := value.FieldByName(name) iface := field.Interface() // interface type of value slice := reflect.ValueOf(iface) // XXX: should we just drop these everywhere and have the kind strings be all lowercase? kind := util.FirstToUpper(name) if global.DEBUG { log.Printf("Config: Processing: %v...", kind) } for j := 0; j < slice.Len(); j++ { // loop through resources of same kind x := slice.Index(j).Interface() res, ok := x.(resources.Res) // convert to Res type if !ok { return nil, fmt.Errorf("Config: Error: Can't convert: %v of type: %T to Res.", x, x) } if noop { res.Meta().Noop = noop } if _, exists := lookup[kind]; !exists { lookup[kind] = make(map[string]*pgraph.Vertex) } // XXX: should we export based on a @@ prefix, or a metaparam // like exported => true || exported => (host pattern)||(other pattern?) if !strings.HasPrefix(res.GetName(), "@@") { // not exported resource // XXX: we don't have a way of knowing if any of the // metaparams are undefined, and as a result to set the // defaults that we want! I hate the go yaml parser!!! v := graph.GetVertexMatch(res) if v == nil { // no match found res.Init() v = pgraph.NewVertex(res) graph.AddVertex(v) // call standalone in case not part of an edge } lookup[kind][res.GetName()] = v // used for constructing edges keep = append(keep, v) // append } else if !noop { // do not export any resources if noop // store for addition to etcd storage... res.SetName(res.GetName()[2:]) //slice off @@ res.SetKind(kind) // cheap init resourceList = append(resourceList, res) } } } // store in etcd if err := etcd.EtcdSetResources(embdEtcd, c.Hostname, resourceList); err != nil { return nil, fmt.Errorf("Config: Could not export resources: %v", err) } // lookup from etcd var hostnameFilter []string // empty to get from everyone kindFilter := []string{} for _, t := range c.Collector { // XXX: should we just drop these everywhere and have the kind strings be all lowercase? kind := util.FirstToUpper(t.Kind) kindFilter = append(kindFilter, kind) } // do all the graph look ups in one single step, so that if the etcd // database changes, we don't have a partial state of affairs... if len(kindFilter) > 0 { // if kindFilter is empty, don't need to do lookups! var err error resourceList, err = etcd.EtcdGetResources(embdEtcd, hostnameFilter, kindFilter) if err != nil { return nil, fmt.Errorf("Config: Could not collect resources: %v", err) } } for _, res := range resourceList { matched := false // see if we find a collect pattern that matches for _, t := range c.Collector { // XXX: should we just drop these everywhere and have the kind strings be all lowercase? kind := util.FirstToUpper(t.Kind) // use t.Kind and optionally t.Pattern to collect from etcd storage log.Printf("Collect: %v; Pattern: %v", kind, t.Pattern) // XXX: expand to more complex pattern matching here... if res.Kind() != kind { continue } if matched { // we've already matched this resource, should we match again? log.Printf("Config: Warning: Matching %v[%v] again!", kind, res.GetName()) } matched = true // collect resources but add the noop metaparam if noop { res.Meta().Noop = noop } if t.Pattern != "" { // XXX: simplistic for now res.CollectPattern(t.Pattern) // res.Dirname = t.Pattern } log.Printf("Collect: %v[%v]: collected!", kind, res.GetName()) // XXX: similar to other resource add code: if _, exists := lookup[kind]; !exists { lookup[kind] = make(map[string]*pgraph.Vertex) } v := graph.GetVertexMatch(res) if v == nil { // no match found res.Init() // initialize go channels or things won't work!!! v = pgraph.NewVertex(res) graph.AddVertex(v) // call standalone in case not part of an edge } lookup[kind][res.GetName()] = v // used for constructing edges keep = append(keep, v) // append //break // let's see if another resource even matches } } // get rid of any vertices we shouldn't "keep" (that aren't in new graph) for _, v := range graph.GetVertices() { if !pgraph.VertexContains(v, keep) { // wait for exit before starting new graph! v.SendEvent(event.EventExit, true, false) graph.DeleteVertex(v) } } for _, e := range c.Edges { if _, ok := lookup[util.FirstToUpper(e.From.Kind)]; !ok { return nil, fmt.Errorf("Can't find 'from' resource!") } if _, ok := lookup[util.FirstToUpper(e.To.Kind)]; !ok { return nil, fmt.Errorf("Can't find 'to' resource!") } if _, ok := lookup[util.FirstToUpper(e.From.Kind)][e.From.Name]; !ok { return nil, fmt.Errorf("Can't find 'from' name!") } if _, ok := lookup[util.FirstToUpper(e.To.Kind)][e.To.Name]; !ok { return nil, fmt.Errorf("Can't find 'to' name!") } graph.AddEdge(lookup[util.FirstToUpper(e.From.Kind)][e.From.Name], lookup[util.FirstToUpper(e.To.Kind)][e.To.Name], pgraph.NewEdge(e.Name)) } return graph, nil }