func (s *ManagerTestSuite) TestStartStopManager(t *C) { /** * mm is a proxy manager for monitors, so it's always running. * It should implement the service manager interface anyway, * but it doesn't actually start or stop. Its main work is done * in Handle, starting and stopping monitors (tested later). */ mrm := mock.NewMrmsMonitor() m := mm.NewManager(s.logger, s.factory, s.clock, s.spool, s.im, mrm) if m == nil { t.Fatal("Make new mm.Manager") } // It shouldn't have added a tickChan yet. if len(s.clock.Added) != 0 { t.Error("tickChan not added yet") } // First the API marshals an mm.Config. config := &mm.Config{ ServiceInstance: proto.ServiceInstance{ Service: "mysql", InstanceId: 1, }, Collect: 1, Report: 60, // No monitor-specific config } err := pct.Basedir.WriteConfig("mm-mysql-1", config) t.Assert(err, IsNil) // The agent calls mm.Start(). err = m.Start() t.Assert(err, IsNil) // There is a monitor so there should be tickers. if ok, diff := test.IsDeeply(s.clock.Added, []uint{1}); !ok { test.Dump(s.clock.Added) t.Errorf("Does not add tickChan, got %#v", diff) } // Its status should be "Running". status := m.Status() t.Check(status["mm"], Equals, "Running") // Can't start mm twice. err = m.Start() t.Check(err, Not(Equals), "") // Stopping should be idempotent. err = m.Stop() t.Check(err, IsNil) err = m.Stop() t.Check(err, IsNil) status = m.Status() t.Check(status["mm"], Equals, "Stopped") }
func (s *TestSuite) SetUpSuite(t *C) { if dsn == "" { t.Fatal("PCT_TEST_MYSQL_DSN is not set") } // Get our own connection to MySQL. db, err := test.ConnectMySQL(dsn) if err != nil { t.Fatal(err) } s.db = db s.logChan = make(chan *proto.LogEntry, 10) s.logger = pct.NewLogger(s.logChan, "mm-manager-test") s.tickChan = make(chan time.Time) s.collectionChan = make(chan *mm.Collection, 1) s.name = "mm-mysql-db1" s.mrm = mock.NewMrmsMonitor() }
func (s *ManagerTestSuite) SetUpSuite(t *C) { s.nullmysql = mock.NewNullMySQL() s.mrmsMonitor = mock.NewMrmsMonitor() s.logChan = make(chan *proto.LogEntry, 1000) s.logger = pct.NewLogger(s.logChan, "qan-test") s.dataChan = make(chan interface{}, 2) s.spool = mock.NewSpooler(s.dataChan) var err error s.tmpDir, err = ioutil.TempDir("/tmp", "agent-test") t.Assert(err, IsNil) if err := pct.Basedir.Init(s.tmpDir); err != nil { t.Fatal(err) } s.configDir = pct.Basedir.Dir("config") s.im = instance.NewRepo(pct.NewLogger(s.logChan, "im-test"), s.configDir, s.api) data, err := json.Marshal(&proto.MySQLInstance{ Hostname: "bm-cloud-db01", Alias: "db01", DSN: "user:pass@tcp/", }) t.Assert(err, IsNil) s.im.Add("mysql", 1, data, false) s.mysqlInstance = proto.ServiceInstance{Service: "mysql", InstanceId: 1} links := map[string]string{ "agent": "http://localhost/agent", "instances": "http://localhost/instances", } s.api = mock.NewAPI("http://localhost", "http://localhost", "123", "abc-123-def", links) }
func (s *TestSuite) SetUpSuite(t *C) { s.mockMrmsMonitor = mock.NewMrmsMonitor() s.logChan = make(chan *proto.LogEntry, 1000) s.logger = pct.NewLogger(s.logChan, "mrms-test") }
func (s *ManagerTestSuite) TestGetConfig(t *C) { mrm := mock.NewMrmsMonitor() m := mm.NewManager(s.logger, s.factory, s.clock, s.spool, s.im, mrm) t.Assert(m, NotNil) err := m.Start() t.Assert(err, IsNil) /** * Start a mock MySQL monitor. */ mysqlMonitorConfig := &mysql.Config{ Config: mm.Config{ ServiceInstance: proto.ServiceInstance{ Service: "mysql", InstanceId: 1, }, Collect: 1, Report: 60, }, Status: map[string]string{ "threads_connected": "gauge", "threads_running": "gauge", }, } mysqlData, err := json.Marshal(mysqlMonitorConfig) t.Assert(err, IsNil) cmd := &proto.Cmd{ User: "******", Service: "mm", Cmd: "StartService", Data: mysqlData, } s.mysqlMonitor.SetConfig(mysqlMonitorConfig) reply := m.Handle(cmd) t.Assert(reply, NotNil) t.Assert(reply.Error, Equals, "") /** * Start a mock system monitor. */ systemMonitorConfig := &system.Config{ Config: mm.Config{ ServiceInstance: proto.ServiceInstance{ Service: "server", InstanceId: 1, }, Collect: 10, Report: 60, }, } systemData, err := json.Marshal(systemMonitorConfig) t.Assert(err, IsNil) cmd = &proto.Cmd{ User: "******", Service: "mm", Cmd: "StartService", Data: systemData, } s.systemMonitor.SetConfig(systemMonitorConfig) reply = m.Handle(cmd) t.Assert(reply, NotNil) t.Assert(reply.Error, Equals, "") /** * GetConfig from mm which should return all monitors' configs. */ cmd = &proto.Cmd{ Cmd: "GetConfig", Service: "mm", } reply = m.Handle(cmd) t.Assert(reply, NotNil) t.Assert(reply.Error, Equals, "") t.Assert(reply.Data, NotNil) gotConfig := []proto.AgentConfig{} if err := json.Unmarshal(reply.Data, &gotConfig); err != nil { t.Fatal(err) } expectConfig := []proto.AgentConfig{ { InternalService: "mm", ExternalService: proto.ServiceInstance{ Service: "mysql", InstanceId: 1, }, Config: string(mysqlData), Running: true, }, { InternalService: "mm", ExternalService: proto.ServiceInstance{ Service: "server", InstanceId: 1, }, Config: string(systemData), Running: true, }, } sort.Sort(test.ByInternalService(gotConfig)) if same, diff := test.IsDeeply(gotConfig, expectConfig); !same { test.Dump(gotConfig) t.Error(diff) } }
/** * Tests: * - starting monitor * - stopping monitor * - starting monitor again (restarting monitor) * - sneaked in:) unknown cmd test */ func (s *ManagerTestSuite) TestRestartMonitor(t *C) { // Create and start mm, no monitors yet. mrm := mock.NewMrmsMonitor() m := mm.NewManager(s.logger, s.factory, s.clock, s.spool, s.im, mrm) t.Assert(m, NotNil) err := m.Start() t.Assert(err, IsNil) // Start a monitor by sending StartService + monitor config. // This is the config in test/mm/config/mm-mysql-1.conf. mmConfig := &mysql.Config{ Config: mm.Config{ ServiceInstance: proto.ServiceInstance{ Service: "mysql", InstanceId: 1, }, Collect: 1, Report: 60, }, Status: map[string]string{ "threads_connected": "gauge", "threads_running": "gauge", }, } mmConfigData, err := json.Marshal(mmConfig) t.Assert(err, IsNil) // If this were a real monitor, it would decode and set its own config. // The mock monitor doesn't have any real config type, so we set it manually. s.mysqlMonitor.SetConfig(mmConfig) // The agent calls mm.Handle() with the cmd (for logging and status) and the config data. cmd := &proto.Cmd{ User: "******", Service: "mm", Cmd: "StartService", Data: mmConfigData, } reply := m.Handle(cmd) t.Assert(reply, NotNil) t.Check(reply.Error, Equals, "") // The monitor should be running. The mock monitor returns "Running" if // Start() has been called; else it returns "Stopped". status := m.Status() t.Check(status["monitor"], Equals, "Running") // There should be a 1s collect ticker for the monitor. if ok, diff := test.IsDeeply(s.clock.Added, []uint{1}); !ok { t.Errorf("Make 1s ticker for collect interval\n%s", diff) } // After starting a monitor, mm should write its config to the dir // it learned when mm.LoadConfig() was called. Next time agent starts, // it will have mm start the monitor with this config. data, err := ioutil.ReadFile(s.configDir + "/mm-mysql-1.conf") t.Check(err, IsNil) gotConfig := &mysql.Config{} err = json.Unmarshal(data, gotConfig) t.Check(err, IsNil) if same, diff := test.IsDeeply(gotConfig, mmConfig); !same { test.Dump(gotConfig) t.Error(diff) } /** * Stop the monitor. */ cmd = &proto.Cmd{ User: "******", Service: "mm", Cmd: "StopService", Data: mmConfigData, } // Handles StopService without error. reply = m.Handle(cmd) t.Assert(reply, NotNil) t.Check(reply.Error, Equals, "") // Stop a monitor removes it from the managers list of monitors. // So it's no longer present in a status request. status = m.Status() t.Check(status["monitor"], Equals, "") // After stopping the monitor, the manager should remove its tickChan. if len(s.clock.Removed) != 1 { t.Error("Remove's monitor's tickChan from clock") } // After stopping a monitor, mm should remove its config file so agent // doesn't start it on restart. file := s.configDir + "/mm-mysql-1.conf" if pct.FileExists(file) { t.Error("Stopping monitor removes its config; ", file, " exists") } /** * Start the monitor again (restarting monitor). */ cmd = &proto.Cmd{ User: "******", Service: "mm", Cmd: "StartService", Data: mmConfigData, } // If this were a real monitor, it would decode and set its own config. // The mock monitor doesn't have any real config type, so we set it manually. s.mysqlMonitor.SetConfig(mmConfig) // The agent calls mm.Handle() with the cmd (for logging and status) and the config data. reply = m.Handle(cmd) t.Assert(reply, NotNil) t.Check(reply.Error, Equals, "") // The monitor should be running. The mock monitor returns "Running" if // Start() has been called; else it returns "Stopped". status = m.Status() t.Check(status["monitor"], Equals, "Running") // There should be a 1s collect ticker for the monitor. // (Actually two in s.clock.Added, as this is mock and we started monitor twice) if ok, diff := test.IsDeeply(s.clock.Added, []uint{1, 1}); !ok { t.Errorf("Make 1s ticker for collect interval\n%s", diff) } // After starting a monitor, mm should write its config to the dir // it learned when mm.LoadConfig() was called. Next time agent starts, // it will have mm start the monitor with this config. data, err = ioutil.ReadFile(s.configDir + "/mm-mysql-1.conf") t.Check(err, IsNil) gotConfig = &mysql.Config{} err = json.Unmarshal(data, gotConfig) t.Check(err, IsNil) if same, diff := test.IsDeeply(gotConfig, mmConfig); !same { t.Logf("%+v", gotConfig) t.Error(diff) } /** * While we're all setup and working, let's sneak in an unknown cmd test. */ cmd = &proto.Cmd{ User: "******", Service: "mm", Cmd: "Pontificate", Data: mmConfigData, } // Unknown cmd causes error. reply = m.Handle(cmd) t.Assert(reply, NotNil) t.Check(reply.Error, Not(Equals), "") }