// WatchForFinishedTestRuns simply watches the local cache for any test runs that have test data. // It will periodically look at the table and send any present test data back to the server as a response. // When the server has successfully received this data, it will send a task back to this specific agent // to delete this row from the cache. func WatchForFinishedTestRuns(cfg config.Config) { var ac = cache.NewAgentCache(cfg) agentUuid := ac.GetKeyValue("uuid") for { time.Sleep(5000 * time.Millisecond) testruns, err := ac.GetFinishedTestRuns() if err != nil { log.Error("Problem retrieving finished test runs") os.Exit(1) } for testUuid, testData := range testruns { log.Debug("Found ripe testrun: ", testUuid) var utdr = responses.UploadTestDataResponse{ TestUuid: testUuid, TestData: testData, } utdr.AgentUuid = agentUuid utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? var tc = comms.NewToDDComms(cfg) tc.CommsPackage.SendResponse(utdr) } } }
func main() { todd_version := "0.0.1" cfg := config.GetConfig(arg_config) // Start serving collectors and testlets, and retrieve map of names and hashes assets := serveAssets(cfg) // Perform database initialization tasks tdb, err := db.NewToddDB(cfg) if err != nil { log.Fatalf("Error setting up database: %v\n", err) } if err := tdb.Init(); err != nil { log.Fatalf("Error initializing database: %v\n", err) } // Initialize API var tapi toddapi.ToDDApi go func() { log.Fatal(tapi.Start(cfg)) }() // Start listening for agent advertisements var tc = comms.NewToDDComms(cfg) go tc.CommsPackage.ListenForAgent(assets) // Kick off group calculation in background go func() { for { log.Info("Beginning group calculation") grouping.CalculateGroups(cfg) time.Sleep(time.Second * time.Duration(cfg.Grouping.Interval)) } }() log.Infof("ToDD server v%s. Press any key to exit...\n", todd_version) // Sssh, sssh, only dreams now.... for { time.Sleep(time.Second * 10) } }
// CalculateGroups is a function designed to ingest a list of group objects, a collection of agents, and // return a map that contains the resulting group for each agent UUID. func CalculateGroups(cfg config.Config) { tdb, err := db.NewToddDB(cfg) if err != nil { log.Fatalf("Error connecting to DB: %v", err) } // Retrieve all currently active agents agents, err := tdb.GetAgents() if err != nil { log.Fatalf("Error retrieving agents: %v", err) } // Retrieve all objects with type "group" group_objs, err := tdb.GetObjects("group") if err != nil { log.Fatalf("Error retrieving groups: %v", err) } // Cast retrieved slice of ToddObject interfaces to actual GroupObjects groups := make([]objects.GroupObject, len(group_objs)) for i, gobj := range group_objs { groups[i] = gobj.(objects.GroupObject) } // groupmap contains the uuid-to-groupname mappings to be used for test runs groupmap := map[string]string{} // This slice will hold all of the agents that are sad because they didn't get into a group var lonelyAgents []defs.AgentAdvert next: for x := range agents { for i := range groups { // See if this agent is in this group if isInGroup(groups[i].Spec.Matches, agents[x].Facts) { // Insert this group name ("Label") into groupmap under the key of the UUID for the agent that belongs to it log.Debugf("Agent %s is in group %s\n", agents[x].Uuid, groups[i].Label) groupmap[agents[x].Uuid] = groups[i].Label continue next } } // The "continue next" should prohibit all agents that have a group from getting to this point, // so the only ones left do not have a group. lonelyAgents = append(lonelyAgents, agents[x]) } // Write results to database err = tdb.SetGroupMap(groupmap) if err != nil { log.Fatalf("Error setting group map: %v", err) } // Send notifications to each agent to let them know what group they're in, so they can cache it var tc = comms.NewToDDComms(cfg) for uuid, groupName := range groupmap { setGroupTask := tasks.SetGroupTask{ GroupName: groupName, } setGroupTask.Type = "SetGroup" //TODO(mierdin): Apparently this is necessary because inner type promotion doesn't apply for struct literals? tc.CommsPackage.SendTask(uuid, setGroupTask) } // need to send a message to all agents that weren't in groupmap to set their group to nothing for x := range lonelyAgents { setGroupTask := tasks.SetGroupTask{ GroupName: "", } setGroupTask.Type = "SetGroup" //TODO(mierdin): Apparently this is necessary because inner type promotion doesn't apply for struct literals? tc.CommsPackage.SendTask(lonelyAgents[x].Uuid, setGroupTask) } }
func main() { cfg := config.GetConfig(arg_config) // Set up cache var ac = cache.NewAgentCache(cfg) ac.Init() // Generate UUID uuid := hostresources.GenerateUuid() ac.SetKeyValue("uuid", uuid) log.Infof("ToDD Agent Activated: %s", uuid) // Start test data reporting service go testing.WatchForFinishedTestRuns(cfg) // Construct comms package var tc = comms.NewToDDComms(cfg) // Spawn goroutine to listen for tasks issued by server go func() { for { err := tc.CommsPackage.ListenForTasks(uuid) if err != nil { log.Warn("ListenForTasks reported a failure. Trying again...") } } }() // Watch for changes to group membership go tc.CommsPackage.WatchForGroup() // Continually advertise agent status into message queue for { // Gather assets here as a map, and refer to a key in that map in the below struct gatheredAssets := GetLocalAssets(cfg) var defaultaddr string if cfg.LocalResources.IPAddrOverride != "" { defaultaddr = cfg.LocalResources.IPAddrOverride } else { defaultaddr = hostresources.GetIPOfInt(cfg.LocalResources.DefaultInterface).String() } // Create an AgentAdvert instance to represent this particular agent me := defs.AgentAdvert{ Uuid: uuid, DefaultAddr: defaultaddr, FactCollectors: gatheredAssets["factcollectors"], Testlets: gatheredAssets["testlets"], Facts: facts.GetFacts(cfg), LocalTime: time.Now().UTC(), } // Advertise this agent err := tc.CommsPackage.AdvertiseAgent(me) if err != nil { log.Error("Failed to advertise agent after several retries") } time.Sleep(15 * time.Second) // TODO(moswalt): make configurable } }