1
0
mirror of https://github.com/go-micro/go-micro.git synced 2025-03-29 20:39:48 +02:00

Use internal runtime package for gomu run (#2248)

This change refactors the `gomu run` command to use Go Micro's internal
runtime package in order to run services. Not only does this clean up
duplicate functionality between Go Micro and Gomu, but also adds the
feature to Gomu to run remote projects. For example, the following
command pulls in a remote project and runs it locally.

```bash
gomu run github.com/auditemarlow/helloworld
```

The `gomu run` command remains backwards compatible. By invoking `gomu
run` in a Go Micro project directory, Gomu will simply run that project.
This commit is contained in:
Niek den Breeje 2021-09-06 15:01:29 +02:00 committed by GitHub
parent 270d910b73
commit a58b8883f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 194 deletions

View File

@ -5,17 +5,40 @@ import (
"os"
"os/signal"
"path/filepath"
"time"
"strings"
"github.com/asim/go-micro/cmd/gomu/cmd"
"github.com/asim/go-micro/v3/runtime"
"github.com/asim/go-micro/v3/runtime/local/git"
"github.com/fsnotify/fsnotify"
"github.com/urfave/cli/v2"
)
var (
DefaultRetries = 3
flags []cli.Flag = []cli.Flag{
&cli.StringFlag{
Name: "command",
Usage: "command to execute",
},
&cli.StringFlag{
Name: "args",
Usage: "command arguments",
},
&cli.StringFlag{
Name: "type",
Usage: "the type of service to operate on",
},
}
)
// NewCommand returns a new run command.
func NewCommand() *cli.Command {
return &cli.Command{
Name: "run",
Usage: "Build and run a service continuously",
Usage: "Build and run a service continuously, e.g. gomu run [github.com/auditemarlow/helloworld]",
Flags: flags,
Action: Run,
}
}
@ -23,76 +46,114 @@ func NewCommand() *cli.Command {
// Run runs a service and watches the project directory for change events. On
// write, the service is restarted. Exits on error.
func Run(ctx *cli.Context) error {
service := newService()
if err := service.Start(); err != nil {
wd, err := os.Getwd()
if err != nil {
return err
}
defer service.Stop()
sig := make(chan os.Signal)
done := make(chan bool)
signal.Notify(sig, os.Interrupt)
go func() {
<-sig
service.Stop()
done <- true
}()
go func() {
for {
service.Wait()
fmt.Println("Service has stopped, restarting service")
service.Start()
time.Sleep(time.Second)
}
}()
watcher, err := fsnotify.NewWatcher()
source, err := git.ParseSourceLocal(wd, ctx.Args().First())
if err != nil {
fmt.Println(err)
return err
}
defer watcher.Close()
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
svc := &runtime.Service{
Name: source.RuntimeName(),
Source: source.RuntimeSource(),
Version: source.Ref,
Metadata: make(map[string]string),
}
typ := ctx.String("type")
command := strings.TrimSpace(ctx.String("command"))
args := strings.TrimSpace(ctx.String("args"))
r := *cmd.DefaultCmd.Options().Runtime
var retries = DefaultRetries
if ctx.IsSet("retries") {
retries = ctx.Int("retries")
}
opts := []runtime.CreateOption{
runtime.WithOutput(os.Stdout),
runtime.WithRetries(retries),
runtime.CreateType(typ),
}
if len(command) > 0 {
opts = append(opts, runtime.WithCommand(command))
}
if len(args) > 0 {
opts = append(opts, runtime.WithArgs(args))
}
if err := r.Create(svc, opts...); err != nil {
return err
}
done := make(chan bool)
if r.String() == "local" {
sig := make(chan os.Signal)
signal.Notify(sig, os.Interrupt)
go func() {
<-sig
r.Delete(svc)
done <- true
}()
}
if source.Local {
watcher, err := fsnotify.NewWatcher()
if err != nil {
fmt.Println(err)
}
defer watcher.Close()
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
r.Update(svc)
}
if event.Op&fsnotify.Create == fsnotify.Create {
watcher.Add(event.Name)
}
if event.Op&fsnotify.Remove == fsnotify.Remove {
watcher.Remove(event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
fmt.Println("ERROR", err)
}
if event.Op&fsnotify.Write == fsnotify.Write {
service.Stop()
}
if event.Op&fsnotify.Create == fsnotify.Create {
watcher.Add(event.Name)
}
if event.Op&fsnotify.Remove == fsnotify.Remove {
watcher.Remove(event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
fmt.Println("ERROR", err)
}
}()
var files []string
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
files = append(files, path)
return nil
})
for _, file := range files {
if err := watcher.Add(file); err != nil {
return err
}
}
}()
var files []string
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
files = append(files, path)
return nil
})
for _, file := range files {
if err := watcher.Add(file); err != nil {
return err
}
}
<-done
if r.String() == "local" {
<-done
}
return nil
}

View File

@ -1,62 +0,0 @@
// +build !windows
package run
import (
"os"
"os/exec"
"syscall"
)
// Service is the interface that wraps the service that should run.
//
// Start starts the service and exits on error.
//
// Stop stops the service and exits on error.
//
// Wait waits for the service to exit and exits on error.
type Service interface {
Start() error
Stop() error
Wait() error
}
type service struct {
cmd *exec.Cmd
running bool
}
// Start starts the service and exits on error.
func (s *service) Start() error {
if s.running {
return nil
}
s.cmd = exec.Command("go", "run", ".")
s.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
s.cmd.Stdout = os.Stdout
s.cmd.Stderr = os.Stderr
s.running = true
return s.cmd.Start()
}
// Stop stops the service and exits on error.
func (s *service) Stop() error {
if !s.running {
return nil
}
s.running = false
return syscall.Kill(-s.cmd.Process.Pid, syscall.SIGTERM)
}
// Wait waits for the service to exit and exits on error.
func (s *service) Wait() error {
return s.cmd.Wait()
}
func newService() *service {
service := new(service)
return service
}

View File

@ -1,66 +0,0 @@
// +build windows
package run
import (
"os"
"os/exec"
"syscall"
)
// Service is the interface that wraps the service that should run.
//
// Start starts the service and exits on error.
//
// Stop stops the service and exits on error.
//
// Wait waits for the service to exit and exits on error.
type Service interface {
Start() error
Stop() error
Wait() error
}
type service struct {
cmd *exec.Cmd
running bool
}
// Start starts the service and exits on error.
func (s *service) Start() error {
if s.running {
return nil
}
s.cmd = exec.Command("go", "run", ".")
s.cmd.SysProcAttr = &syscall.SysProcAttr{}
s.cmd.Stdout = os.Stdout
s.cmd.Stderr = os.Stderr
s.running = true
return s.cmd.Start()
}
// Stop stops the service and exits on error.
func (s *service) Stop() error {
if !s.running {
return nil
}
s.running = false
pro, err := os.FindProcess(s.cmd.Process.Pid)
if err != nil {
return err
}
return pro.Signal(syscall.SIGTERM)
}
// Wait waits for the service to exit and exits on error.
func (s *service) Wait() error {
return s.cmd.Wait()
}
func newService() *service {
service := new(service)
return service
}

View File

@ -71,8 +71,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asim/go-micro/v3 v3.6.0 h1:I6UVJBpBtWNKCjWf0dRpZznRCW9TR4DXjV4wieyGhK0=
github.com/asim/go-micro/v3 v3.6.0/go.mod h1:cNGIIYQcp0qy+taNYmrBdaIHeqMWHV5ZH/FfQzfOyE8=
github.com/asim/go-micro/v3 v3.6.1-0.20210831082736-088ccb50019c h1:bwjwOyCBbClb9BwnVo8XohDYzA3Huk/PVpxSfqrx3xg=
github.com/asim/go-micro/v3 v3.6.1-0.20210831082736-088ccb50019c/go.mod h1:UQd2JfP/+4goQxddksr2r6ZSaUXNIlVE8hlepbAx/DI=
github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
@ -346,8 +344,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/micro/cli/v2 v2.1.2 h1:43J1lChg/rZCC1rvdqZNFSQDrGT7qfMrtp6/ztpIkEM=
github.com/micro/cli/v2 v2.1.2/go.mod h1:EguNh6DAoWKm9nmk+k/Rg0H3lQnDxqzu5x5srOtGtYg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=