From ed028d1000fed3c8b9e125a014b589cb307739ea Mon Sep 17 00:00:00 2001 From: hzsunshx Date: Mon, 7 Sep 2015 13:11:31 +0800 Subject: [PATCH] use inject to short code --- Godeps/Godeps.json | 5 + .../github.com/codegangsta/inject/.gitignore | 2 + .../src/github.com/codegangsta/inject/LICENSE | 20 ++ .../github.com/codegangsta/inject/README.md | 92 +++++++++ .../github.com/codegangsta/inject/inject.go | 187 ++++++++++++++++++ .../codegangsta/inject/inject_test.go | 159 +++++++++++++++ .../inject/translations/README_zh_cn.md | 85 ++++++++ .../codegangsta/inject/update_readme.sh | 3 + gosuv.go | 81 ++++---- gosuvpb/gosuv.pb.go | 60 ++++++ gosuvpb/gosuv.proto | 9 +- program.go | 17 ++ service.go | 4 + 13 files changed, 685 insertions(+), 39 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/.gitignore create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/LICENSE create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/README.md create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/inject.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/translations/README_zh_cn.md create mode 100644 Godeps/_workspace/src/github.com/codegangsta/inject/update_readme.sh diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 81b2b1b..6ab9377 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -11,6 +11,11 @@ "Comment": "1.2.0-139-g142e6cd", "Rev": "142e6cd241a4dfbf7f07a018f1f8225180018da4" }, + { + "ImportPath": "github.com/codegangsta/inject", + "Comment": "v1.0-rc1-10-g33e0aa1", + "Rev": "33e0aa1cb7c019ccc3fbe049a8262a6403d30504" + }, { "ImportPath": "github.com/codeskyblue/kproc", "Rev": "fcb55eb35ab6b7290f395ad596e80e8355d52d69" diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/.gitignore b/Godeps/_workspace/src/github.com/codegangsta/inject/.gitignore new file mode 100644 index 0000000..df3df8a --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/.gitignore @@ -0,0 +1,2 @@ +inject +inject.test diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/LICENSE b/Godeps/_workspace/src/github.com/codegangsta/inject/LICENSE new file mode 100644 index 0000000..eb68a0e --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Jeremy Saenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/README.md b/Godeps/_workspace/src/github.com/codegangsta/inject/README.md new file mode 100644 index 0000000..679abe0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/README.md @@ -0,0 +1,92 @@ +# inject +-- + import "github.com/codegangsta/inject" + +Package inject provides utilities for mapping and injecting dependencies in +various ways. + +Language Translations: +* [简体中文](translations/README_zh_cn.md) + +## Usage + +#### func InterfaceOf + +```go +func InterfaceOf(value interface{}) reflect.Type +``` +InterfaceOf dereferences a pointer to an Interface type. It panics if value is +not an pointer to an interface. + +#### type Applicator + +```go +type Applicator interface { + // Maps dependencies in the Type map to each field in the struct + // that is tagged with 'inject'. Returns an error if the injection + // fails. + Apply(interface{}) error +} +``` + +Applicator represents an interface for mapping dependencies to a struct. + +#### type Injector + +```go +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent sets the parent of the injector. If the injector cannot find a + // dependency in its Type map it will check its parent before returning an + // error. + SetParent(Injector) +} +``` + +Injector represents an interface for mapping and injecting dependencies into +structs and function arguments. + +#### func New + +```go +func New() Injector +``` +New returns a new Injector. + +#### type Invoker + +```go +type Invoker interface { + // Invoke attempts to call the interface{} provided as a function, + // providing dependencies for function arguments based on Type. Returns + // a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke(interface{}) ([]reflect.Value, error) +} +``` + +Invoker represents an interface for calling functions via reflection. + +#### type TypeMapper + +```go +type TypeMapper interface { + // Maps the interface{} value based on its immediate type from reflect.TypeOf. + Map(interface{}) TypeMapper + // Maps the interface{} value based on the pointer of an Interface provided. + // This is really only useful for mapping a value as an interface, as interfaces + // cannot at this time be referenced directly without a pointer. + MapTo(interface{}, interface{}) TypeMapper + // Provides a possibility to directly insert a mapping based on type and value. + // This makes it possible to directly map type arguments not possible to instantiate + // with reflect like unidirectional channels. + Set(reflect.Type, reflect.Value) TypeMapper + // Returns the Value that is mapped to the current type. Returns a zeroed Value if + // the Type has not been mapped. + Get(reflect.Type) reflect.Value +} +``` + +TypeMapper represents an interface for mapping interface{} values based on type. diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go b/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go new file mode 100644 index 0000000..3ff713c --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/inject.go @@ -0,0 +1,187 @@ +// Package inject provides utilities for mapping and injecting dependencies in various ways. +package inject + +import ( + "fmt" + "reflect" +) + +// Injector represents an interface for mapping and injecting dependencies into structs +// and function arguments. +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent sets the parent of the injector. If the injector cannot find a + // dependency in its Type map it will check its parent before returning an + // error. + SetParent(Injector) +} + +// Applicator represents an interface for mapping dependencies to a struct. +type Applicator interface { + // Maps dependencies in the Type map to each field in the struct + // that is tagged with 'inject'. Returns an error if the injection + // fails. + Apply(interface{}) error +} + +// Invoker represents an interface for calling functions via reflection. +type Invoker interface { + // Invoke attempts to call the interface{} provided as a function, + // providing dependencies for function arguments based on Type. Returns + // a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke(interface{}) ([]reflect.Value, error) +} + +// TypeMapper represents an interface for mapping interface{} values based on type. +type TypeMapper interface { + // Maps the interface{} value based on its immediate type from reflect.TypeOf. + Map(interface{}) TypeMapper + // Maps the interface{} value based on the pointer of an Interface provided. + // This is really only useful for mapping a value as an interface, as interfaces + // cannot at this time be referenced directly without a pointer. + MapTo(interface{}, interface{}) TypeMapper + // Provides a possibility to directly insert a mapping based on type and value. + // This makes it possible to directly map type arguments not possible to instantiate + // with reflect like unidirectional channels. + Set(reflect.Type, reflect.Value) TypeMapper + // Returns the Value that is mapped to the current type. Returns a zeroed Value if + // the Type has not been mapped. + Get(reflect.Type) reflect.Value +} + +type injector struct { + values map[reflect.Type]reflect.Value + parent Injector +} + +// InterfaceOf dereferences a pointer to an Interface type. +// It panics if value is not an pointer to an interface. +func InterfaceOf(value interface{}) reflect.Type { + t := reflect.TypeOf(value) + + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + + if t.Kind() != reflect.Interface { + panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") + } + + return t +} + +// New returns a new Injector. +func New() Injector { + return &injector{ + values: make(map[reflect.Type]reflect.Value), + } +} + +// Invoke attempts to call the interface{} provided as a function, +// providing dependencies for function arguments based on Type. +// Returns a slice of reflect.Value representing the returned values of the function. +// Returns an error if the injection fails. +// It panics if f is not a function +func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { + t := reflect.TypeOf(f) + + var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func + for i := 0; i < t.NumIn(); i++ { + argType := t.In(i) + val := inj.Get(argType) + if !val.IsValid() { + return nil, fmt.Errorf("Value not found for type %v", argType) + } + + in[i] = val + } + + return reflect.ValueOf(f).Call(in), nil +} + +// Maps dependencies in the Type map to each field in the struct +// that is tagged with 'inject'. +// Returns an error if the injection fails. +func (inj *injector) Apply(val interface{}) error { + v := reflect.ValueOf(val) + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return nil // Should not panic here ? + } + + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + structField := t.Field(i) + if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { + ft := f.Type() + v := inj.Get(ft) + if !v.IsValid() { + return fmt.Errorf("Value not found for type %v", ft) + } + + f.Set(v) + } + + } + + return nil +} + +// Maps the concrete value of val to its dynamic type using reflect.TypeOf, +// It returns the TypeMapper registered in. +func (i *injector) Map(val interface{}) TypeMapper { + i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) + return i +} + +func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { + i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) + return i +} + +// Maps the given reflect.Type to the given reflect.Value and returns +// the Typemapper the mapping has been registered in. +func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { + i.values[typ] = val + return i +} + +func (i *injector) Get(t reflect.Type) reflect.Value { + val := i.values[t] + + if val.IsValid() { + return val + } + + // no concrete types found, try to find implementors + // if t is an interface + if t.Kind() == reflect.Interface { + for k, v := range i.values { + if k.Implements(t) { + val = v + break + } + } + } + + // Still no type found, try to look it up on the parent + if !val.IsValid() && i.parent != nil { + val = i.parent.Get(t) + } + + return val + +} + +func (i *injector) SetParent(parent Injector) { + i.parent = parent +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go b/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go new file mode 100644 index 0000000..eb94471 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/inject_test.go @@ -0,0 +1,159 @@ +package inject_test + +import ( + "fmt" + "github.com/codegangsta/inject" + "reflect" + "testing" +) + +type SpecialString interface { +} + +type TestStruct struct { + Dep1 string `inject:"t" json:"-"` + Dep2 SpecialString `inject` + Dep3 string +} + +type Greeter struct { + Name string +} + +func (g *Greeter) String() string { + return "Hello, My name is" + g.Name +} + +/* Test Helpers */ +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func Test_InjectorInvoke(t *testing.T) { + injector := inject.New() + expect(t, injector == nil, false) + + dep := "some dependency" + injector.Map(dep) + dep2 := "another dep" + injector.MapTo(dep2, (*SpecialString)(nil)) + dep3 := make(chan *SpecialString) + dep4 := make(chan *SpecialString) + typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem()) + typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem()) + injector.Set(typRecv, reflect.ValueOf(dep3)) + injector.Set(typSend, reflect.ValueOf(dep4)) + + _, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) { + expect(t, d1, dep) + expect(t, d2, dep2) + expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem()) + expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem()) + expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir) + expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir) + }) + + expect(t, err, nil) +} + +func Test_InjectorInvokeReturnValues(t *testing.T) { + injector := inject.New() + expect(t, injector == nil, false) + + dep := "some dependency" + injector.Map(dep) + dep2 := "another dep" + injector.MapTo(dep2, (*SpecialString)(nil)) + + result, err := injector.Invoke(func(d1 string, d2 SpecialString) string { + expect(t, d1, dep) + expect(t, d2, dep2) + return "Hello world" + }) + + expect(t, result[0].String(), "Hello world") + expect(t, err, nil) +} + +func Test_InjectorApply(t *testing.T) { + injector := inject.New() + + injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil)) + + s := TestStruct{} + err := injector.Apply(&s) + expect(t, err, nil) + + expect(t, s.Dep1, "a dep") + expect(t, s.Dep2, "another dep") + expect(t, s.Dep3, "") +} + +func Test_InterfaceOf(t *testing.T) { + iType := inject.InterfaceOf((*SpecialString)(nil)) + expect(t, iType.Kind(), reflect.Interface) + + iType = inject.InterfaceOf((**SpecialString)(nil)) + expect(t, iType.Kind(), reflect.Interface) + + // Expecting nil + defer func() { + rec := recover() + refute(t, rec, nil) + }() + iType = inject.InterfaceOf((*testing.T)(nil)) +} + +func Test_InjectorSet(t *testing.T) { + injector := inject.New() + typ := reflect.TypeOf("string") + typSend := reflect.ChanOf(reflect.SendDir, typ) + typRecv := reflect.ChanOf(reflect.RecvDir, typ) + + // instantiating unidirectional channels is not possible using reflect + // http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064 + chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) + chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) + + injector.Set(typSend, chanSend) + injector.Set(typRecv, chanRecv) + + expect(t, injector.Get(typSend).IsValid(), true) + expect(t, injector.Get(typRecv).IsValid(), true) + expect(t, injector.Get(chanSend.Type()).IsValid(), false) +} + +func Test_InjectorGet(t *testing.T) { + injector := inject.New() + + injector.Map("some dependency") + + expect(t, injector.Get(reflect.TypeOf("string")).IsValid(), true) + expect(t, injector.Get(reflect.TypeOf(11)).IsValid(), false) +} + +func Test_InjectorSetParent(t *testing.T) { + injector := inject.New() + injector.MapTo("another dep", (*SpecialString)(nil)) + + injector2 := inject.New() + injector2.SetParent(injector) + + expect(t, injector2.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true) +} + +func TestInjectImplementors(t *testing.T) { + injector := inject.New() + g := &Greeter{"Jeremy"} + injector.Map(g) + + expect(t, injector.Get(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/translations/README_zh_cn.md b/Godeps/_workspace/src/github.com/codegangsta/inject/translations/README_zh_cn.md new file mode 100644 index 0000000..0ac3d3f --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/translations/README_zh_cn.md @@ -0,0 +1,85 @@ +# inject +-- + import "github.com/codegangsta/inject" + +inject包提供了多种对实体的映射和依赖注入方式。 + +## 用法 + +#### func InterfaceOf + +```go +func InterfaceOf(value interface{}) reflect.Type +``` +函数InterfaceOf返回指向接口类型的指针。如果传入的value值不是指向接口的指针,将抛出一个panic异常。 + +#### type Applicator + +```go +type Applicator interface { + // 在Type map中维持对结构体中每个域的引用并用'inject'来标记 + // 如果注入失败将会返回一个error. + Apply(interface{}) error +} +``` + +Applicator接口表示到结构体的依赖映射关系。 + +#### type Injector + +```go +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖, + // 将会继续从它的父injector中找,直到返回error. + SetParent(Injector) +} +``` + +Injector接口表示对结构体、函数参数的映射和依赖注入。 + +#### func New + +```go +func New() Injector +``` +New创建并返回一个Injector. + +#### type Invoker + +```go +type Invoker interface { + // Invoke尝试将interface{}作为一个函数来调用,并基于Type为函数提供参数。 + // 它将返回reflect.Value的切片,其中存放原函数的返回值。 + // 如果注入失败则返回error. + Invoke(interface{}) ([]reflect.Value, error) +} +``` + +Invoker接口表示通过反射进行函数调用。 + +#### type TypeMapper + +```go +type TypeMapper interface { + // 基于调用reflect.TypeOf得到的类型映射interface{}的值。 + Map(interface{}) TypeMapper + // 基于提供的接口的指针映射interface{}的值。 + // 该函数仅用来将一个值映射为接口,因为接口无法不通过指针而直接引用到。 + MapTo(interface{}, interface{}) TypeMapper + // 为直接插入基于类型和值的map提供一种可能性。 + // 它使得这一类直接映射成为可能:无法通过反射直接实例化的类型参数,如单向管道。 + Set(reflect.Type, reflect.Value) TypeMapper + // 返回映射到当前类型的Value. 如果Type没被映射,将返回对应的零值。 + Get(reflect.Type) reflect.Value +} +``` + +TypeMapper接口用来表示基于类型到接口值的映射。 + + +## 译者 + +张强 (qqbunny@yeah.net) \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/codegangsta/inject/update_readme.sh b/Godeps/_workspace/src/github.com/codegangsta/inject/update_readme.sh new file mode 100644 index 0000000..497f9a5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/inject/update_readme.sh @@ -0,0 +1,3 @@ +#!/bin/bash +go get github.com/robertkrimen/godocdown/godocdown +godocdown > README.md diff --git a/gosuv.go b/gosuv.go index 7c7eba8..6f627c7 100644 --- a/gosuv.go +++ b/gosuv.go @@ -12,6 +12,7 @@ import ( "time" "github.com/codegangsta/cli" + "github.com/codegangsta/inject" pb "github.com/codeskyblue/gosuv/gosuvpb" "github.com/franela/goreq" "github.com/golang/protobuf/proto" @@ -32,42 +33,55 @@ func MkdirIfNoExists(dir string) error { return nil } -func wrapAction(f func(*cli.Context)) func(*cli.Context) { - return func(c *cli.Context) { - // check if server alive - _, err := goreq.Request{ - Method: "GET", - Uri: buildURI(c, "/api/version"), - }.Do() - if err != nil { - go exec.Command(os.Args[0], "serv").Run() - time.Sleep(time.Millisecond * 500) +func init() { + log.SetOutputLevel(log.Ldebug) +} + +func connect(ctx *cli.Context) (cc *grpc.ClientConn, err error) { + sockPath := filepath.Join(GOSUV_HOME, "gosuv.sock") + conn, err := grpcDial("unix", sockPath) + return conn, err +} + +func testConnection(network, address string) error { + log.Debugf("test connection") + testconn, err := net.DialTimeout(network, address, time.Millisecond*100) + if err != nil { + log.Debugf("start run server") + cmd := exec.Command(os.Args[0], "serv") + timeout := time.Millisecond * 500 + er := <-GoTimeoutFunc(timeout, cmd.Run) + if er == ErrGoTimeout { + fmt.Println("server started") + } else { + return fmt.Errorf("server stared failed, %v", er) } - f(c) + } else { + testconn.Close() } + return nil } -func wrapPbProgramAction(f func(*cli.Context, pb.ProgramClient)) func(*cli.Context) { +func wrap(f interface{}) func(*cli.Context) { return func(ctx *cli.Context) { - conn, err := connect(ctx) - if err != nil { + sockPath := filepath.Join(GOSUV_HOME, "gosuv.sock") + if err := testConnection("unix", sockPath); err != nil { log.Fatal(err) } - defer conn.Close() - client := pb.NewProgramClient(conn) - f(ctx, client) - } -} -func wrapPbServerAction(f func(*cli.Context, pb.GoSuvClient)) func(*cli.Context) { - return func(ctx *cli.Context) { conn, err := connect(ctx) if err != nil { log.Fatal(err) } defer conn.Close() - client := pb.NewGoSuvClient(conn) - f(ctx, client) + programClient := pb.NewProgramClient(conn) + gosuvClient := pb.NewGoSuvClient(conn) + + inj := inject.New() + inj.Map(programClient) + inj.Map(gosuvClient) + inj.Map(ctx) + inj.Invoke(f) } } @@ -190,12 +204,6 @@ func grpcDial(network, addr string) (*grpc.ClientConn, error) { })) } -func connect(ctx *cli.Context) (cc *grpc.ClientConn, err error) { - sockPath := filepath.Join(GOSUV_HOME, "gosuv.sock") - conn, err := grpcDial("unix", sockPath) - return conn, err -} - func ShutdownAction(ctx *cli.Context, client pb.GoSuvClient) { res, err := client.Shutdown(context.Background(), &pb.NopRequest{}) if err != nil { @@ -234,13 +242,13 @@ func initCli() { { Name: "version", Usage: "Show version", - Action: wrapPbServerAction(VersionAction), + Action: wrap(VersionAction), }, { Name: "status", Aliases: []string{"st"}, Usage: "show program status", - Action: wrapAction(StatusAction), + Action: wrap(StatusAction), }, { Name: "add", @@ -255,22 +263,22 @@ func initCli() { Usage: "Specify environ", }, }, - Action: wrapAction(AddAction), + Action: wrap(AddAction), }, { Name: "start", Usage: "start a not running program", - Action: wrapAction(StartAction), + Action: wrap(StartAction), }, { Name: "stop", Usage: "Stop running program", - Action: wrapAction(StopAction), + Action: wrap(StopAction), }, { Name: "shutdown", Usage: "Shutdown server", - Action: wrapPbServerAction(ShutdownAction), + Action: wrap(ShutdownAction), }, { Name: "serv", @@ -286,9 +294,6 @@ func initCli() { if !finfo.IsDir() { continue } - //modeExec := os.FileMode(0500) - //if strings.HasPrefix(finfo.Name(), "gosuv-") && (finfo.Mode()&modeExec) == modeExec { - //cmdName := string(finfo.Name()[6:]) cmdName := finfo.Name() app.Commands = append(app.Commands, cli.Command{ Name: cmdName, diff --git a/gosuvpb/gosuv.pb.go b/gosuvpb/gosuv.pb.go index 5def88f..9bc2abe 100644 --- a/gosuvpb/gosuv.pb.go +++ b/gosuvpb/gosuv.pb.go @@ -14,6 +14,7 @@ It has these top-level messages: NopRequest Response Request + StatusResponse */ package gosuvpb @@ -119,6 +120,38 @@ func (m *Request) GetName() string { return "" } +type StatusResponse struct { + Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + Status *string `protobuf:"bytes,2,req,name=status" json:"status,omitempty"` + Extra *string `protobuf:"bytes,3,opt,name=extra" json:"extra,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *StatusResponse) Reset() { *m = StatusResponse{} } +func (m *StatusResponse) String() string { return proto.CompactTextString(m) } +func (*StatusResponse) ProtoMessage() {} + +func (m *StatusResponse) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *StatusResponse) GetStatus() string { + if m != nil && m.Status != nil { + return *m.Status + } + return "" +} + +func (m *StatusResponse) GetExtra() string { + if m != nil && m.Extra != nil { + return *m.Extra + } + return "" +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -129,6 +162,7 @@ type GoSuvClient interface { Control(ctx context.Context, in *CtrlRequest, opts ...grpc.CallOption) (*CtrlResponse, error) Shutdown(ctx context.Context, in *NopRequest, opts ...grpc.CallOption) (*Response, error) Version(ctx context.Context, in *NopRequest, opts ...grpc.CallOption) (*Response, error) + Status(ctx context.Context, in *NopRequest, opts ...grpc.CallOption) (*StatusResponse, error) } type goSuvClient struct { @@ -166,12 +200,22 @@ func (c *goSuvClient) Version(ctx context.Context, in *NopRequest, opts ...grpc. return out, nil } +func (c *goSuvClient) Status(ctx context.Context, in *NopRequest, opts ...grpc.CallOption) (*StatusResponse, error) { + out := new(StatusResponse) + err := grpc.Invoke(ctx, "/gosuvpb.GoSuv/Status", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Server API for GoSuv service type GoSuvServer interface { Control(context.Context, *CtrlRequest) (*CtrlResponse, error) Shutdown(context.Context, *NopRequest) (*Response, error) Version(context.Context, *NopRequest) (*Response, error) + Status(context.Context, *NopRequest) (*StatusResponse, error) } func RegisterGoSuvServer(s *grpc.Server, srv GoSuvServer) { @@ -214,6 +258,18 @@ func _GoSuv_Version_Handler(srv interface{}, ctx context.Context, codec grpc.Cod return out, nil } +func _GoSuv_Status_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) { + in := new(NopRequest) + if err := codec.Unmarshal(buf, in); err != nil { + return nil, err + } + out, err := srv.(GoSuvServer).Status(ctx, in) + if err != nil { + return nil, err + } + return out, nil +} + var _GoSuv_serviceDesc = grpc.ServiceDesc{ ServiceName: "gosuvpb.GoSuv", HandlerType: (*GoSuvServer)(nil), @@ -230,6 +286,10 @@ var _GoSuv_serviceDesc = grpc.ServiceDesc{ MethodName: "Version", Handler: _GoSuv_Version_Handler, }, + { + MethodName: "Status", + Handler: _GoSuv_Status_Handler, + }, }, Streams: []grpc.StreamDesc{}, } diff --git a/gosuvpb/gosuv.proto b/gosuvpb/gosuv.proto index 2a8721b..568d7b6 100644 --- a/gosuvpb/gosuv.proto +++ b/gosuvpb/gosuv.proto @@ -21,13 +21,20 @@ message Request { required string name = 1; } +message StatusResponse { + required string name = 1; + required string status = 2; + optional string extra = 3; +} + service GoSuv { rpc Control(CtrlRequest) returns (CtrlResponse) {}; rpc Shutdown(NopRequest) returns (Response) {}; rpc Version(NopRequest) returns (Response) {}; + rpc Status(NopRequest) returns (StatusResponse) {}; } service Program { rpc Start(Request) returns (Response) {}; rpc Stop(Request) returns (Response) {}; -} \ No newline at end of file +} diff --git a/program.go b/program.go index 79a44eb..5fa4ff1 100644 --- a/program.go +++ b/program.go @@ -16,6 +16,8 @@ import ( "github.com/qiniu/log" ) +var ErrGoTimeout = errors.New("GoTimeoutFunc") + func GoFunc(f func() error) chan error { ch := make(chan error) go func() { @@ -24,6 +26,21 @@ func GoFunc(f func() error) chan error { return ch } +func GoTimeoutFunc(timeout time.Duration, f func() error) chan error { + ch := make(chan error) + go func() { + var err error + select { + case err = <-GoFunc(f): + ch <- err + case <-time.After(timeout): + log.Debugf("timeout: %v", f) + ch <- ErrGoTimeout + } + }() + return ch +} + const ( ST_STANDBY = "STANDBY" ST_RUNNING = "RUNNING" diff --git a/service.go b/service.go index b7994e1..00ebc3c 100644 --- a/service.go +++ b/service.go @@ -63,3 +63,7 @@ func (s *PbSuvServer) Version(ctx context.Context, in *pb.NopRequest) (res *pb.R } return } + +func (s *PbSuvServer) Status(ctx context.Context, in *pb.NopRequest) (res *pb.StatusResponse, err error) { + return +}