func startATC(atcBin string, atcServerNumber uint16) (ifrit.Process, uint16) { atcPort := 5697 + uint16(GinkgoParallelNode()) + (atcServerNumber * 100) debugPort := 6697 + uint16(GinkgoParallelNode()) + (atcServerNumber * 100) atcCommand := exec.Command( atcBin, "--bind-port", fmt.Sprintf("%d", atcPort), "--peer-url", fmt.Sprintf("http://127.0.0.1:%d", atcPort), "--postgres-data-source", postgresRunner.DataSourceName(), "--debug-bind-port", fmt.Sprintf("%d", debugPort), "--basic-auth-username", "admin", "--basic-auth-password", "password", "--publicly-viewable", "--templates", filepath.Join("..", "web", "templates"), "--public", filepath.Join("..", "web", "public"), ) atcRunner := ginkgomon.New(ginkgomon.Config{ Command: atcCommand, Name: "atc", StartCheck: "atc.listening", AnsiColorCode: "32m", }) return ginkgomon.Invoke(atcRunner), atcPort }
func configureMetron(protocol string) { cfgFile, err := ioutil.TempFile(tmpdir, "metron") Expect(err).NotTo(HaveOccurred()) _, err = cfgFile.WriteString(` { "Index": 42, "Job": "test-component", "LegacyIncomingMessagesPort": ` + strconv.Itoa(incomingLegacyPort) + `, "DropsondeIncomingMessagesPort": ` + strconv.Itoa(incomingDropsondePort) + `, "SharedSecret": "shared_secret", "EtcdUrls" : ["` + etcdRunner.NodeURLS()[0] + `"], "EtcdMaxConcurrentRequests": 1, "Zone": "z1", "Deployment": "deployment-name", "LoggregatorDropsondePort": ` + strconv.Itoa(dropsondePort) + `, "PreferredProtocol": "` + protocol + `" }`) Expect(err).NotTo(HaveOccurred()) cfgFile.Close() metronRunner = ginkgomon.New(ginkgomon.Config{ Name: "metron", AnsiColorCode: "97m", StartCheck: "metron started", Command: exec.Command( metronPath, "--config", cfgFile.Name(), "--debug", ), }) }
func New(binPath string, args Args) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "receptor", Command: exec.Command(binPath, args.ArgSlice()...), StartCheck: "receptor.started", }) }
func startATC(atcBin string, atcServerNumber uint16) (ifrit.Process, uint16) { atcPort := 5697 + uint16(GinkgoParallelNode()) + (atcServerNumber * 100) debugPort := 6697 + uint16(GinkgoParallelNode()) + (atcServerNumber * 100) atcCommand := exec.Command( atcBin, "-webListenPort", fmt.Sprintf("%d", atcPort), "-callbacksURL", fmt.Sprintf("http://127.0.0.1:%d", atcPort), "-debugListenPort", fmt.Sprintf("%d", debugPort), "-httpUsername", "admin", "-httpHashedPassword", "$2a$04$DYaOWeQgyxTCv7QxydTP9u1KnwXWSKipC4BeTuBy.9m.IlkAdqNGG", // "password" "-publiclyViewable=true", "-templates", filepath.Join("..", "web", "templates"), "-public", filepath.Join("..", "web", "public"), "-sqlDataSource", postgresRunner.DataSourceName(), ) atcRunner := ginkgomon.New(ginkgomon.Config{ Command: atcCommand, Name: "atc", StartCheck: "atc.listening", AnsiColorCode: "32m", }) return ginkgomon.Invoke(atcRunner), atcPort }
func (maker ComponentMaker) StagerN(portOffset int, argv ...string) ifrit.Runner { address := maker.Addresses.Stager port, err := strconv.Atoi(strings.Split(address, ":")[1]) Expect(err).NotTo(HaveOccurred()) return ginkgomon.New(ginkgomon.Config{ Name: "stager", AnsiColorCode: "94m", StartCheck: "Listening for staging requests!", StartCheckTimeout: 10 * time.Second, Command: exec.Command( maker.Artifacts.Executables["stager"], append(maker.appendLifecycleArgs([]string{ "-ccBaseURL", "http://" + maker.Addresses.FakeCC, "-ccUsername", fake_cc.CC_USERNAME, "-ccPassword", fake_cc.CC_PASSWORD, "-dockerStagingStack", maker.DefaultStack(), "-diegoAPIURL", "http://" + maker.Addresses.Receptor, "-stagerURL", fmt.Sprintf("http://127.0.0.1:%d", offsetPort(port, portOffset)), "-fileServerURL", "http://" + maker.Addresses.FileServer, "-logLevel", "debug", }), argv...)..., ), }) }
func StartGarden(gardenBin, containerizerBin string, argv ...string) (ifrit.Process, garden.Client) { gardenPort, err := localip.LocalPort() Expect(err).NotTo(HaveOccurred()) gardenAddr := fmt.Sprintf("127.0.0.1:%d", gardenPort) tmpDir := os.TempDir() // If below fails, try // netsh advfirewall firewall add rule name="Open Port 48080" dir=in action=allow protocol=TCP localport=48080 containerizerPort, err := localip.LocalPort() Expect(err).NotTo(HaveOccurred()) gardenRunner := garden_runner.New("tcp4", gardenAddr, tmpDir, gardenBin, fmt.Sprintf("http://127.0.0.1:%d", containerizerPort)) containerizerRunner := ginkgomon.New(ginkgomon.Config{ Name: "containerizer", Command: exec.Command(containerizerBin, "127.0.0.1", strconv.Itoa(int(containerizerPort))), AnsiColorCode: "", StartCheck: "Control-C to quit.", StartCheckTimeout: 10 * time.Second, Cleanup: func() {}, }) group := grouper.NewOrdered(syscall.SIGTERM, []grouper.Member{ {Name: "containerizer", Runner: containerizerRunner}, {Name: "garden", Runner: gardenRunner}, }) gardenProcess := ifrit.Invoke(group) return gardenProcess, gardenRunner.NewClient() }
func New(binPath string, args Args) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "routing-api", Command: exec.Command(binPath, args.ArgSlice()...), StartCheck: "starting", }) }
func (maker ComponentMaker) Etcd(argv ...string) ifrit.Runner { nodeName := fmt.Sprintf("etcd_%d", ginkgo.GinkgoParallelNode()) dataDir := path.Join(os.TempDir(), nodeName) return ginkgomon.New(ginkgomon.Config{ Name: "etcd", AnsiColorCode: "31m", StartCheck: "etcdserver: published", StartCheckTimeout: 10 * time.Second, Command: exec.Command( "etcd", append([]string{ "--name", nodeName, "--data-dir", dataDir, "--listen-client-urls", "https://" + maker.Addresses.Etcd, "--listen-peer-urls", "http://" + maker.Addresses.EtcdPeer, "--initial-cluster", nodeName + "=" + "http://" + maker.Addresses.EtcdPeer, "--initial-advertise-peer-urls", "http://" + maker.Addresses.EtcdPeer, "--initial-cluster-state", "new", "--advertise-client-urls", "https://" + maker.Addresses.Etcd, "--cert-file", maker.SSL.ServerCert, "--key-file", maker.SSL.ServerKey, "--ca-file", maker.SSL.CACert, }, argv...)..., ), Cleanup: func() { err := os.RemoveAll(dataDir) Expect(err).NotTo(HaveOccurred()) }, }) }
func New(config *Config) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "converger", AnsiColorCode: "94m", Command: exec.Command(config.BinPath, config.ArgSlice()...), StartCheck: "acquiring-lock", }) }
func (maker ComponentMaker) RepN(n int, argv ...string) *ginkgomon.Runner { host, portString, err := net.SplitHostPort(maker.Addresses.Rep) Expect(err).NotTo(HaveOccurred()) port, err := strconv.Atoi(portString) Expect(err).NotTo(HaveOccurred()) name := "rep-" + strconv.Itoa(n) tmpDir, err := ioutil.TempDir(os.TempDir(), "executor") Expect(err).NotTo(HaveOccurred()) cachePath := path.Join(tmpDir, "cache") args := append( []string{ "-sessionName", name, "-rootFSProvider", "docker", "-etcdCluster", "https://" + maker.Addresses.Etcd, "-bbsAddress", fmt.Sprintf("http://%s", maker.Addresses.BBS), "-listenAddr", fmt.Sprintf("%s:%d", host, offsetPort(port, n)), "-cellID", "the-cell-id-" + strconv.Itoa(ginkgo.GinkgoParallelNode()) + "-" + strconv.Itoa(n), "-pollingInterval", "1s", "-evacuationPollingInterval", "1s", "-evacuationTimeout", "1s", "-lockTTL", "10s", "-lockRetryInterval", "1s", "-consulCluster", maker.ConsulCluster(), "-receptorTaskHandlerURL", "http://" + maker.Addresses.ReceptorTaskHandler, "-gardenNetwork", "tcp", "-gardenAddr", maker.Addresses.GardenLinux, "-containerMaxCpuShares", "1024", "-cachePath", cachePath, "-tempDir", tmpDir, "-logLevel", "debug", "-etcdCertFile", maker.SSL.ClientCert, "-etcdKeyFile", maker.SSL.ClientKey, "-etcdCaFile", maker.SSL.CACert, }, argv..., ) for stack, path := range maker.PreloadedStackPathMap { args = append(args, "-preloadedRootFS", fmt.Sprintf("%s:%s", stack, path)) } return ginkgomon.New(ginkgomon.Config{ Name: name, AnsiColorCode: "92m", StartCheck: `"` + name + `.started"`, // rep is not started until it can ping an executor; executor can take a // bit to start, so account for it StartCheckTimeout: 30 * time.Second, Command: exec.Command(maker.Artifacts.Executables["rep"], args...), Cleanup: func() { os.RemoveAll(tmpDir) }, }) }
func (maker ComponentMaker) Router() ifrit.Runner { _, routerPort, err := net.SplitHostPort(maker.Addresses.Router) Expect(err).NotTo(HaveOccurred()) routerPortInt, err := strconv.Atoi(routerPort) Expect(err).NotTo(HaveOccurred()) natsHost, natsPort, err := net.SplitHostPort(maker.Addresses.NATS) Expect(err).NotTo(HaveOccurred()) natsPortInt, err := strconv.Atoi(natsPort) Expect(err).NotTo(HaveOccurred()) routerConfig := &gorouterconfig.Config{ Port: uint16(routerPortInt), PruneStaleDropletsIntervalInSeconds: 5, DropletStaleThresholdInSeconds: 10, PublishActiveAppsIntervalInSeconds: 0, StartResponseDelayIntervalInSeconds: 1, Nats: []gorouterconfig.NatsConfig{ { Host: natsHost, Port: uint16(natsPortInt), }, }, Logging: gorouterconfig.LoggingConfig{ File: "/dev/stdout", Level: "info", MetronAddress: "127.0.0.1:65534", // nonsense to make dropsonde happy }, } configFile, err := ioutil.TempFile(os.TempDir(), "router-config") Expect(err).NotTo(HaveOccurred()) defer configFile.Close() err = candiedyaml.NewEncoder(configFile).Encode(routerConfig) Expect(err).NotTo(HaveOccurred()) return ginkgomon.New(ginkgomon.Config{ Name: "router", AnsiColorCode: "32m", StartCheck: "router.started", StartCheckTimeout: 10 * time.Second, // it waits 1 second before listening. yep. Command: exec.Command( maker.Artifacts.Executables["router"], "-c", configFile.Name(), ), Cleanup: func() { err := os.Remove(configFile.Name()) Expect(err).NotTo(HaveOccurred()) }, }) }
func New(binPath string, args Args) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "sample-receiver", AnsiColorCode: "1;96m", StartCheck: "Listening on", StartCheckTimeout: 10 * time.Second, Command: exec.Command(binPath, args.ArgSlice()...), }) }
func New(binPath string, args Args) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "ssh-proxy", AnsiColorCode: "1;95m", StartCheck: "ssh-proxy.started", StartCheckTimeout: 10 * time.Second, Command: exec.Command(binPath, args.ArgSlice()...), }) }
func start(network, addr string, argv ...string) *RunningGarden { tmpDir := filepath.Join( os.TempDir(), fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()), ) Expect(os.MkdirAll(tmpDir, 0755)).To(Succeed()) if GraphRoot == "" { GraphRoot = filepath.Join(tmpDir, "graph") } graphPath := filepath.Join(GraphRoot, fmt.Sprintf("node-%d", ginkgo.GinkgoParallelNode())) stateDirPath := filepath.Join(tmpDir, "state") depotPath := filepath.Join(tmpDir, "containers") snapshotsPath := filepath.Join(tmpDir, "snapshots") if err := os.MkdirAll(stateDirPath, 0755); err != nil { Expect(err).ToNot(HaveOccurred()) } if err := os.MkdirAll(depotPath, 0755); err != nil { Expect(err).ToNot(HaveOccurred()) } if err := os.MkdirAll(snapshotsPath, 0755); err != nil { Expect(err).ToNot(HaveOccurred()) } MustMountTmpfs(graphPath) r := &RunningGarden{ GraphRoot: GraphRoot, GraphPath: graphPath, StateDirPath: stateDirPath, DepotPath: depotPath, SnapshotsPath: snapshotsPath, tmpdir: tmpDir, logger: lagertest.NewTestLogger("garden-runner"), Client: client.New(connection.New(network, addr)), } c := cmd(stateDirPath, depotPath, snapshotsPath, graphPath, network, addr, GardenBin, BinPath, RootFSPath, argv...) r.runner = ginkgomon.New(ginkgomon.Config{ Name: "garden-linux", Command: c, AnsiColorCode: "31m", StartCheck: "garden-linux.started", StartCheckTimeout: 30 * time.Second, }) r.process = ifrit.Invoke(r.runner) r.Pid = c.Process.Pid return r }
func New(binPath string, args Args) *ginkgomon.Runner { if args.MetricsReportInterval == 0 { args.MetricsReportInterval = time.Minute } return ginkgomon.New(ginkgomon.Config{ Name: "bbs", Command: exec.Command(binPath, args.ArgSlice()...), StartCheck: "bbs.started", }) }
func startATC(atcBin string, atcServerNumber uint16, publiclyViewable bool, authTypes ...string) (ifrit.Process, uint16) { atcCommand, atcPort := getATCCommand(atcBin, atcServerNumber, publiclyViewable, authTypes...) atcRunner := ginkgomon.New(ginkgomon.Config{ Command: atcCommand, Name: "atc", StartCheck: "atc.listening", AnsiColorCode: "32m", }) return ginkgomon.Invoke(atcRunner), atcPort }
func WaitForMigration(binPath string, args Args) *ginkgomon.Runner { if args.MetricsReportInterval == 0 { args.MetricsReportInterval = time.Minute } return ginkgomon.New(ginkgomon.Config{ Name: "bbs", Command: exec.Command(binPath, args.ArgSlice()...), StartCheck: "finished-migrations", }) }
func (cr *ClusterRunner) Start() { cr.mutex.Lock() defer cr.mutex.Unlock() if cr.running { return } tmpDir, err := ioutil.TempDir("", defaultDataDirPrefix) Expect(err).NotTo(HaveOccurred()) cr.dataDir = tmpDir tmpDir, err = ioutil.TempDir("", defaultConfigDirPrefix) Expect(err).NotTo(HaveOccurred()) cr.configDir = tmpDir cr.consulProcesses = make([]ifrit.Process, cr.numNodes) for i := 0; i < cr.numNodes; i++ { iStr := fmt.Sprintf("%d", i) nodeDataDir := path.Join(cr.dataDir, iStr) os.MkdirAll(nodeDataDir, 0700) configFilePath := writeConfigFile( cr.configDir, nodeDataDir, iStr, cr.startingPort, i, cr.numNodes, cr.sessionTTL, ) process := ginkgomon.Invoke(ginkgomon.New(ginkgomon.Config{ Name: fmt.Sprintf("consul_cluster[%d]", i), AnsiColorCode: "35m", StartCheck: "agent: Join completed.", StartCheckTimeout: 10 * time.Second, Command: exec.Command( "consul", "agent", "--log-level", "trace", "--config-file", configFilePath, ), })) cr.consulProcesses[i] = process ready := process.Ready() Eventually(ready, 10, 0.05).Should(BeClosed(), "Expected consul to be up and running") } cr.running = true }
func NewWatcher(bin, bbsAddress, ccBaseURL, consulCluster string) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "tps-watcher", Command: exec.Command( bin, "-bbsAddress", bbsAddress, "-ccBaseURL", ccBaseURL, "-lockRetryInterval", "1s", "-consulCluster", consulCluster, ), StartCheck: "tps-watcher.started", }) }
func NewListener(bin, listenAddr, bbsAddress, trafficControllerURL string) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "tps-listener", Command: exec.Command( bin, "-bbsAddress", bbsAddress, "-listenAddr", listenAddr, "-trafficControllerURL", trafficControllerURL, "-skipSSLVerification", "true", ), StartCheck: "tps-listener.started", }) }
func (maker ComponentMaker) TPSListener(argv ...string) ifrit.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "tps-listener", AnsiColorCode: "96m", StartCheck: `"tps-listener.started"`, StartCheckTimeout: 10 * time.Second, Command: exec.Command( maker.Artifacts.Executables["tps-listener"], append([]string{ "-diegoAPIURL", "http://" + maker.Addresses.Receptor, "-listenAddr", maker.Addresses.TPSListener, "-logLevel", "debug", }, argv...)..., ), }) }
func (maker ComponentMaker) NATS(argv ...string) ifrit.Runner { host, port, err := net.SplitHostPort(maker.Addresses.NATS) Expect(err).NotTo(HaveOccurred()) return ginkgomon.New(ginkgomon.Config{ Name: "gnatsd", AnsiColorCode: "30m", StartCheck: "gnatsd is ready", StartCheckTimeout: 10 * time.Second, Command: exec.Command( "gnatsd", append([]string{ "--addr", host, "--port", port, }, argv...)..., ), }) }
func (maker ComponentMaker) SSHProxy(argv ...string) ifrit.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "ssh-proxy", AnsiColorCode: "95m", StartCheck: "ssh-proxy.started", StartCheckTimeout: 10 * time.Second, Command: exec.Command( maker.Artifacts.Executables["ssh-proxy"], append([]string{ "-address", maker.Addresses.SSHProxy, "-hostKey", maker.SSHConfig.HostKeyPem, "-diegoAPIURL", "http://" + maker.Addresses.Receptor, "-logLevel", "debug", "-enableDiegoAuth", }, argv...)..., ), }) }
func createEmitterRunner(sessionName string) *ginkgomon.Runner { return ginkgomon.New(ginkgomon.Config{ Command: exec.Command( string(emitterPath), "-sessionName", sessionName, "-natsAddresses", fmt.Sprintf("127.0.0.1:%d", natsPort), "-bbsAddress", bbsURL.String(), "-communicationTimeout", "100ms", "-syncInterval", syncInterval.String(), "-lockRetryInterval", "1s", "-consulCluster", consulRunner.ConsulCluster(), ), StartCheck: "route-emitter.started", AnsiColorCode: "97m", }) }
func (maker ComponentMaker) RouteEmitter(argv ...string) ifrit.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "route-emitter", AnsiColorCode: "95m", StartCheck: `"route-emitter.started"`, StartCheckTimeout: 10 * time.Second, Command: exec.Command( maker.Artifacts.Executables["route-emitter"], append([]string{ "-natsAddresses", maker.Addresses.NATS, "-diegoAPIURL", "http://" + maker.Addresses.Receptor, "-lockRetryInterval", "1s", "-consulCluster", maker.ConsulCluster(), "-logLevel", "debug", }, argv...)..., ), }) }
func NewGnatsdTestRunner(natsPort int) *ginkgomon.Runner { gnatsdPath, err := exec.LookPath("gnatsd") if err != nil { fmt.Println("You need gnatsd installed!") os.Exit(1) } return ginkgomon.New(ginkgomon.Config{ Name: "gnatsd", AnsiColorCode: "99m", StartCheck: "gnatsd is ready", StartCheckTimeout: 5 * time.Second, Command: exec.Command( gnatsdPath, "-p", strconv.Itoa(natsPort), ), }) }
func (runner *AgentRunner) Start() { runner.mutex.Lock() defer runner.mutex.Unlock() if runner.running { return } tmpDir, err := ioutil.TempDir("", defaultDataDirPrefix) Expect(err).NotTo(HaveOccurred()) runner.dataDir = tmpDir tmpDir, err = ioutil.TempDir("", defaultConfigDirPrefix) Expect(err).NotTo(HaveOccurred()) runner.configDir = tmpDir os.MkdirAll(runner.dataDir, 0700) configFilePath := writeConfigFile( runner.configDir, runner.dataDir, runner.bindAddress, runner.serverIps, ) timeout := 1 * time.Minute process := ginkgomon.Invoke(ginkgomon.New(ginkgomon.Config{ Name: "consul_agent", AnsiColorCode: "35m", StartCheck: "agent: Join completed.", StartCheckTimeout: timeout, Command: exec.Command( "consul", "agent", "--config-file", configFilePath, ), })) runner.consulProcess = process ready := process.Ready() Eventually(ready, timeout, 100*time.Millisecond).Should(BeClosed(), "Expected consul to be up and running") runner.running = true }
func (maker ComponentMaker) NsyncListener(argv ...string) ifrit.Runner { address := maker.Addresses.NsyncListener port, err := strconv.Atoi(strings.Split(address, ":")[1]) Expect(err).NotTo(HaveOccurred()) return ginkgomon.New(ginkgomon.Config{ Name: "nsync-listener", AnsiColorCode: "97m", StartCheck: `"nsync.listener.started"`, StartCheckTimeout: 10 * time.Second, Command: exec.Command( maker.Artifacts.Executables["nsync-listener"], append(maker.appendLifecycleArgs([]string{ "-diegoAPIURL", "http://" + maker.Addresses.Receptor, "-nsyncURL", fmt.Sprintf("http://127.0.0.1:%d", port), "-fileServerURL", "http://" + maker.Addresses.FileServer, "-logLevel", "debug", }), argv...)..., ), }) }
func (maker ComponentMaker) BBS(argv ...string) ifrit.Runner { return ginkgomon.New(ginkgomon.Config{ Name: "bbs", AnsiColorCode: "33m", StartCheck: "bbs.started", StartCheckTimeout: 10 * time.Second, Command: exec.Command( maker.Artifacts.Executables["bbs"], append([]string{ "-address", maker.Addresses.BBS, "-auctioneerAddress", "http://" + maker.Addresses.Auctioneer, "-consulCluster", maker.ConsulCluster(), "-etcdCluster", maker.EtcdCluster(), "-etcdCertFile", maker.SSL.ClientCert, "-etcdKeyFile", maker.SSL.ClientKey, "-etcdCaFile", maker.SSL.CACert, "-logLevel", "debug", }, argv...)..., ), }) }
func (m *MetronRunner) Configure() *ginkgomon.Runner { cfgFile, err := ioutil.TempFile(m.TempDir, "metron") Expect(err).NotTo(HaveOccurred()) config := config.Config{ Index: "42", Job: "test-component", LoggregatorDropsondePort: m.DropsondePort, IncomingUDPPort: m.MetronPort, SharedSecret: "shared_secret", EtcdUrls: m.EtcdRunner.NodeURLS(), EtcdMaxConcurrentRequests: 1, Zone: "z1", Deployment: "deployment-name", Protocols: m.Protocols, MetricBatchIntervalMilliseconds: 50, RuntimeStatsIntervalMilliseconds: 500, TCPBatchSizeBytes: 1024, TLSConfig: config.TLSConfig{ CertFile: m.CertFile, KeyFile: m.KeyFile, CAFile: m.CAFile, }, } configBytes, err := json.Marshal(config) _, err = cfgFile.Write(configBytes) Expect(err).NotTo(HaveOccurred()) cfgFile.Close() command := exec.Command(m.Path, "--config", cfgFile.Name(), "--debug") command.Stdout = gexec.NewPrefixedWriter("[o][metron]", GinkgoWriter) command.Stderr = gexec.NewPrefixedWriter("[e][metron]", GinkgoWriter) m.Runner = ginkgomon.New(ginkgomon.Config{ Name: "metron", AnsiColorCode: "97m", StartCheck: "metron started", Command: command, }) return m.Runner }