func (lsm *LocalServiceManager) call(serviceMethod string, args interface{}, reply interface{}) error { lsm.mu.Lock() defer lsm.mu.Unlock() if lsm.client == nil { exe, err := osext.Executable() if err != nil { return err } listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return err } defer listener.Close() runner := exec.Command(exe, "service-runner", "--address", listener.Addr().String(), "--state-file", lsm.stateFile) runner.Stdout = os.Stdout runner.Stderr = os.Stderr go func() { runner.Run() lsm.mu.Lock() lsm.client = nil lsm.mu.Unlock() }() conn, err := listener.Accept() if err != nil { return err } lsm.client = rpc.NewClient(conn) } return lsm.client.Call(serviceMethod, args, reply) }
func main() { log.SetFlags(0) app := cli.NewApp() app.Name = "stack" app.Usage = "a simple, cross-platform, open-source, pull-based deployment system" app.Version = "0.2" app.Author = "Caleb Doxsey" app.Email = "*****@*****.**" app.Commands = []cli.Command{ { Name: "apply", Usage: "apply the configuration file", Action: func(c *cli.Context) { err := apply(c.Args().First()) if err != nil { log.Fatalln(err) } }, }, { Name: "auth", Usage: "generate credentials for services that need it", Action: func(c *cli.Context) { if len(c.Args()) < 1 { log.Fatalln("provider is required") } storage.Authenticate(c.Args()[0]) }, }, { Name: "cp", Usage: "copy a file: cp <source> <dest>", Action: func(c *cli.Context) { if len(c.Args()) < 2 { log.Fatalln("source and destination arguments are required") } err := cp(c.Args()[0], c.Args()[1]) if err != nil { log.Fatalln(err) } }, }, { Name: "ls", Usage: "list a directory", Action: func(c *cli.Context) { err := ls(c.Args().First()) if err != nil { log.Fatalln(err) } }, }, { Name: "rm", Usage: "remove a file", Action: func(c *cli.Context) { err := rm(c.Args().First()) if err != nil { log.Fatalln(err) } }, }, { Name: "service-runner", Usage: "daemon started by `watch` that runs applications", Flags: []cli.Flag{ cli.StringFlag{ Name: "address", Usage: "address to connect to", }, cli.StringFlag{ Name: "state-file", Usage: "file to store state in", }, }, Action: func(c *cli.Context) { runner.Run(c.String("address"), c.String("state-file")) }, }, { Name: "watch", Usage: "watch a config file", Action: func(c *cli.Context) { err := watch(c.Args().First()) if err != nil { log.Fatalln(err) } }, }, } app.Run(os.Args) }